/** * seed-generator.ts — Generate diverse seed variants for Push3 evolution (#638). * * Produces parametric Push3 programs that implement the same binary bear/bull * logic as optimizer_seed.push3, with systematically varied constants: * - stakedThreshold: the staked% cutoff for entering the penalty branch * - penaltyThreshold: the penalty value below which bull params are used * - bull/bear params: capitalInefficiency, anchorShare, anchorWidth, discoveryDepth * - taxDistribution: spacing of the 30-bucket tax-rate lookup thresholds * * All generated programs: * 1. Consume 8 inputs (same DYADIC stack interface as the seed) * 2. Produce 4 outputs in valid ranges [0..1e18] for fractions, positive int for anchorWidth * 3. Have identical structure to optimizer_seed.push3 (only constants differ) */ const ONE_E18 = 1_000_000_000_000_000_000n; export interface BullBearParams { capitalInefficiency: bigint; // 0..1e18 anchorShare: bigint; // 0..1e18 anchorWidth: number; // tick units (positive integer) discoveryDepth: bigint; // 0..1e18 } export type TaxDistribution = 'exponential' | 'linear' | 'sqrt'; export interface SeedVariantParams { stakedThreshold: number; // integer 0-100: staked% above which penalty branch is entered penaltyThreshold: number; // penalty < N → bull output; else → bear output bull: BullBearParams; bear: BullBearParams; taxDistribution: TaxDistribution; } // ---- Parameter sets -------------------------------------------------------- /** Staked% thresholds: when staked > threshold, enter penalty computation. */ export const STAKED_THRESHOLDS: readonly number[] = [80, 85, 88, 91, 94, 97]; /** Penalty thresholds: penalty < N → bull; else → bear. */ export const PENALTY_THRESHOLDS: readonly number[] = [30, 50, 70, 100]; /** * Bull output parameter variants (higher confidence / tighter ranges). * * capitalInefficiency is intentionally fixed at 0 across all variants — the * current seed (optimizer_v3.push3) uses CI=0 in all branches, and varying it * is left to the mutation operators once evolution starts. */ export const BULL_VARIANTS: readonly BullBearParams[] = [ // Aggressive bull (current seed): maximum confidence, tight anchor { capitalInefficiency: 0n, anchorShare: ONE_E18, anchorWidth: 20, discoveryDepth: ONE_E18 }, // Tight bull: slightly lower confidence, narrower anchor { capitalInefficiency: 0n, anchorShare: 900_000_000_000_000_000n, anchorWidth: 15, discoveryDepth: 900_000_000_000_000_000n }, // Moderate bull: wider anchor, moderate confidence { capitalInefficiency: 0n, anchorShare: 800_000_000_000_000_000n, anchorWidth: 30, discoveryDepth: 800_000_000_000_000_000n }, // Mild bull: broad discovery, lower confidence { capitalInefficiency: 0n, anchorShare: 700_000_000_000_000_000n, anchorWidth: 50, discoveryDepth: 700_000_000_000_000_000n }, ]; /** * Bear output parameter variants (defensive / wide ranges). * * capitalInefficiency is intentionally fixed at 0 for the same reason as BULL_VARIANTS. */ export const BEAR_VARIANTS: readonly BullBearParams[] = [ // Standard bear (current seed) { capitalInefficiency: 0n, anchorShare: 300_000_000_000_000_000n, anchorWidth: 100, discoveryDepth: 300_000_000_000_000_000n }, // Strong bear: very defensive, very wide anchor { capitalInefficiency: 0n, anchorShare: 200_000_000_000_000_000n, anchorWidth: 150, discoveryDepth: 200_000_000_000_000_000n }, // Mild bear: slightly wider than neutral { capitalInefficiency: 0n, anchorShare: 400_000_000_000_000_000n, anchorWidth: 80, discoveryDepth: 400_000_000_000_000_000n }, // Very mild bear: approaching neutral { capitalInefficiency: 0n, anchorShare: 500_000_000_000_000_000n, anchorWidth: 60, discoveryDepth: 500_000_000_000_000_000n }, ]; export const TAX_DISTRIBUTIONS: readonly TaxDistribution[] = [ 'exponential', 'linear', 'sqrt', ]; // ---- Tax threshold distributions ------------------------------------------- /** * Current seed thresholds — exponentially spaced from ~2e14 to ~8.9e17. * These are the exact values from optimizer_seed.push3. */ function exponentialThresholds(): bigint[] { return [ 206185567010309n, 412371134020618n, 618556701030927n, 1030927835051546n, 1546391752577319n, 2164948453608247n, 2783505154639175n, 3608247422680412n, 4639175257731958n, 5670103092783505n, 7216494845360824n, 9278350515463917n, 11855670103092783n, 15979381443298969n, 22164948453608247n, 29381443298969072n, 38144329896907216n, 49484536082474226n, 63917525773195876n, 83505154639175257n, 109278350515463917n, 144329896907216494n, 185567010309278350n, 237113402061855670n, 309278350515463917n, 402061855670103092n, 520618556701030927n, 680412371134020618n, 886597938144329896n, ]; } /** * Linear thresholds — 29 boundaries evenly spaced across [0, 1e18]. * Gives equal bucket widths: each bucket covers 1/30 of the tax rate range. */ function linearThresholds(): bigint[] { return Array.from({ length: 29 }, (_, i) => (BigInt(i + 1) * ONE_E18) / 30n, ); } /** * Square-root thresholds — more buckets at low tax rates. * Threshold[i] = sqrt((i+1)/30) * 1e18. * Compresses the high-tax range and spreads the low-tax range. */ function sqrtThresholds(): bigint[] { return Array.from({ length: 29 }, (_, i) => { // Work in units of 1e9 for float precision, then scale with BigInt const scaled = Math.sqrt((i + 1) / 30) * 1e9; return BigInt(Math.round(scaled)) * 1_000_000_000n; }); } /** Return the 29 boundary values for the given tax distribution. */ export function getTaxThresholds(dist: TaxDistribution): bigint[] { switch (dist) { case 'exponential': return exponentialThresholds(); case 'linear': return linearThresholds(); case 'sqrt': return sqrtThresholds(); } } // ---- Code generation ------------------------------------------------------- /** * Generate the output-push fragment for a bear/bull param set. * Pushes 4 values bottom-first so stack top = CI, bottom = DD. */ function outputBlock(p: BullBearParams): string { return `${p.discoveryDepth} ${p.anchorWidth} ${p.anchorShare} ${p.capitalInefficiency}`; } /** * Generate the 30-way nested EXEC.IF tax-rate lookup. * * Returns inline Push3 tokens that, when placed inside a list, leave one * integer (0..29) on the DYADIC stack representing the tax rate bucket index. * * Structure (right-recursive): * TAXRATE t0 DYADIC.<= EXEC.IF 0 ( TAXRATE t1 DYADIC.<= EXEC.IF 1 ( ... ) ) */ function generateTaxLookup(thresholds: bigint[]): string { if (thresholds.length !== 29) { throw new Error(`Expected 29 tax thresholds, got ${thresholds.length}`); } function nested(i: number): string { if (i === 28) { // Innermost: threshold[28] → index 28 or 29 return `TAXRATE ${thresholds[i]} DYADIC.<= EXEC.IF ${i} ${i + 1}`; } return `TAXRATE ${thresholds[i]} DYADIC.<= EXEC.IF ${i} ( ${nested(i + 1)} )`; } return nested(0); } /** * Generate Push3 source text for one seed variant. * * The generated program has the same structure as optimizer_seed.push3: * 1. Bind inputs 0-1; discard 2-7 * 2. Scale percentageStaked → integer 0-100 * 3. If staked > stakedThreshold: compute penalty, choose bull or bear * 4. Else: bear outputs */ export function generateSeedVariant(params: SeedVariantParams): string { const { stakedThreshold, penaltyThreshold, bull, bear, taxDistribution } = params; const thresholds = getTaxThresholds(taxDistribution); const taxLookup = generateTaxLookup(thresholds); const bullOut = outputBlock(bull); const bearOut = outputBlock(bear); return [ `;; Generated seed variant (#638)`, `;; staked_threshold=${stakedThreshold} penalty_threshold=${penaltyThreshold}`, `;; bull: CI=${bull.capitalInefficiency} AS=${bull.anchorShare} AW=${bull.anchorWidth} DD=${bull.discoveryDepth}`, `;; bear: CI=${bear.capitalInefficiency} AS=${bear.anchorShare} AW=${bear.anchorWidth} DD=${bear.discoveryDepth}`, `;; tax_distribution=${taxDistribution}`, `(`, ` PERCENTAGESTAKED DYADIC.DEFINE`, ` TAXRATE DYADIC.DEFINE`, ` DYADIC.POP`, ` DYADIC.POP`, ` DYADIC.POP`, ` DYADIC.POP`, ` DYADIC.POP`, ` DYADIC.POP`, ` PERCENTAGESTAKED`, ` 100 DYADIC.*`, ` 1000000000000000000 DYADIC./`, ` STAKED DYADIC.DEFINE`, ` STAKED ${stakedThreshold} DYADIC.>`, ` EXEC.IF`, ` (`, ` 100 STAKED DYADIC.-`, ` DELTAS DYADIC.DEFINE`, ` ${taxLookup}`, ` DYADIC.DUP 14 DYADIC.>=`, ` EXEC.IF`, ` (`, ` 1 DYADIC.+`, ` DYADIC.DUP 29 DYADIC.>`, ` EXEC.IF`, ` ( DYADIC.POP 29 )`, ` ( )`, ` )`, ` ( )`, ` EFFIDX DYADIC.DEFINE`, ` DELTAS DELTAS DYADIC.*`, ` DELTAS DYADIC.*`, ` EFFIDX DYADIC.*`, ` 20 DYADIC./`, ` ${penaltyThreshold} DYADIC.<`, ` EXEC.IF`, ` ( ${bullOut} )`, ` ( ${bearOut} )`, ` )`, ` ( ${bearOut} )`, `)`, ].join('\n'); } // ---- Variant enumeration --------------------------------------------------- /** * Enumerate all parameter combinations in a deterministic order. * * STAKED_THRESHOLDS is the outermost loop so that even-stride sampling in * selectVariants(n) naturally covers all staked% values: each block of * (4 penalty × 4 bull × 4 bear × 3 tax) = 192 entries maps to one threshold. * * Total = |STAKED_THRESHOLDS| × |PENALTY_THRESHOLDS| × |BULL_VARIANTS| * × |BEAR_VARIANTS| × |TAX_DISTRIBUTIONS| * = 6 × 4 × 4 × 4 × 3 = 1152 */ export function enumerateVariants(): SeedVariantParams[] { const result: SeedVariantParams[] = []; for (const stakedThreshold of STAKED_THRESHOLDS) { for (const penaltyThreshold of PENALTY_THRESHOLDS) { for (const bull of BULL_VARIANTS) { for (const bear of BEAR_VARIANTS) { for (const taxDistribution of TAX_DISTRIBUTIONS) { result.push({ stakedThreshold, penaltyThreshold, bull, bear, taxDistribution, }); } } } } } return result; } /** * Select `n` diverse variants from the full parameter space. * * When n ≤ total combinations (1152), samples evenly across the enumeration so * each axis of variation is represented. Because STAKED_THRESHOLDS is the * outermost loop, selectVariants(6) picks one representative per staked% * threshold (stride = 1152/6 = 192, one block per threshold value). * * When n > total, returns all 1152 combinations. */ export function selectVariants(n: number): SeedVariantParams[] { if (n < 1) throw new RangeError('n must be at least 1'); const all = enumerateVariants(); if (n >= all.length) return all; // Even-stride sampling across the full enumeration const stride = all.length / n; return Array.from({ length: n }, (_, i) => all[Math.floor(i * stride)]); }