a11oy / packages /policy /src /gates /pareto_stabilization.ts
betterwithage's picture
sync(space): complete build context — fix BUILD_ERROR (CTO)
518343a verified
Raw
History Blame
6.98 kB
/**
* Pareto Finite Stabilization Gate
*
* @lean_theorem Lutar.Thesis.ParetoStabilization.th_v18_11_pareto_stabilization
* @lean_file Lutar/Thesis/TH_V18_11_ParetoFiniteStabilization.lean
* @lean_status GREEN — 0 sorries
* @lean_commit see LEAN_COMMIT_SHA env var; pin at CI time from lutar-lean/lean-toolchain
*
* Theorem (Lynch 1996 Distributed Algorithms Ch.8; Sipser 2012 Intro to Theory of Computation):
* For finite discrete multi-objective optimization over objectives O ⊂ ℝⁿ (finite set),
* the Pareto frontier F* stabilizes in at most |O| steps.
* Proof: Each update either adds a new non-dominated point (bounded by |O|) or removes
* dominated points. Since |O| is finite, the frontier cannot oscillate indefinitely.
* The governance optimizer reaches a fixed point in finite rounds.
*
* Applications:
* - Multi-organ Ouroboros governance convergence guarantee
* - Budget allocation convergence across 7 organs under discrete utility spaces
*
* References:
* Lynch (1996) Distributed Algorithms Ch.8 — stabilization in finite systems
* Sipser (2012) Introduction to the Theory of Computation §7
* Lutar/Thesis/TH_V18_11_ParetoFiniteStabilization.lean
*
* SPDX-License-Identifier: Apache-2.0
*/
import * as crypto from "crypto";
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
/** A multi-dimensional objective vector (maximize each dimension) */
export type ObjectiveVector = readonly number[];
export interface ParetoStabilizationResult {
/** True iff the Pareto frontier stabilized before maxRounds */
stabilized: boolean;
/** Round at which stabilization was detected (1-indexed) */
stabilizationRound: number;
/** Final Pareto frontier */
paretoFrontier: ObjectiveVector[];
/** Total rounds processed */
roundsProcessed: number;
/** DSSE receipt */
receipt: ParetoDsseReceipt;
}
export interface ParetoDsseReceipt {
formula: string;
lean_theorem: string;
lean_file: string;
lean_commit_sha: string;
inputs_hash: string;
output: {
stabilized: boolean;
stabilizationRound: number;
frontierSize: number;
roundsProcessed: number;
};
ts: string;
}
// ---------------------------------------------------------------------------
// Pareto dominance
// ---------------------------------------------------------------------------
/**
* Returns true if `a` dominates `b` (a ≥ b in all dimensions, a > b in at least one).
*/
function dominates(a: ObjectiveVector, b: ObjectiveVector): boolean {
if (a.length !== b.length) {
throw new Error(`dominates: dimension mismatch ${a.length} vs ${b.length}`);
}
let strictlyBetter = false;
for (let i = 0; i < a.length; i++) {
if (a[i]! < b[i]!) return false;
if (a[i]! > b[i]!) strictlyBetter = true;
}
return strictlyBetter;
}
/**
* Compute Pareto frontier: non-dominated subset of `objectives`.
*/
function computeParetoFrontier(
objectives: ObjectiveVector[]
): ObjectiveVector[] {
const frontier: ObjectiveVector[] = [];
for (const cand of objectives) {
// Check if any existing frontier member dominates cand
if (frontier.some((f) => dominates(f, cand))) continue;
// Remove any frontier members dominated by cand
const newFrontier = frontier.filter((f) => !dominates(cand, f));
newFrontier.push(cand);
frontier.length = 0;
frontier.push(...newFrontier);
}
return frontier;
}
/**
* Frontier equality check (order-independent).
*/
function frontierEqual(
a: ObjectiveVector[],
b: ObjectiveVector[]
): boolean {
if (a.length !== b.length) return false;
const toStr = (v: ObjectiveVector) => v.join(",");
const setA = new Set(a.map(toStr));
return b.every((v) => setA.has(toStr(v)));
}
// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------
function sha256Hex(obj: unknown): string {
return crypto.createHash("sha256").update(JSON.stringify(obj)).digest("hex");
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
* Assert the Pareto finite stabilization theorem.
*
* Given a sequence of round-by-round candidate objective vectors, check that
* the Pareto frontier stabilizes (does not change) within `maxRounds` rounds.
*
* @param candidateStream Array of rounds; each round is an array of new objective vectors.
* Objectives accumulate across rounds.
* @param maxRounds Maximum rounds before declaring non-stabilization (default 50)
*
* @returns ParetoStabilizationResult with stabilization round and DSSE receipt.
*
* By the Lean theorem, stabilization is guaranteed if the total objective space is finite.
*/
export function paretoStabilizationGate(
candidateStream: ObjectiveVector[][],
maxRounds = 50
): ParetoStabilizationResult {
let allObjectives: ObjectiveVector[] = [];
let prevFrontier: ObjectiveVector[] = [];
let stabilizationRound = -1;
let roundsProcessed = 0;
for (let r = 0; r < Math.min(candidateStream.length, maxRounds); r++) {
allObjectives = allObjectives.concat(candidateStream[r]!);
const newFrontier = computeParetoFrontier(allObjectives);
roundsProcessed = r + 1;
if (r > 0 && frontierEqual(prevFrontier, newFrontier)) {
stabilizationRound = r + 1;
prevFrontier = newFrontier;
break;
}
prevFrontier = newFrontier;
}
const stabilized = stabilizationRound !== -1;
const inputs_hash = sha256Hex({ candidateStream, maxRounds });
const lean_commit_sha = process.env["LEAN_COMMIT_SHA"] ?? "unknown";
const receipt: ParetoDsseReceipt = {
formula: "th_v18_11_pareto_stabilization",
lean_theorem:
"Lutar.Thesis.ParetoStabilization.th_v18_11_pareto_stabilization",
lean_file: "Lutar/Thesis/TH_V18_11_ParetoFiniteStabilization.lean",
lean_commit_sha,
inputs_hash,
output: {
stabilized,
stabilizationRound,
frontierSize: prevFrontier.length,
roundsProcessed,
},
ts: new Date().toISOString(),
};
return {
stabilized,
stabilizationRound,
paretoFrontier: prevFrontier,
roundsProcessed,
receipt,
};
}
/**
* Build a candidate stream from a static pool of objectives.
* Splits the pool across `rounds` batches evenly.
* Convenience for testing stabilization under finite objective spaces.
*/
export function buildCandidateStream(
objectives: ObjectiveVector[],
rounds: number
): ObjectiveVector[][] {
const batchSize = Math.ceil(objectives.length / rounds);
const stream: ObjectiveVector[][] = [];
for (let i = 0; i < objectives.length; i += batchSize) {
stream.push(objectives.slice(i, i + batchSize));
}
return stream;
}