Skip to content

feat: the bipartite regression for the successor contract (#196) - #203

Merged
cameronfreer merged 11 commits into
masterfrom
r4-bipartite-regression
Aug 18, 2026
Merged

feat: the bipartite regression for the successor contract (#196)#203
cameronfreer merged 11 commits into
masterfrom
r4-bipartite-regression

Conversation

@cameronfreer

Copy link
Copy Markdown
Owner

The bipartite regression for the successor contract (#196; #107 remains open) — a hand-built rank 1 → 2 successor witness over digraphSig, landed before either route is attempted so the acceptance battery cannot be shaped by an implementation.

Each vertex carries an i.i.d. colour on its own fresh singleton coordinate, and the edge is the parity colour u ⊕ colour v. The diagonal is constantly false for free, since b ⊕ b = false.

Three independent kill switches

1. Genuinely separate descriptions. bipartiteLaw is defined from the fresh singleton layer alone, and the rank-one coupling is the independent product bipartiteLaw × rankLatentSource 1. Neither is read off the rank-two object, so

rankTwoCoupling_truncation :
    rankTwoCoupling.map (Prod.map id (rankLatentProjection (Nat.le_succ 1))) = rankOneCoupling

compares two separately described couplings and genuinely consumes the source factorization — U_∅ splits off from the singleton layer, the array reads only the latter, the truncation only the former. Defining C as a projection of D would have made this hold by unfolding and certified nothing.

2. Numerical nonindependence. not_indepFun_rankTwoCoupling shows the array is not independent of its new latent layer: the edge event and the matching colour-parity event coincide a.e. and each has probability 1/2, so their joint is 1/2 against a product of marginals 1/4. "The edge is a function of the colours" would also hold for a constant edge; the arithmetic is what excludes that.

3. Both directed coordinates. The rank-two decoder recovers X_uv and X_vu as the same parity, so symmetry is exhibited as a property of this law rather than something hidden in the signature.

Contents

  • bipartiteExchangeable — the law, with exchangeability from array equivariance plus source invariance;
  • rankOneRep, rankTwoRep — full representations, all seven fields each;
  • rankTwoCoupling_map_fst / _map_snd / _truncation — the three central identities, proved immediately after the definition and before invariance or screening;
  • blockMap_pair_arr — the organizing deterministic-block lemma, which drives rank-two screening and the XOR identity and nothing else (recovery below rank two ranges over cardinality < 2 and keeps its own vacuous/constant-diagonal proof);
  • bipartiteSuccessor : RankSuccessor rankOneRep — the witness, a two-field literal whose truncation proof is exactly the gate identity;
  • ae_edge_xor — the public XOR identity, a coordinate projection of the block lemma.

Invariance goes through one exact pointwise joint-action lemma proved before any measure is touched, isolating the orientation conventions in a single place.

Bookkeeping

  • New module Graphon/RelBipartiteRegression.lean; import and doc bullet in Graphon.lean.
  • uniform01_Iic relocated from the graph-specific InfiniteSampler to SamplerSources, beside uniform01 itself — both existing consumers compile unchanged, and the regression avoids a graph-specific dependency.
  • Audit 409 → 415 in both scripts/axiom_audit.lean and the intended-set literal in scripts/check_census_and_axioms.py.
  • Gates: lake build clean (3405 jobs), census + axiom audit pass, zero sorries, no warnings in the new module.

Next: the i.i.d.-edge regression at rank 2 → 3, then #197.

…sentation

The XOR array over digraphSig: each vertex carries an i.i.d. colour on
its own fresh singleton coordinate, and the edge is the parity. The
diagonal is constantly false for free, since b XOR b = false.

bipartiteLaw is defined from the FRESH SINGLETON LAYER ALONE — not read
off any rank-two object — with exchangeability from the equivariance
arr (ω ∘ supportPerm σ) = relabel σ (arr ω) plus source invariance under
an index equivalence.

rankOneRep : RankRepresentation 1 with P := bipartiteLaw.prod
(rankLatentSource 1), the independent product. Its invariance uses the
rank-one latent action being the identity; lower_recovers is trivial
because no coordinate of this signature has empty support, so the
rank-zero block space is a point; screening is condIndepFun_const_left
transported by CondIndepFun.congr, since the singleton block reads only
the constantly-false diagonal.
Proved immediately after the definition, before invariance or screening
add noise, as directed:

1. rankTwoCoupling_map_fst = bipartiteLaw
2. rankTwoCoupling_map_snd = rankLatentSource 2
3. rankTwoCoupling_truncation = rankOneCoupling — the gate

The third visibly consumes rankLatentSource_map_rankLatentSpaceSuccEquiv:
U_∅ splits off from the singleton layer, the array reads only the
latter, the truncation reads only the former, and Measure.prod_swap plus
map_prod_map reassemble the independently defined product. It passed with
only orientation fixes, which is exactly what proving it early was meant
to surface.
lower_recovers at rank two ranges over supports of cardinality < 2, so
it sees only the empty and singleton cases and never a two-point
support — the two-point decoder plays no part. The empty case is vacuous
for this signature (no coordinate has empty support), the singleton case
is the constantly-false diagonal, transported through identity 1
(rankTwoCoupling_map_fst) as a measure-preserving projection.

Statement pins (S := digraphSig) on LocalLatentSpace/BlockSpace/
localLatents: with A : Finset (Σ _ : Unit, ℕ) the signature is otherwise
a metavariable.
blockMap_pair_arr: on a two-point support the whole directed block is the
deterministic parity of the two singleton latents visible there. BOTH
X_uv and X_vu decode to the same value — symmetry exhibited as a property
of this law, not of the signature.

Supporting: pairSupport / pairLocalIndex / twoPointDecoder with its
measurability, coord_of_pair (a two-point block coordinate is one of the
two directed pairs, the degenerate diagonal cases ruled out by u ≠ v via
the two vertex witnesses), and freshLayer_vertexSupport, which is rfl —
the successor split's inverse keeps the same underlying finset, so the
decoder reads the fresh layer with no transport.

This lemma organizes rank-two screening and the public XOR identity, and
nothing else: lower_recovers at rank two ranges over cardinality < 2 and
keeps its own vacuous/constant-diagonal proof.
exists_pairSupport_of_card_two normalizes an arbitrary two-point support
to pairSupport u v with u ≠ v FIRST, via an obtain with rfl, so
blockMap_pair_arr applies directly and no dependent block or
local-latent space is transported across a later equality.

Screening is then exactly the three steps: push the source-level decoder
identity to an a.e. identity under rankTwoCoupling (ae_blockMap_pair, via
ae_map_iff and measurableSet_eq_fun), replace the block observation with
CondIndepFun.congr, and discharge conditional independence with
condIndepFun_of_measurable_left — the decoder is measurable through
localLatents, hence measurable for the conditioning sigma-algebra by
comap_measurable. No conditional-expectation reasoning appears.
rankTwoRep assembles all seven fields from the theorems above, and

  bipartiteSuccessor : RankSuccessor rankOneRep

is a two-field literal whose truncation proof is exactly
rankTwoCoupling_truncation. The successor contract is now satisfied by an
explicit rank 1 -> 2 example whose two couplings were described
INDEPENDENTLY — the array law from the fresh singleton layer, the
rank-one coupling as a product with U_∅ — so the truncation identity is a
theorem about two separate descriptions rather than bookkeeping.

Invariance came from one exact pointwise joint-action lemma proved before
any measure was touched, isolating the orientation conventions; the
rankSupportEquiv/supportPerm bridge is kept local, since the finitely
supported rank action and the full-permutation helper serve different
APIs.
ae_edge_xor: almost surely the edge at a pair of distinct vertices is the
parity of their two colours. Obtained by evaluating the deterministic-block
identity at one coordinate — no second pushforward calculation, as
intended. support_digraphCoord records that the directed coordinate lies
in that pair's block.
It is a general fact about uniform01, which is defined in
SamplerSources, but it lived in the graph-specific InfiniteSampler. The
bipartite regression needs it and must not import that module for it.
Both existing consumers (InfiniteSampler, SampleExposure) compile
unchanged, since they already import SamplerSources.
Via the repository-native route: Measure.infinitePi_map_comp_of_injective
at delta := Fin 2 with f := ![vertexSupport 0, vertexSupport 1] gives the
two-vertex marginal as Measure.pi (fun _ : Fin 2 => uniform01); the
XOR-true event is the disjoint union of the two rectangles Iic(1/2) x
Iic(1/2)ᶜ and its mirror, each of mass 1/4 by Measure.pi_pi with
uniform01_Iic_half and its complement.

This is the arithmetic that rules out the degenerate reading: a constant
edge would also be 'a function of the colours', but could not have this
mass.
…ence

not_indepFun_rankTwoCoupling: the array is NOT independent of its new
latent layer. The edge and colour-parity events coincide a.e., each has
probability 1/2, so their joint is 1/2 against a product of marginals
1/4. A constant edge would also be 'a function of the colours' but could
not achieve this, which is why the witness is numerical rather than
functional.

All null-set reasoning is confined to one a.e. event-equivalence lemma
(ae_edgeEvent_iff_parityEvent); the final theorem is pure arithmetic.

Wired into Graphon.lean with a doc bullet; audit 409 -> 415.
…rding (#196)

SamplerSources' contents list now mentions uniform01_Iic — the
declaration moved into the authoritative module but its header did not
record it.

The claim that the bipartite and i.i.d. regressions 'live in a separate
shared module' is replaced by 'Adversarial examples are kept in
route-independent regression modules, outside this interface'. The i.i.d.
module does not exist yet, and the plan is separate modules rather than a
shared one, so the previous wording was both premature and wrong about
the layout. Applied consistently in Graphon.lean and the contract
module's own header.
@cameronfreer
cameronfreer merged commit bf468d1 into master Aug 18, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant