harb/tools/push3-evolution/test/seed-generator.test.ts

268 lines
8.6 KiB
TypeScript
Raw Normal View History

import { describe, it, expect } from 'vitest';
import { parse } from '../../push3-transpiler/src/parser';
import { isValid } from '../mutate';
import {
generateSeedVariant,
selectVariants,
enumerateVariants,
getTaxThresholds,
STAKED_THRESHOLDS,
PENALTY_THRESHOLDS,
BULL_VARIANTS,
BEAR_VARIANTS,
TAX_DISTRIBUTIONS,
type SeedVariantParams,
type TaxDistribution,
} from '../seed-generator';
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function parseAndValidate(src: string): boolean {
try {
const program = parse(src);
return isValid(program);
} catch {
return false;
}
}
const DEFAULT_PARAMS: SeedVariantParams = {
stakedThreshold: 91,
penaltyThreshold: 50,
bull: BULL_VARIANTS[0]!,
bear: BEAR_VARIANTS[0]!,
taxDistribution: 'exponential',
};
// ---------------------------------------------------------------------------
// getTaxThresholds
// ---------------------------------------------------------------------------
describe('getTaxThresholds', () => {
for (const dist of TAX_DISTRIBUTIONS) {
it(`returns 29 values for ${dist}`, () => {
const t = getTaxThresholds(dist as TaxDistribution);
expect(t).toHaveLength(29);
});
it(`all ${dist} thresholds are positive`, () => {
const t = getTaxThresholds(dist as TaxDistribution);
for (const v of t) {
expect(v).toBeGreaterThan(0n);
}
});
it(`${dist} thresholds are strictly increasing`, () => {
const t = getTaxThresholds(dist as TaxDistribution);
for (let i = 1; i < t.length; i++) {
expect(t[i]).toBeGreaterThan(t[i - 1]!);
}
});
it(`${dist} thresholds are below 1e18`, () => {
const t = getTaxThresholds(dist as TaxDistribution);
const ONE_E18 = 1_000_000_000_000_000_000n;
for (const v of t) {
expect(v).toBeLessThan(ONE_E18);
}
});
}
it('linear thresholds are evenly spaced', () => {
const t = getTaxThresholds('linear');
const ONE_E18 = 1_000_000_000_000_000_000n;
// Each gap should be approximately 1e18/30
const expected = ONE_E18 / 30n;
for (let i = 1; i < t.length; i++) {
const gap = t[i]! - t[i - 1]!;
// Allow ±1 for integer rounding
expect(gap - expected).toBeGreaterThanOrEqual(-1n);
expect(gap - expected).toBeLessThanOrEqual(1n);
}
});
});
// ---------------------------------------------------------------------------
// generateSeedVariant — structure and validity
// ---------------------------------------------------------------------------
describe('generateSeedVariant', () => {
it('generates parseable Push3 with default seed params', () => {
const src = generateSeedVariant(DEFAULT_PARAMS);
expect(() => parse(src)).not.toThrow();
});
it('generates a valid program with default seed params', () => {
const src = generateSeedVariant(DEFAULT_PARAMS);
expect(parseAndValidate(src)).toBe(true);
});
it('generates valid programs for all staked thresholds', () => {
for (const st of STAKED_THRESHOLDS) {
const src = generateSeedVariant({ ...DEFAULT_PARAMS, stakedThreshold: st });
expect(parseAndValidate(src)).toBe(true);
}
});
it('generates valid programs for all penalty thresholds', () => {
for (const pt of PENALTY_THRESHOLDS) {
const src = generateSeedVariant({ ...DEFAULT_PARAMS, penaltyThreshold: pt });
expect(parseAndValidate(src)).toBe(true);
}
});
it('generates valid programs for all bull variants', () => {
for (const bull of BULL_VARIANTS) {
const src = generateSeedVariant({ ...DEFAULT_PARAMS, bull });
expect(parseAndValidate(src)).toBe(true);
}
});
it('generates valid programs for all bear variants', () => {
for (const bear of BEAR_VARIANTS) {
const src = generateSeedVariant({ ...DEFAULT_PARAMS, bear });
expect(parseAndValidate(src)).toBe(true);
}
});
it('generates valid programs for all tax distributions', () => {
for (const taxDistribution of TAX_DISTRIBUTIONS) {
const src = generateSeedVariant({ ...DEFAULT_PARAMS, taxDistribution });
expect(parseAndValidate(src)).toBe(true);
}
});
it('default params reproduce optimizer_seed.push3 constants', () => {
const src = generateSeedVariant(DEFAULT_PARAMS);
// Staked threshold
expect(src).toContain('STAKED 91 DYADIC.>');
// Penalty threshold
expect(src).toContain('50 DYADIC.<');
// Bull outputs (DD=1e18, AW=20, AS=1e18, CI=0)
expect(src).toContain('1000000000000000000 20 1000000000000000000 0');
// Bear outputs (DD=0.3e18, AW=100, AS=0.3e18, CI=0)
expect(src).toContain('300000000000000000 100 300000000000000000 0');
});
it('different staked thresholds produce distinct programs', () => {
const programs = STAKED_THRESHOLDS.map(st =>
generateSeedVariant({ ...DEFAULT_PARAMS, stakedThreshold: st }),
);
const unique = new Set(programs);
expect(unique.size).toBe(STAKED_THRESHOLDS.length);
});
it('different tax distributions produce distinct programs', () => {
const programs = TAX_DISTRIBUTIONS.map(td =>
generateSeedVariant({ ...DEFAULT_PARAMS, taxDistribution: td }),
);
const unique = new Set(programs);
expect(unique.size).toBe(TAX_DISTRIBUTIONS.length);
});
});
// ---------------------------------------------------------------------------
// enumerateVariants
// ---------------------------------------------------------------------------
describe('enumerateVariants', () => {
it('returns 1152 combinations', () => {
const all = enumerateVariants();
const expected =
STAKED_THRESHOLDS.length *
PENALTY_THRESHOLDS.length *
BULL_VARIANTS.length *
BEAR_VARIANTS.length *
TAX_DISTRIBUTIONS.length;
expect(all).toHaveLength(expected);
});
it('all combinations are distinct', () => {
const all = enumerateVariants();
const keys = new Set(
all.map(v =>
JSON.stringify([
v.stakedThreshold,
v.penaltyThreshold,
v.bull.anchorWidth,
v.bear.anchorWidth,
v.taxDistribution,
]),
),
);
expect(keys.size).toBe(all.length);
});
it('covers all staked thresholds', () => {
const all = enumerateVariants();
const seen = new Set(all.map(v => v.stakedThreshold));
for (const st of STAKED_THRESHOLDS) {
expect(seen.has(st)).toBe(true);
}
});
it('covers all tax distributions', () => {
const all = enumerateVariants();
const seen = new Set(all.map(v => v.taxDistribution));
for (const td of TAX_DISTRIBUTIONS) {
expect(seen.has(td)).toBe(true);
}
});
});
// ---------------------------------------------------------------------------
// selectVariants
// ---------------------------------------------------------------------------
describe('selectVariants', () => {
it('returns exactly n variants for n < total', () => {
expect(selectVariants(1)).toHaveLength(1);
expect(selectVariants(10)).toHaveLength(10);
expect(selectVariants(50)).toHaveLength(50);
});
it('returns all variants when n >= total', () => {
const total = enumerateVariants().length;
expect(selectVariants(total)).toHaveLength(total);
expect(selectVariants(total + 100)).toHaveLength(total);
});
it('throws for n < 1', () => {
expect(() => selectVariants(0)).toThrow();
expect(() => selectVariants(-1)).toThrow();
});
it('all selected variants produce valid programs', () => {
const variants = selectVariants(20);
for (const v of variants) {
const src = generateSeedVariant(v);
expect(parseAndValidate(src)).toBe(true);
}
});
it('n=6 covers all staked thresholds via even stride', () => {
// enumerateVariants puts STAKED_THRESHOLDS outermost (192 entries per threshold).
// stride = 1152/6 = 192 → one representative per staked% value.
const variants = selectVariants(6);
expect(variants).toHaveLength(6);
const stakedValues = new Set(variants.map(v => v.stakedThreshold));
expect(stakedValues.size).toBe(STAKED_THRESHOLDS.length);
for (const st of STAKED_THRESHOLDS) {
expect(stakedValues.has(st)).toBe(true);
}
// Each representative is a valid Push3 program
for (const v of variants) {
expect(parseAndValidate(generateSeedVariant(v))).toBe(true);
}
});
it('produces diverse programs (no duplicates in typical pop size)', () => {
const variants = selectVariants(24);
const programs = variants.map(v => generateSeedVariant(v));
const unique = new Set(programs);
expect(unique.size).toBe(24);
});
});