Discovery.Linear­Disentanglement

Entry point for the formalization of Squires, Seigal, Bhate & Uhler, *Linear Causal Disentanglement via Interventions* (ICML 2023, arXiv:2211.16467).

Model 7 core · 1 supporting Formalization of the setup of Squires, Seigal, Bhate & Uhler, *Linear Causal Disentanglement via Interventions* (ICML 2023), arXiv:2211.16467. ★ Solution★ InSG★ permMat_mul_transpose

Linear causal disentanglement: model layer

Formalization of the setup of Squires, Seigal, Bhate & Uhler, Linear Causal Disentanglement via Interventions (ICML 2023), arXiv:2211.16467.

We work over d latent variables (Fin d) and p ≥ d observed variables (Fin p). The latent DAG 𝒢 is ordered so that an edge j → i implies j > i; this makes the structural matrices upper triangular in the Fin d order.

For a fixed context, the latent SEM is Z = A Z + Ω^{1/2} ε, equivalently Z = B⁻¹ ε with B = Ω^{-1/2}(I − A). The observed vector is X = G Z with G ∈ ℝ^{p×d} full column rank, and H := G⁺ its left inverse (a full-row-rank d × p matrix with H G = I_d). The only observable content is the family of precision matrices Θ_k = Hᵀ Bₖᵀ Bₖ H ∈ ℝ^{p×p} (one per context).

We take the algebraic (matrix-level) view of the paper's Theorem 2: the data of a disentanglement model is a tuple (H, {B_k}, {i_k}, {λ_k}) satisfying the structural assumptions, and identifiability compares two such tuples that produce the same family {Θ_k}. No Moore–Penrose pseudoinverse is needed: H enters only through LinearIndependent ℝ H (full row rank) and the formula for Θ_k.

Conventions for a Solution d p K: * context 0 is observational with matrix B0; contexts 1 … K are interventional with matrices Bint k, intervention target target k, and (perfect-intervention) scaling lam k > 0; * Edge j i means the latent edge j → i (j a parent of i), forced by hAcyc to satisfy i < j; * B0 is upper triangular (B0 i j = 0 for j < i), has positive diagonal, and its off-diagonal support is exactly Edge — this is what defines 𝒢; * hInt is Assumption 1(b) specialized to perfect interventions (Assumption 2): Bₖ = B₀ + e_{iₖ} cₖᵀ with cₖ = λₖ e_{iₖ} − B₀ᵀ e_{iₖ}.

S(𝒢) is the set of node relabelings preserving edge orientation (Edge j i → σ i < σ j); permMat σ is the corresponding permutation matrix (P_σ)_{ij} = ⟦i = σ j⟧, matching the paper.

abbrev stdVec reviewed
Causalean.Discovery.LinearDisentanglement

Standard basis (column) vector e_i : Fin d → ℝ, e_i j = ⟦j = i⟧.

Definition (Lean source)
abbrev stdVec (d : ℕ) (i : Fin d) : Fin d → ℝ := single i (1 : ℝ)
Causalean.Discovery.LinearDisentanglement.stdVec · Causalean/Discovery/LinearDisentanglement/Model.lean:55
def permMat reviewed
Causalean.Discovery.LinearDisentanglement

The permutation matrix of σ, with (permMat σ) i j = 1 if i = σ j and 0 otherwise — the convention (P_σ)_{ij} = ⟦i = σ(j)⟧ of the paper.

Definition (Lean source)
def permMat {d : ℕ} (σ : Perm (Fin d)) : Matrix (Fin d) (Fin d) ℝ := of fun i j => if i = σ j then (1 : ℝ) else 0
Causalean.Discovery.LinearDisentanglement.permMat · Causalean/Discovery/LinearDisentanglement/Model.lean:58
structure Solution reviewed
Causalean.Discovery.LinearDisentanglement

A linear causal disentanglement model with d latent variables, p observed variables, and K interventional contexts (Squires, Seigal, Bhate & Uhler 2023) bundles a full-row-rank mixing pseudoinverse from the observed to the latent space, a latent edge relation that respects the node order, and an observational structural matrix that is upper triangular, has a positive diagonal, and has off-diagonal support exactly equal to the edge set. For each interventional context it further carries a structural matrix, an intervention target node, and a positive perfect-intervention scaling, tied to the observational matrix by the perfect-single-node-intervention formula.

Definition (Lean source)
d p K :
The (transpose of the) mixing pseudoinverse: a `d × p` matrix.
H :
Matrix (Fin d) (Fin p) ℝ
`H` has full row rank: its rows are linearly independent.
hH :
LinearIndependent ℝ (fun i : Fin d => (H i : Fin p → ℝ))
The latent DAG edge relation; `Edge j i` means `j → i`.
Edge :
Fin d → Fin d → Prop
Edges respect the node order: `j → i` implies `i < j`.
hAcyc :
∀ j i
if
Edge j i
then
i < j
Observational structural matrix `B₀`.
B0 :
Matrix (Fin d) (Fin d) ℝ
`B₀` is upper triangular in the node order.
hB0up :
∀ i j
if
j < i
then
B0 i j = 0
`B₀` has positive diagonal.
hB0pos :
∀ i, 0 < B0 i i
The off-diagonal support of `B₀` is exactly the edge set: this defines `𝒢`.
hB0supp :
∀ i j
if
i ≠ j
then
(B0 i j ≠ 0 ↔ Edge j i)
Interventional structural matrices `Bₖ`, `k ∈ {1,…,K}`.
Bint :
Fin K → Matrix (Fin d) (Fin d) ℝ
The intervention target `iₖ` of context `k`.
target :
Fin K → Fin d
The perfect-intervention scaling `λₖ`.
lam :
Fin K → ℝ
`λₖ > 0`.
hlam :
∀ k, 0 < lam k
Assumption 1(b) + Assumption 2 (perfect single-node interventions): `Bₖ = B₀ + e_{iₖ} cₖᵀ` with `cₖ = λₖ e_{iₖ} − B₀ᵀ e_{iₖ}`.
hInt :
∀ k,
Bint k
= B0
+ vecMulVec (stdVec d (target k)) (fun j => lam k * stdVec d (target k) j - B0 (target k) j)
Causalean.Discovery.LinearDisentanglement.Solution · Causalean/Discovery/LinearDisentanglement/Model.lean:63
def Theta0 reviewed
Causalean.Discovery.LinearDisentanglement.Solution

The observational precision matrix Θ₀ = Hᵀ B₀ᵀ B₀ H ∈ ℝ^{p×p}.

Definition (Lean source)
def Theta0 (S : Solution d p K) : Matrix (Fin p) (Fin p) ℝ := S.H.transpose * S.B0.transpose * S.B0 * S.H
Causalean.Discovery.LinearDisentanglement.Solution.Theta0 · Causalean/Discovery/LinearDisentanglement/Model.lean:108 · uses Solution
def Theta reviewed
Causalean.Discovery.LinearDisentanglement.Solution

The interventional precision matrix Θ_k = Hᵀ Bₖᵀ Bₖ H ∈ ℝ^{p×p}.

Definition (Lean source)
def Theta (S : Solution d p K) (k : Fin K) : Matrix (Fin p) (Fin p) ℝ := S.H.transpose * (S.Bint k).transpose * (S.Bint k) * S.H
Causalean.Discovery.LinearDisentanglement.Solution.Theta · Causalean/Discovery/LinearDisentanglement/Model.lean:112 · uses Solution
def InSG reviewed
Causalean.Discovery.LinearDisentanglement.Solution

The order-preserving relabelings S(𝒢): permutations σ with σ i < σ j for every edge j → i.

Definition (Lean source)
def InSG (S : Solution d p K) (σ : Perm (Fin d)) : Prop := ∀ j i, S.Edge j i → σ i < σ j
Causalean.Discovery.LinearDisentanglement.Solution.InSG · Causalean/Discovery/LinearDisentanglement/Model.lean:116 · uses Solution
theorem permMat_mul_transpose reviewed
Causalean.Discovery.LinearDisentanglement

For a dimension d and a permutation σ of Fin d, the permutation matrix permMat σ is orthogonal: its product with its own transpose is the identity matrix.

Formal statement
d :
σ :
Perm (Fin d)
permMat σ * (permMat σ).transpose = 1
Proof (Lean source)
theorem permMat_mul_transpose {d : ℕ} (σ : Perm (Fin d)) : permMat σ * (permMat σ).transpose = 1 := by ext i k rw [Matrix.mul_apply, Matrix.one_apply] simp only [Matrix.transpose_apply, permMat, Matrix.of_apply] rw [Finset.sum_eq_single (σ.symm i)] · simp only [Equiv.apply_symm_apply] by_cases h : i = k · subst h; simp · simp [h, Ne.symm h] · intro j _ hj have : i ≠ σ j := fun h => hj (by rw [h, Equiv.symm_apply_apply]) simp [this] · intro h; exact absurd (Finset.mem_univ _) h
Causalean.Discovery.LinearDisentanglement.permMat_mul_transpose · Causalean/Discovery/LinearDisentanglement/Model.lean:125 · uses permMat
1 supporting declaration (lemmas, instances)
Partial­Order­RQ 11 core · 16 supporting We package the graph-theoretic notions used throughout (parents, ancestors, and the partial order ≺_𝒢 with i ≺ j ↔ j ∈ an(i)), and the partial order RQ decomposition of the paper (Definition 1, Appendix B): a generaliza ★ IsPORQ★ porq_exists★ porq_unique

Linear causal disentanglement: graph order and the partial-order RQ decomposition

We package the graph-theoretic notions used throughout (parents, ancestors, and the partial order ≺_𝒢 with i ≺ j ↔ j ∈ an(i)), and the partial order RQ decomposition of the paper (Definition 1, Appendix B): a generalization of the reduced RQ decomposition where the upper-triangular support of R is replaced by the partial order, and orthogonality of the rows of Q is required only along the order. porq_exists / porq_unique are its existence and uniqueness (their Proposition in Appendix B), proved there by the Gram–Schmidt-style construction in Algorithm "Partial Order RQ Decomposition".

def pa reviewed
Causalean.Discovery.LinearDisentanglement.Solution

Parents of i: nodes j with an edge j → i.

Definition (Lean source)
def pa (S : Solution d p K) (i : Fin d) : Set (Fin d) := {j | S.Edge j i}
Causalean.Discovery.LinearDisentanglement.Solution.pa · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:35 · uses Solution
def Pa reviewed
Causalean.Discovery.LinearDisentanglement.Solution

Pa(i) = pa(i) ∪ {i}.

Definition (Lean source)
def Pa (S : Solution d p K) (i : Fin d) : Set (Fin d) := insert i (S.pa i)
Causalean.Discovery.LinearDisentanglement.Solution.Pa · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:38 · uses Solution
def anc reviewed
Causalean.Discovery.LinearDisentanglement.Solution

Ancestors of i: nodes j with a directed path j ⇝ i (transitive closure of the edge relation).

Definition (Lean source)
def anc (S : Solution d p K) (i : Fin d) : Set (Fin d) := {j | TransGen S.Edge j i}
Causalean.Discovery.LinearDisentanglement.Solution.anc · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:41 · uses Solution
def An reviewed
Causalean.Discovery.LinearDisentanglement.Solution

An(i) = an(i) ∪ {i}.

Definition (Lean source)
def An (S : Solution d p K) (i : Fin d) : Set (Fin d) := insert i (S.anc i)
Causalean.Discovery.LinearDisentanglement.Solution.An · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:45 · uses Solution
def prec reviewed
Causalean.Discovery.LinearDisentanglement.Solution

The partial order ≺_𝒢: i ≺ j iff j is an ancestor of i.

Definition (Lean source)
def prec (S : Solution d p K) (i j : Fin d) : Prop := TransGen S.Edge j i
Causalean.Discovery.LinearDisentanglement.Solution.prec · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:48 · uses Solution
def preceq reviewed
Causalean.Discovery.LinearDisentanglement.Solution

The reflexive closure ⪯_𝒢 of ≺_𝒢.

Definition (Lean source)
def preceq (S : Solution d p K) (i j : Fin d) : Prop := i = j ∨ S.prec i j
Causalean.Discovery.LinearDisentanglement.Solution.preceq · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:51 · uses Solution
structure IsPORQ reviewed
Causalean.Discovery.LinearDisentanglement

Definition 1 (partial order RQ decomposition). A witness that a full-row-rank matrix H factors as H = R Q with R having a non-negative diagonal and support confined to the reflexive partial order — Rᵢⱼ = 0 unless i ⪯ j, and the rows of Q each of unit Euclidean norm and pairwise orthogonal along the strict order.

Definition (Lean source)
S :
Solution d p K
R :
Matrix (Fin d) (Fin d) ℝ
Q :
Matrix (Fin d) (Fin p) ℝ
`H` factors as `R Q`.
factor :
S.H = R * Q
`R` has non-negative diagonal.
diag_nonneg :
∀ i, 0 ≤ R i i
`R` is supported on the partial order: `Rᵢⱼ = 0` unless `i ⪯ j`.
supp :
∀ i j
if
¬ S.preceq i j
then
R i j = 0
Each row of `Q` has unit norm.
unit :
∀ i, (Q i) ⬝ᵥ (Q i) = 1
Rows of `Q` are orthogonal along the strict order.
orth :
∀ i j
if
S.prec i j
then
(Q i) ⬝ᵥ (Q j) = 0
Causalean.Discovery.LinearDisentanglement.IsPORQ · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:137 · uses Solution
def porqResidual reviewed
Causalean.Discovery.LinearDisentanglement

The partial-order Gram–Schmidt residuals, working in EuclideanSpace ℝ (Fin p): porqResidual i = (toLp (hᵢ)) − proj_{Wᵢ} (toLp hᵢ) where Wᵢ = span {porqResidual k : i ≺ k} is the span of the already-built strict-ancestor residuals. The recursion terminates because every recursive call is to a strict ancestor k with i < k (Solution.prec_lt), so the measure d − i strictly decreases.

Definition (Lean source)
noncomputable def porqResidual (S : Solution d p K) (i : Fin d) : EuclideanSpace ℝ (Fin p) := letI W : Submodule ℝ (EuclideanSpace ℝ (Fin p)) := span ℝ (range (fun k : {k // S.prec i k} => porqResidual S k.1)) (toLp 2 (S.H i) : EuclideanSpace ℝ (Fin p)) - W.starProjection (toLp 2 (S.H i)) termination_by d - i.1 decreasing_by have hik : i.1 < k.1.1 := S.prec_lt k.2 omega
Causalean.Discovery.LinearDisentanglement.porqResidual · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:213 · uses Solution
def porqAncSpan reviewed
Causalean.Discovery.LinearDisentanglement

The span Wᵢ = span {porqResidual k : i ≺ k} of the already-built strict-ancestor residuals, the subspace the i-th residual is orthogonalized against.

Definition (Lean source)
noncomputable def porqAncSpan (S : Solution d p K) (i : Fin d) : Submodule ℝ (EuclideanSpace ℝ (Fin p)) := span ℝ (range (fun k : {k // S.prec i k} => porqResidual S k.1))
Causalean.Discovery.LinearDisentanglement.porqAncSpan · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:229 · uses Solution
theorem porq_exists reviewed
Causalean.Discovery.LinearDisentanglement

Existence of the partial order RQ decomposition (Appendix B Proposition). For a linear causal disentanglement solution, there exist matrices R and Q realizing a partial order RQ decomposition of S's factor loading matrix H — that is, H = R·Q with R's diagonal entries nonnegative and R's support confined to the partial order on nodes, and with Q's rows unit-norm and mutually orthogonal along that order.

Formal statement
S :
Solution d p K
∃ (R : Matrix (Fin d) (Fin d) ℝ) (Q : Matrix (Fin d) (Fin p) ℝ), IsPORQ S R Q
Proof (Lean source)
theorem porq_exists (S : Solution d p K) : ∃ (R : Matrix (Fin d) (Fin d) ℝ) (Q : Matrix (Fin d) (Fin p) ℝ), IsPORQ S R Q := by classical -- The unit-norm residual directions `qᵢ = ‖rᵢ‖⁻¹ • rᵢ` (in `EuclideanSpace`). set q : Fin d → EuclideanSpace ℝ (Fin p) := fun i => (‖porqResidual S i‖)⁻¹ • porqResidual S i with hq have hrne : ∀ i, porqResidual S i ≠ 0 := porqResidual_ne_zero S have hnne : ∀ i, ‖porqResidual S i‖ ≠ 0 := fun i => norm_ne_zero_iff.mpr (hrne i) -- The matrix `Q` of these directions, in `dotProduct` coordinates. set Qmat : Matrix (Fin d) (Fin p) ℝ := of fun i j => ofLp (q i) j with hQmat -- Bridge: `dotProduct` of two `Q` rows is the `EuclideanSpace` inner product of `qᵢ, qⱼ`. have hbridge : ∀ i j, (Qmat i) ⬝ᵥ (Qmat j) = (inner ℝ (q i) (q j) : ℝ) := by intro i j have hb := inner_toLp_eq_dotProduct (ofLp (q i)) (ofLp (q j)) rw [WithLp.toLp_ofLp, WithLp.toLp_ofLp] at hb change (ofLp (q i)) ⬝ᵥ (ofLp (q j)) = _ rw [hb] -- The row coefficients of `R`, packaged by `porq_rowCoeffs`. choose Rrow hRsupp hRdiag hRfactor using fun i => porq_rowCoeffs S i refine ⟨Matrix.of fun i j => Rrow i j, Qmat, ?_, ?_, ?_, ?_, ?_⟩ · -- factor: `H = R Q`. ext i j have hHi : (ofLp (toLp 2 (S.H i)) : Fin p → ℝ) = ofLp (∑ m, Rrow i m • q m) := by rw [hRfactor i] rw [WithLp.ofLp_toLp] at hHi rw [hHi] rw [Matrix.mul_apply_eq_vecMul, Matrix.vecMul_eq_sum] -- `ofLp (∑ Rrow i m • q m) j = ∑ m, Rrow i m • (Qmat m) j`. simp only [hQmat, Matrix.of_apply, Finset.sum_apply, Pi.smul_apply, WithLp.ofLp_sum, WithLp.ofLp_smul] · -- diag_nonneg: `R i i = ‖rᵢ‖ ≥ 0`. intro i rw [Matrix.of_apply, hRdiag i] exact norm_nonneg _ · -- supp. intro i j hij rw [Matrix.of_apply] exact hRsupp i j hij · -- unit: `‖qᵢ‖ = 1` in `dotProduct`. intro i rw [hbridge, hq] simp only [inner_smul_left, inner_smul_right, real_inner_self_eq_norm_sq, RCLike.conj_to_real] rw [pow_two] field_simp exact div_self (hnne i) · -- orth: `qᵢ ⟂ qⱼ` for `i ≺ j`. intro i j hij rw [hbridge, hq] simp only [inner_smul_left, inner_smul_right] rw [porqResidual_orthogonal S hij] ring
Causalean.Discovery.LinearDisentanglement.porq_exists · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:428 · uses IsPORQ , Solution
theorem porq_unique reviewed
Causalean.Discovery.LinearDisentanglement

Uniqueness of the partial order RQ decomposition (Appendix B Proposition). Given a solution S, if both (R,Q) and (R',Q') are partial order RQ decompositions of S's latent-direction matrix H — factoring H as a lower- triangular-along-the-order matrix R times a row-orthonormal-along-the-order matrix Q, then the two decompositions coincide: R = R' and Q = Q'.

Formal statement
S :
Solution d p K
R R' :
Matrix (Fin d) (Fin d) ℝ
Q Q' :
Matrix (Fin d) (Fin p) ℝ
h :
IsPORQ S R Q
h' :
IsPORQ S R' Q'
R = R' ∧ Q = Q'
Proof (Lean source)
theorem porq_unique (S : Solution d p K) {R R' : Matrix (Fin d) (Fin d) ℝ} {Q Q' : Matrix (Fin d) (Fin p) ℝ} (h : IsPORQ S R Q) (h' : IsPORQ S R' Q') : R = R' ∧ Q = Q' := by -- `H = R Q` has full row rank, so `Q` has linearly independent rows. have hHQ : LinearIndependent ℝ (R * Q).row := by have hind := S.hH rw [h.factor] at hind rwa [Matrix.row_def] have hQrow : LinearIndependent ℝ Q.row := row_indep_of_mul_row_indep R Q hHQ -- The `Q` factor is determined by the analytic uniqueness of the Gram–Schmidt -- vectors along the partial order (the genuinely hard half). have hQeq : Q = Q' := by -- Both diagonals are strictly positive (the sign-fixing fact). have hRpos := porq_diag_pos S h have hR'pos := porq_diag_pos S h' -- Prove `Q i = Q' i` for every `i`, by strong induction on the reversed `Fin d` -- order: a strict ancestor `k` of `i` (`i ≺ k`) has `k > i`, so the rows `qₖ` -- it is orthogonal to are already known to coincide with `q'ₖ`. have key : ∀ i, Q i = Q' i := by intro i induction i using WellFoundedGT.induction with | _ i hIH => -- `aᵢ := Rᵢᵢ • qᵢ` and `bᵢ := R'ᵢᵢ • q'ᵢ` are the residuals; both equal -- `hᵢ` minus a combination of ancestor rows, hence `aᵢ - bᵢ` lies in the -- ancestor span, while orthogonality makes `aᵢ - bᵢ` perpendicular to it. set a := R i i • Q i with ha set b := R' i i • Q' i with hb -- The residual is orthogonal to every strict ancestor row. have horth : ∀ k, S.prec i k → (a - b) ⬝ᵥ Q k = 0 := by intro k hk have hqk : Q k = Q' k := hIH k (S.prec_lt hk) rw [sub_dotProduct, ha, hb] have h1 : (R i i • Q i) ⬝ᵥ Q k = 0 := by rw [smul_dotProduct, h.orth i k hk, smul_zero] have h2 : (R' i i • Q' i) ⬝ᵥ Q k = 0 := by rw [hqk, smul_dotProduct, h'.orth i k hk, smul_zero] rw [h1, h2, sub_zero] -- Membership: `a - b = Y - X`, a combination of ancestor rows, where -- `hᵢ = a + X = b + Y` (row-split). have hmem : a - b = (∑ k ∈ Finset.univ.erase i, R' i k • Q' k) - ∑ k ∈ Finset.univ.erase i, R i k • Q k := by have hX := porq_row_split S h i have hY := porq_row_split S h' i rw [← ha] at hX rw [← hb] at hY -- a + X = H i = b + Y ⟹ a - b = Y - X have heq : a + (∑ k ∈ Finset.univ.erase i, R i k • Q k) = b + ∑ k ∈ Finset.univ.erase i, R' i k • Q' k := by rw [← hX, ← hY] rw [sub_eq_sub_iff_add_eq_add, heq, add_comm] -- `a - b` is in the ancestor span (`hmem`) and ⟂ it (`horth`), so it is null. have hself : (a - b) ⬝ᵥ (a - b) = 0 := by nth_rewrite 2 [hmem] rw [dotProduct_sub, dotProduct_sum, dotProduct_sum] have hYz : ∀ k ∈ Finset.univ.erase i, (a - b) ⬝ᵥ (R' i k • Q' k) = 0 := by intro k hk rw [dotProduct_smul] by_cases hRk : R' i k = 0 · rw [hRk, zero_smul] · have hprec : S.prec i k := by rcases Classical.em (S.preceq i k) with hpe | hpe · rcases hpe with rfl | hp · exact absurd rfl (Finset.ne_of_mem_erase hk) · exact hp · exact absurd (h'.supp i k hpe) hRk rw [← hIH k (S.prec_lt hprec), horth k hprec, smul_zero] have hXz : ∀ k ∈ Finset.univ.erase i, (a - b) ⬝ᵥ (R i k • Q k) = 0 := by intro k hk rw [dotProduct_smul] by_cases hRk : R i k = 0 · rw [hRk, zero_smul] · have hprec : S.prec i k := by rcases Classical.em (S.preceq i k) with hpe | hpe · rcases hpe with rfl | hp · exact absurd rfl (Finset.ne_of_mem_erase hk) · exact hp · exact absurd (h.supp i k hpe) hRk rw [horth k hprec, smul_zero] rw [Finset.sum_eq_zero hYz, Finset.sum_eq_zero hXz, sub_zero] -- Positive-definiteness forces `a = b`. have hab : a = b := by by_contra hne have : (0 : ℝ) < (a - b) ⬝ᵥ (a - b) := dotProduct_self_pos (sub_ne_zero.mpr hne) rw [hself] at this exact lt_irrefl _ this -- `a = b` means `Rᵢᵢ • qᵢ = R'ᵢᵢ • q'ᵢ`; squaring (unit norm) pins `Rᵢᵢ = R'ᵢᵢ`. rw [ha, hb] at hab have hsq : R i i * R i i = R' i i * R' i i := by have := congrArg (fun v => v ⬝ᵥ v) hab simp only [smul_dotProduct, dotProduct_smul, h.unit i, h'.unit i, smul_eq_mul, mul_one] at this linarith have hRR : R i i = R' i i := by have h1 : (0 : ℝ) ≤ R i i := le_of_lt (hRpos i) have h2 : (0 : ℝ) ≤ R' i i := le_of_lt (hR'pos i) nlinarith [hsq, hRpos i, hR'pos i] -- Cancel the (nonzero) scalar. rw [hRR] at hab exact smul_right_injective (Fin p → ℝ) (ne_of_gt (hR'pos i)) hab funext i exact key i -- Once `Q = Q'`, right-cancellation by the full-row-rank `Q` pins `R`. refine ⟨?_, hQeq⟩ apply eq_of_mul_eq_mul_row_indep hQrow rw [← h.factor, hQeq, ← h'.factor]
Causalean.Discovery.LinearDisentanglement.porq_unique · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:497 · uses IsPORQ , Solution
16 supporting declarations (lemmas, instances)
  • prec_lt theorem — The strict partial order embeds into the Fin d linear order: if i ≺ j (j is a strict ancestor of i) then i < j. This is the acyclicity of 𝒢 lifted through the transitive closure, and the engine behind every "process the nodes in a topological order" argument below.
    S :
    Solution d p K
    i j :
    Fin d
    hij :
    S.prec i j
    i < j
    Proof (Lean source)
    theorem prec_lt (S : Solution d p K) {i j : Fin d} (hij : S.prec i j) : i < j := by induction hij with | single hab => exact S.hAcyc _ _ hab | tail _ hbc ih => exact lt_trans (S.hAcyc _ _ hbc) ih
    Causalean.Discovery.LinearDisentanglement.Solution.prec_lt · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:54
  • not_prec_self theorem — ≺ is irreflexive: a node is never its own strict ancestor.
    S :
    Solution d p K
    i :
    Fin d
    ¬ S.prec i i
    Proof (Lean source)
    theorem not_prec_self (S : Solution d p K) (i : Fin d) : ¬ S.prec i i := fun h => lt_irrefl i (S.prec_lt h)
    Causalean.Discovery.LinearDisentanglement.Solution.not_prec_self · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:63
  • inner_toLp_eq_dotProduct theorem — The EuclideanSpace ℝ (Fin p) inner product of toLp x and toLp y is exactly the dotProduct x ⬝ᵥ y. This is the bridge used to import all of Mathlib's inner-product-space machinery into the dotProduct world.
    x y :
    Fin p → ℝ
    (inner ℝ (toLp 2 x : EuclideanSpace ℝ (Fin p)) (toLp 2 y) : ℝ)
    Proof (Lean source)
    theorem inner_toLp_eq_dotProduct (x y : Fin p → ℝ) : (inner ℝ (toLp 2 x : EuclideanSpace ℝ (Fin p)) (toLp 2 y) : ℝ) = dotProduct x y := by rw [PiLp.inner_apply, dotProduct] apply Finset.sum_congr rfl intro i _ rw [WithLp.ofLp_toLp, WithLp.ofLp_toLp, real_inner_eq_re_inner, mul_comm] simp
    Causalean.Discovery.LinearDisentanglement.inner_toLp_eq_dotProduct · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:76
  • dotProduct_self_pos theorem — The self dotProduct of a nonzero real vector is strictly positive (positive-definiteness of ⬝ᵥ). Equivalently, x ⬝ᵥ x = 0 ↔ x = 0; this is what makes "normalize the projection residual" well defined in the construction.
    x :
    Fin p → ℝ
    hx :
    x ≠ 0
    0 < dotProduct x x
    Proof (Lean source)
    theorem dotProduct_self_pos {x : Fin p → ℝ} (hx : x ≠ 0) : 0 < dotProduct x x := by rcases Function.ne_iff.mp hx with ⟨i, hi⟩ have hnn : 0 ≤ dotProduct x x := sum_nonneg (fun j _ => mul_self_nonneg _) rcases lt_or_eq_of_le hnn with h | h · exact h · exfalso have hzero := (Finset.sum_eq_zero_iff_of_nonneg (fun j _ => mul_self_nonneg (x j))).mp h.symm i (Finset.mem_univ i) exact hi (by simpa using (mul_self_eq_zero).mp hzero)
    Causalean.Discovery.LinearDisentanglement.dotProduct_self_pos · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:88
  • eq_of_mul_eq_mul_row_indep theorem — Right cancellation by a full-row-rank Q. If the rows of Q are linearly independent and R Q = R' Q, then R = R'. This pins the R factor once Q is fixed, and is the algebraic half of the uniqueness proof.
    R R' :
    Matrix (Fin d) (Fin d) ℝ
    Q :
    Matrix (Fin d) (Fin p) ℝ
    hQ :
    hRQ :
    R * Q = R' * Q
    R = R'
    Proof (Lean source)
    theorem eq_of_mul_eq_mul_row_indep {R R' : Matrix (Fin d) (Fin d) ℝ} {Q : Matrix (Fin d) (Fin p) ℝ} (hQ : LinearIndependent ℝ Q.row) (hRQ : R * Q = R' * Q) : R = R' := by have hinj : Injective (fun v => vecMul v Q) := Matrix.vecMul_injective_iff.mpr hQ ext i j have hrow : vecMul (R i) Q = vecMul (R' i) Q := by rw [← Matrix.mul_apply_eq_vecMul, ← Matrix.mul_apply_eq_vecMul, hRQ] exact congrFun (hinj hrow) j
    Causalean.Discovery.LinearDisentanglement.eq_of_mul_eq_mul_row_indep · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:101
  • row_indep_of_mul_row_indep theorem — A full-row-rank product forces a full-row-rank right factor. If the rows of R Q are linearly independent (d of them), then so are the rows of Q. The rows of R Q lie in the span of the rows of Q, which has at most d of them, so a dimension count forces the d rows of Q to be independent. Applied to H = R Q with H of full row rank, this shows the rows qᵢ of any PORQ factor are independent — the hypothesis eq_of_mul_eq_mul_row_indep needs.
    R :
    Matrix (Fin d) (Fin d) ℝ
    Q :
    Matrix (Fin d) (Fin p) ℝ
    hH :
    Proof (Lean source)
    theorem row_indep_of_mul_row_indep (R : Matrix (Fin d) (Fin d) ℝ) (Q : Matrix (Fin d) (Fin p) ℝ) (hH : LinearIndependent ℝ (R * Q).row) : LinearIndependent ℝ Q.row := by have hHcard := (linearIndependent_iff_card_le_finrank_span (b := (R * Q).row)).mp hH rw [linearIndependent_iff_card_le_finrank_span] refine le_trans hHcard ?_ have hsub : span ℝ (range (R * Q).row) ≤ span ℝ (range Q.row) := by rw [Submodule.span_le] rintro x ⟨i, rfl⟩ simp only [SetLike.mem_coe, Matrix.row_def, Matrix.mul_apply_eq_vecMul, Matrix.vecMul_eq_sum] apply Submodule.sum_mem intro j _ exact Submodule.smul_mem _ _ (Submodule.subset_span ⟨j, rfl⟩) exact Submodule.finrank_mono hsub
    Causalean.Discovery.LinearDisentanglement.row_indep_of_mul_row_indep · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:114
  • porq_diag_pos theorem — The diagonal of any PORQ factor is strictly positive.
    S :
    Solution d p K
    R :
    Matrix (Fin d) (Fin d) ℝ
    Q :
    Matrix (Fin d) (Fin p) ℝ
    h :
    IsPORQ S R Q
    i :
    Fin d
    0 < R i i
    Proof (Lean source)
    theorem porq_diag_pos (S : Solution d p K) {R : Matrix (Fin d) (Fin d) ℝ} {Q : Matrix (Fin d) (Fin p) ℝ} (h : IsPORQ S R Q) (i : Fin d) : 0 < R i i := by -- `H = R Q` has full row rank. have hHrow : LinearIndependent ℝ (R * Q).row := by have hind := S.hH rw [h.factor] at hind rwa [Matrix.row_def] -- `R` itself has full row rank: `v R = 0 ⟹ v (R Q) = 0 ⟹ v = 0`. have hRrow : LinearIndependent ℝ R.row := by rw [← Matrix.vecMul_injective_iff] have hinjH : Injective (fun v => vecMul v (R * Q)) := Matrix.vecMul_injective_iff.mpr hHrow intro v w hvw apply hinjH simp only at hvw ⊢ rw [← Matrix.vecMul_vecMul, ← Matrix.vecMul_vecMul, hvw] -- Hence `R` is a unit, so `det R ≠ 0`. have hdet : R.det ≠ 0 := by have hunit : IsUnit R := Matrix.linearIndependent_rows_iff_isUnit.mp hRrow exact (Matrix.isUnit_iff_isUnit_det R).mp hunit |>.ne_zero -- `R` is upper triangular, so `det R = ∏ Rᵢᵢ`. have hupper : R.BlockTriangular id := by intro a b hba apply h.supp rintro (rfl | hprec) · exact lt_irrefl _ hba · exact lt_irrefl _ (lt_trans hba (S.prec_lt hprec)) rw [Matrix.det_of_upperTriangular hupper] at hdet -- Each diagonal entry is nonzero, hence (with `diag_nonneg`) positive. have hne : R i i ≠ 0 := by intro hzero exact hdet (Finset.prod_eq_zero (Finset.mem_univ i) hzero) exact lt_of_le_of_ne (h.diag_nonneg i) (Ne.symm hne)
    Causalean.Discovery.LinearDisentanglement.porq_diag_pos · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:155
  • porq_row_split theorem — Row-split of a PORQ factorization. Splitting off the diagonal term, the i-th row of H is Rᵢᵢ • qᵢ plus a combination of the *other* rows qₖ (k ≠ i); by supp only the strict ancestors i ≺ k contribute, so this is exactly the triangular equation hᵢ = Rᵢᵢ qᵢ + ∑_{i ≺ k} Rᵢₖ qₖ. It is the membership half of the orthogonal-decomposition uniqueness argument.
    S :
    Solution d p K
    R :
    Matrix (Fin d) (Fin d) ℝ
    Q :
    Matrix (Fin d) (Fin p) ℝ
    h :
    IsPORQ S R Q
    i :
    Fin d
    S.H i = R i i • Q i + ∑ k ∈ Finset.univ.erase i, R i k • Q k
    Proof (Lean source)
    theorem porq_row_split (S : Solution d p K) {R : Matrix (Fin d) (Fin d) ℝ} {Q : Matrix (Fin d) (Fin p) ℝ} (h : IsPORQ S R Q) (i : Fin d) : S.H i = R i i • Q i + ∑ k ∈ Finset.univ.erase i, R i k • Q k := by have hrow : S.H i = ∑ k, R i k • Q k := by rw [h.factor, Matrix.mul_apply_eq_vecMul, Matrix.vecMul_eq_sum] rw [hrow, ← Finset.add_sum_erase univ (fun k => R i k • Q k) (Finset.mem_univ i)]
    Causalean.Discovery.LinearDisentanglement.porq_row_split · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:199
  • porqResidual_eq theorem — Unfolding equation for porqResidual: it is toLp hᵢ minus its orthogonal projection onto the ancestor span porqAncSpan i.
    S :
    Solution d p K
    i :
    Fin d
    = (toLp 2 (S.H i) : EuclideanSpace ℝ (Fin p))
    - (porqAncSpan S i).starProjection (toLp 2 (S.H i))
    Proof (Lean source)
    theorem porqResidual_eq (S : Solution d p K) (i : Fin d) : porqResidual S i = (toLp 2 (S.H i) : EuclideanSpace ℝ (Fin p)) - (porqAncSpan S i).starProjection (toLp 2 (S.H i)) := by rw [porqResidual, porqAncSpan]
    Causalean.Discovery.LinearDisentanglement.porqResidual_eq · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:236
  • porqResidual_mem_ancSpan theorem — Each strict-ancestor residual lies in the ancestor span porqAncSpan i.
    S :
    Solution d p K
    i k :
    Fin d
    hk :
    S.prec i k
    Proof (Lean source)
    theorem porqResidual_mem_ancSpan (S : Solution d p K) {i k : Fin d} (hk : S.prec i k) : porqResidual S k ∈ porqAncSpan S i := by classical rw [porqAncSpan] exact Submodule.subset_span ⟨⟨k, hk⟩, rfl⟩
    Causalean.Discovery.LinearDisentanglement.porqResidual_mem_ancSpan · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:243
  • porqResidual_mem_orthogonal theorem — The i-th residual is orthogonal to its ancestor span: porqResidual i ∈ (Wᵢ)ᗮ.
    S :
    Solution d p K
    i :
    Fin d
    porqResidual S i ∈ (porqAncSpan S i)ᗮ
    Proof (Lean source)
    theorem porqResidual_mem_orthogonal (S : Solution d p K) (i : Fin d) : porqResidual S i ∈ (porqAncSpan S i)ᗮ := by rw [porqResidual_eq] exact Submodule.sub_starProjection_mem_orthogonal _
    Causalean.Discovery.LinearDisentanglement.porqResidual_mem_orthogonal · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:250
  • porqResidual_orthogonal theorem — Orthogonality along the order. The i-th residual is orthogonal to every strict-ancestor residual porqResidual k (i ≺ k), since the latter lies in Wᵢ and the former in (Wᵢ)ᗮ.
    S :
    Solution d p K
    i k :
    Fin d
    hk :
    S.prec i k
    (inner ℝ (porqResidual S i) (porqResidual S k) : ℝ) = 0
    Proof (Lean source)
    theorem porqResidual_orthogonal (S : Solution d p K) {i k : Fin d} (hk : S.prec i k) : (inner ℝ (porqResidual S i) (porqResidual S k) : ℝ) = 0 := by rw [real_inner_comm] exact Submodule.inner_right_of_mem_orthogonal (porqResidual_mem_ancSpan S hk) (porqResidual_mem_orthogonal S i)
    Causalean.Discovery.LinearDisentanglement.porqResidual_orthogonal · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:256
  • toLp_H_linearIndependent theorem — The images of the rows of H under toLp are linearly independent (full row rank of H transported through the linear isomorphism toLp).
    S :
    Solution d p K
    LinearIndependent ℝ (fun j : Fin d => (toLp 2 (S.H j) : EuclideanSpace ℝ (Fin p)))
    Proof (Lean source)
    theorem toLp_H_linearIndependent (S : Solution d p K) : LinearIndependent ℝ (fun j : Fin d => (toLp 2 (S.H j) : EuclideanSpace ℝ (Fin p))) := by have hker : ((WithLp.linearEquiv 2 ℝ (Fin p → ℝ)).symm.toLinearMap).ker = ⊥ := LinearMap.ker_eq_bot.mpr (WithLp.linearEquiv 2 ℝ (Fin p → ℝ)).symm.injective have h := S.hH.map' (WithLp.linearEquiv 2 ℝ (Fin p → ℝ)).symm.toLinearMap hker exact h
    Causalean.Discovery.LinearDisentanglement.toLp_H_linearIndependent · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:266
  • porqResidual_mem_HspanLE theorem — The i-th residual lies in the span of {toLp hₘ : i ⪯ m} (the row itself and its strict ancestors). Proved by strong induction on the reversed Fin d order: the diagonal term is toLp hᵢ, and the projection lives in Wᵢ, whose generators porqResidual k (i ≺ k) lie, by induction, in span {toLp hₘ : k ⪯ m} ⊆ span {toLp hₘ : i ⪯ m} (by transitivity of ⪯).
    S :
    Solution d p K
    i :
    Fin d
    porqResidual S i ∈ span ℝ {x | ∃ m, S.preceq i m ∧ x = (toLp 2 (S.H m) : EuclideanSpace ℝ (Fin p))}
    Proof (Lean source)
    theorem porqResidual_mem_HspanLE (S : Solution d p K) (i : Fin d) : porqResidual S i ∈ span ℝ {x | ∃ m, S.preceq i m ∧ x = (toLp 2 (S.H m) : EuclideanSpace ℝ (Fin p))} := by induction i using WellFoundedGT.induction with | _ i hIH => rw [porqResidual_eq] apply Submodule.sub_mem · exact Submodule.subset_span ⟨i, inl rfl, rfl⟩ · -- The projection lies in `Wᵢ`, which is contained in the target span. have hproj : (porqAncSpan S i).starProjection (toLp 2 (S.H i)) ∈ porqAncSpan S i := Submodule.starProjection_apply_mem _ _ have hsub : porqAncSpan S i ≤ span ℝ {x | ∃ m, S.preceq i m ∧ x = (toLp 2 (S.H m) : EuclideanSpace ℝ (Fin p))} := by rw [porqAncSpan, Submodule.span_le] rintro x ⟨⟨k, hik⟩, rfl⟩ -- `porqResidual k ∈ span {toLp hₘ : k ⪯ m} ⊆ span {toLp hₘ : i ⪯ m}`. have hk := hIH k (S.prec_lt hik) refine Submodule.span_mono ?_ hk rintro y ⟨m, hkm, rfl⟩ refine ⟨m, ?_, rfl⟩ rcases hkm with rfl | hkm · exact inr hik · exact inr (Relation.TransGen.trans hkm hik) exact hsub hproj
    Causalean.Discovery.LinearDisentanglement.porqResidual_mem_HspanLE · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:277
  • porqResidual_ne_zero theorem — Non-vanishing of the residual. porqResidual i ≠ 0: if it were zero then toLp hᵢ would lie in Wᵢ ⊆ span {toLp hₘ : i ≺ m}, contradicting the linear independence of {toLp hⱼ} (a member is never in the span of the strictly-others).
    S :
    Solution d p K
    i :
    Fin d
    porqResidual S i ≠ 0
    Proof (Lean source)
    theorem porqResidual_ne_zero (S : Solution d p K) (i : Fin d) : porqResidual S i ≠ 0 := by intro hzero -- From `porqResidual i = 0`, `toLp hᵢ` equals its projection, hence lies in `Wᵢ`. have hmem : (toLp 2 (S.H i) : EuclideanSpace ℝ (Fin p)) ∈ porqAncSpan S i := by have := porqResidual_eq S i rw [hzero] at this have heq : (toLp 2 (S.H i) : EuclideanSpace ℝ (Fin p)) = (porqAncSpan S i).starProjection (toLp 2 (S.H i)) := eq_of_sub_eq_zero this.symm rw [heq] exact Submodule.starProjection_apply_mem _ _ -- `Wᵢ ⊆ span {toLp hₘ : i ≺ m}`, so `toLp hᵢ ∈ span {toLp hₘ : m ≠ i}`. have hsub : porqAncSpan S i ≤ span ℝ ((fun j : Fin d => (toLp 2 (S.H j) : EuclideanSpace ℝ (Fin p))) '' {j | j ≠ i}) := by rw [porqAncSpan, Submodule.span_le] rintro x ⟨⟨k, hik⟩, rfl⟩ have hk := porqResidual_mem_HspanLE S k refine Submodule.span_mono ?_ hk rintro y ⟨m, hkm, rfl⟩ refine ⟨m, ?_, rfl⟩ -- `k ⪯ m` and `i ≺ k` give `i ≺ m`, so `m ≠ i`. have him : S.prec i m := by rcases hkm with rfl | hkm · exact hik · exact Relation.TransGen.trans hkm hik exact fun hmi => S.not_prec_self i (hmi ▸ him) have hmem' : (toLp 2 (S.H i) : EuclideanSpace ℝ (Fin p)) ∈ span ℝ ((fun j : Fin d => (toLp 2 (S.H j) : EuclideanSpace ℝ (Fin p))) '' {j | j ≠ i}) := hsub hmem -- Contradiction with linear independence of `{toLp hⱼ}`. exact (toLp_H_linearIndependent S).notMem_span_image (by simp) hmem'
    Causalean.Discovery.LinearDisentanglement.porqResidual_ne_zero · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:307
  • porq_rowCoeffs theorem — Row coefficients of the existence factorization. Writing qₘ = ‖rₘ‖⁻¹ • rₘ for the normalized residuals, the row toLp hᵢ decomposes as ‖rᵢ‖ • qᵢ + ∑_{i ≺ k} (coefficient) • qₖ: the diagonal coefficient is the residual norm ‖rᵢ‖ ≥ 0, the off-diagonal coefficients are supported on the strict ancestors i ≺ k, and there are none off ⪯. This packages factor, diag_nonneg and supp into one existence statement (the i-th row of R).
    S :
    Solution d p K
    i :
    Fin d
    ∃ c : Fin d → ℝ,
    (∀ j, ¬ S.preceq i j → c j = 0) ∧
    c i = ‖porqResidual S i‖ ∧
    (toLp 2 (S.H i) : EuclideanSpace ℝ (Fin p))
    = ∑ m, c m • ((‖porqResidual S m‖)⁻¹ • porqResidual S m)
    Proof (Lean source)
    theorem porq_rowCoeffs (S : Solution d p K) (i : Fin d) : ∃ c : Fin d → ℝ, (∀ j, ¬ S.preceq i j → c j = 0) ∧ c i = ‖porqResidual S i‖ ∧ (toLp 2 (S.H i) : EuclideanSpace ℝ (Fin p)) = ∑ m, c m • ((‖porqResidual S m‖)⁻¹ • porqResidual S m) := by classical set q : Fin d → EuclideanSpace ℝ (Fin p) := fun m => (‖porqResidual S m‖)⁻¹ • porqResidual S m with hq have hrne : ∀ m, porqResidual S m ≠ 0 := porqResidual_ne_zero S have hnne : ∀ m, ‖porqResidual S m‖ ≠ 0 := fun m => norm_ne_zero_iff.mpr (hrne m) -- `rₘ = ‖rₘ‖ • qₘ`. have hrq : ∀ m, porqResidual S m = ‖porqResidual S m‖ • q m := by intro m rw [hq] simp only rw [smul_smul, mul_inv_cancel₀ (hnne m), one_smul] -- The projection lies in `Wᵢ = span (range rₖ)`; extract subtype coefficients. have hproj : (porqAncSpan S i).starProjection (toLp 2 (S.H i)) ∈ porqAncSpan S i := Submodule.starProjection_apply_mem _ _ rw [porqAncSpan, Submodule.mem_span_range_iff_exists_fun] at hproj obtain ⟨c0, hc0⟩ := hproj rw [← porqAncSpan] at hc0 -- Assemble the `Fin d`-indexed coefficient vector. refine ⟨fun m => if m = i then ‖porqResidual S i‖ else if h : S.prec i m then c0 ⟨m, h⟩ * ‖porqResidual S m‖ else 0, ?_, ?_, ?_⟩ · -- support on `⪯` intro j hj have hji : j ≠ i := fun h => hj (inl h.symm) have hnp : ¬ S.prec i j := fun h => hj (inr h) simp only [hji, if_false, hnp, dite_false] · -- diagonal simp · -- factor -- `toLp hᵢ = rᵢ + proj`. have hsplit : (toLp 2 (S.H i) : EuclideanSpace ℝ (Fin p)) = porqResidual S i + (porqAncSpan S i).starProjection (toLp 2 (S.H i)) := by rw [porqResidual_eq]; abel rw [hsplit, ← hc0] -- Split the universe sum off the diagonal `m = i`. rw [← Finset.add_sum_erase univ _ (Finset.mem_univ i)] beta_reduce -- Diagonal: `‖rᵢ‖ • qᵢ = rᵢ`. rw [if_pos rfl, smul_smul, mul_inv_cancel₀ (hnne i), one_smul] congr 1 -- Off-diagonal: the erased sum equals `∑ k, c0 k • r_k` (subtype reindexing). -- The common summand `f m = if prec i m then c0⟨m,_⟩ • r_m else 0`. set f : Fin d → EuclideanSpace ℝ (Fin p) := fun m => if h : S.prec i m then c0 ⟨m, h⟩ • porqResidual S m else 0 with hf -- Step 1: each erased term equals `f m`. have hterm : ∀ m ∈ Finset.univ.erase i, (if m = i then ‖porqResidual S i‖ else if h : S.prec i m then c0 ⟨m, h⟩ * ‖porqResidual S m‖ else 0) • ((‖porqResidual S m‖)⁻¹ • porqResidual S m) = f m := by intro m hm have hmi : m ≠ i := Finset.ne_of_mem_erase hm simp only [hf, if_neg hmi] by_cases hpm : S.prec i m · rw [dif_pos hpm, dif_pos hpm, smul_smul, mul_assoc, mul_inv_cancel₀ (hnne m), mul_one] · rw [dif_neg hpm, dif_neg hpm, zero_smul] rw [Finset.sum_congr rfl hterm] -- Step 2: drop the terms off the ancestor filter (f vanishes there). have hvanish : ∀ m ∈ Finset.univ.erase i, f m ≠ 0 → S.prec i m := by intro m _ hfm by_contra hpm exact hfm (by simp only [hf, dif_neg hpm]) rw [← Finset.sum_filter_of_ne hvanish] -- Step 3: reindex the filter sum to the subtype sum. have hmem_iff : ∀ m, m ∈ (Finset.univ.erase i).filter (fun m => S.prec i m) ↔ S.prec i m := by intro m rw [mem_filter] refine ⟨fun hm => hm.2, fun hm => ⟨?_, hm⟩⟩ exact Finset.mem_erase.mpr ⟨fun h => S.not_prec_self i (h ▸ hm), Finset.mem_univ m⟩ rw [Finset.sum_subtype _ hmem_iff f] -- `f ↑k = c0 k • r_k`. refine Finset.sum_congr rfl fun k _ => ?_ simp only [hf, dif_pos k.2]
    Causalean.Discovery.LinearDisentanglement.porq_rowCoeffs · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:345
Identifiability 1 core · 0 supporting Flagship Theorem 2. ★ disentanglement_identifiability

Linear causal disentanglement: identifiability up to S(𝒢) and signed scaling

Flagship Theorem 2.

The headline result of Squires, Seigal, Bhate & Uhler, Linear Causal Disentanglement via Interventions (ICML 2023), Theorem 2 (thm:main_id_non_constructive), in its algebraic (matrix-level) form.

Under Assumption 1 (linear latent model, single-node interventions, linear observations) and Assumption 2 (perfect interventions), with one intervention per latent node, the latent graph and intervention targets are identifiable up to S(𝒢), the order-preserving relabelings, while the latent directions are identifiable only up to nonzero signed diagonal scaling. The signed diagonal is unavoidable because the observable precision matrices are even in H.

The (⊆) inclusion is disentanglement_uniqueness; the (⊇) inclusion is sigma_solutions.

theorem disentanglement_identifiability reviewed
Causalean.Discovery.LinearDisentanglement

Linear causal disentanglement identifiability (Theorem 2). Let S and S' be two solutions of the linear causal disentanglement model such that each solution's intervention-target map is a bijection onto the latent coordinates, i.e. one intervention per latent node and S's interventions are non-degenerate (the paper's genericity / Assumption 1(b)): every intervened precision matrix Θ_k differs from the observational precision matrix Θ_0. If S and S' share the same observational precision matrix and agree, context by context, on every interventional precision matrix, then S and S' are related by a single order-preserving relabeling σ of the latent coordinates, a nonzero scaling vector μ, and a ±1 sign vector ν: σ, μ, ν transport S's latent-direction matrix and structural coefficient matrices onto S''s, and σ carries S's intervention targets onto S''s.

Formal statement
S S' :
Solution d p K
hcov :
Bijective S.target
hcov' :
Bijective S'.target
hNondeg :
∀ k, S.Theta k ≠ S.Theta0
hΘ0 :
S.Theta0 = S'.Theta0
:
∀ k, S.Theta k = S'.Theta k
∃ (σ : Perm (Fin d)) (μ ν : Fin d → ℝ),
S.InSG σ ∧
(∀ i, μ i ≠ 0) ∧
(∀ i, ν i = 1 ∨ ν i = -1) ∧
S'.H = diagonal μ * permMat σ * S.H ∧
S'.B0 * (diagonal μ * permMat σ) = diagonal ν * permMat σ * S.B0 ∧
(∀ k, S'.Bint k * (diagonal μ * permMat σ) = diagonal ν * permMat σ * S.Bint k) ∧
(∀ k, S'.target k = σ (S.target k))
Proof (Lean source)
theorem disentanglement_identifiability (S S' : Solution d p K) (hcov : Bijective S.target) (hcov' : Bijective S'.target) (hNondeg : ∀ k, S.Theta k ≠ S.Theta0) (hΘ0 : S.Theta0 = S'.Theta0) (hΘ : ∀ k, S.Theta k = S'.Theta k) : ∃ (σ : Perm (Fin d)) (μ ν : Fin d → ℝ), S.InSG σ ∧ (∀ i, μ i ≠ 0) ∧ (∀ i, ν i = 1 ∨ ν i = -1) ∧ S'.H = diagonal μ * permMat σ * S.H ∧ S'.B0 * (diagonal μ * permMat σ) = diagonal ν * permMat σ * S.B0 ∧ (∀ k, S'.Bint k * (diagonal μ * permMat σ) = diagonal ν * permMat σ * S.Bint k) ∧ (∀ k, S'.target k = σ (S.target k)) := disentanglement_uniqueness S S' hcov hcov' hNondeg hΘ0 hΘ
Causalean.Discovery.LinearDisentanglement.disentanglement_identifiability · Causalean/Discovery/LinearDisentanglement/Identifiability.lean:35 · uses Solution , InSG , Theta , Theta0 , permMat
Key­Identity 1 core · 1 supporting The algebraic backbone of the identifiability results (Squires–Seigal–Bhate–Uhler, §3.1). ★ key_identity

Linear causal disentanglement: the key rank-one identity

The algebraic backbone of the identifiability results (Squires–Seigal–Bhate–Uhler, §3.1). fact_transpose_mul is the rank-one decomposition BᵀB = Σᵢ (Bᵀeᵢ)⊗²; key_identity (their Proposition driving everything) computes the difference of precision matrices between an interventional and the observational context as a difference of two rank-one (outer-product) matrices, using that a single-node intervention changes only one row of B.

theorem key_identity reviewed
Causalean.Discovery.LinearDisentanglement

Key identity (Proposition). For a linear causal disentanglement solution and an interventional context k with target iₖ, the difference of precision matrices Θ_k − Θ₀ equals exactly the difference between two outer products: one built from the target row of the interventional structural matrix, and one built from the target row of the observational structural matrix.

Formal statement
S :
Solution d p K
k :
Fin K
S.Theta k - S.Theta0
= vecMulVec (S.H.transpose *ᵥ ((S.Bint k).transpose *ᵥ stdVec d (S.target k))) (S.H.transpose *ᵥ ((S.Bint k).transpose *ᵥ stdVec d (S.target k)))
- vecMulVec (S.H.transpose *ᵥ (S.B0.transpose *ᵥ stdVec d (S.target k))) (S.H.transpose *ᵥ (S.B0.transpose *ᵥ stdVec d (S.target k)))
Proof (Lean source)
theorem key_identity (S : Solution d p K) (k : Fin K) : S.Theta k - S.Theta0 = vecMulVec (S.H.transpose *ᵥ ((S.Bint k).transpose *ᵥ stdVec d (S.target k))) (S.H.transpose *ᵥ ((S.Bint k).transpose *ᵥ stdVec d (S.target k))) - vecMulVec (S.H.transpose *ᵥ (S.B0.transpose *ᵥ stdVec d (S.target k))) (S.H.transpose *ᵥ (S.B0.transpose *ᵥ stdVec d (S.target k))) := by -- Off-target rows of `Bₖ` agree with `B₀`, so the rank-one decompositions cancel -- except in the target row; conjugating by `Hᵀ · _ · H` gives the two outer products. have hBlevel : ∀ (M B0 : Matrix (Fin d) (Fin d) ℝ) (i : Fin d) (c : Fin d → ℝ), M = B0 + vecMulVec (stdVec d i) c → M.transpose * M - B0.transpose * B0 = vecMulVec (M.transpose *ᵥ stdVec d i) (M.transpose *ᵥ stdVec d i) - vecMulVec (B0.transpose *ᵥ stdVec d i) (B0.transpose *ᵥ stdVec d i) := by intro M B0 i c hM have hrow : ∀ l : Fin d, l ≠ i → (M.transpose *ᵥ stdVec d l) = (B0.transpose *ᵥ stdVec d l) := by intro l hl subst hM funext a simp only [stdVec, Matrix.mulVec_single_one, Matrix.col_apply, Matrix.transpose_apply, Matrix.add_apply, Matrix.vecMulVec_apply, Pi.single_eq_of_ne hl, zero_mul, add_zero] rw [fact_transpose_mul M, fact_transpose_mul B0, ← Finset.add_sum_erase _ _ (Finset.mem_univ i), ← Finset.add_sum_erase _ _ (Finset.mem_univ i)] have hcancel : (∑ l ∈ Finset.univ.erase i, vecMulVec (M.transpose *ᵥ stdVec d l) (M.transpose *ᵥ stdVec d l)) = ∑ l ∈ Finset.univ.erase i, vecMulVec (B0.transpose *ᵥ stdVec d l) (B0.transpose *ᵥ stdVec d l) := by apply Finset.sum_congr rfl intro l hl rw [hrow l (Finset.ne_of_mem_erase hl)] rw [hcancel] abel rw [Solution.Theta, Solution.Theta0] have hfactor : S.H.transpose * (S.Bint k).transpose * S.Bint k * S.H - S.H.transpose * S.B0.transpose * S.B0 * S.H = S.H.transpose * ((S.Bint k).transpose * S.Bint k - S.B0.transpose * S.B0) * S.H := by rw [Matrix.mul_sub, Matrix.sub_mul, Matrix.mul_assoc (S.H.transpose) ((S.Bint k).transpose), Matrix.mul_assoc (S.H.transpose) (S.B0.transpose)] rw [hfactor, hBlevel (S.Bint k) S.B0 (S.target k) _ (S.hInt k), Matrix.mul_sub, Matrix.sub_mul, conj_vecMulVec, conj_vecMulVec]
Causalean.Discovery.LinearDisentanglement.key_identity · Causalean/Discovery/LinearDisentanglement/KeyIdentity.lean:42 · uses Solution , Theta , Theta0 , stdVec
1 supporting declaration (lemmas, instances)
  • fact_transpose_mul theorem — Fact (rank-one decomposition). For any square B, BᵀB = Σᵢ (Bᵀeᵢ)⊗², where v⊗² = v vᵀ (Matrix.vecMulVec v v).
    B :
    Matrix (Fin d) (Fin d) ℝ
    B.transpose * B
    = ∑ i, vecMulVec (B.transpose *ᵥ stdVec d i) (B.transpose *ᵥ stdVec d i)
    Proof (Lean source)
    theorem fact_transpose_mul (B : Matrix (Fin d) (Fin d) ℝ) : B.transpose * B = ∑ i, vecMulVec (B.transpose *ᵥ stdVec d i) (B.transpose *ᵥ stdVec d i) := by ext a b simp only [Matrix.mul_apply, Matrix.transpose_apply, Matrix.sum_apply, Matrix.vecMulVec_apply, stdVec, Matrix.mulVec_single_one, Matrix.col_apply]
    Causalean.Discovery.LinearDisentanglement.fact_transpose_mul · Causalean/Discovery/LinearDisentanglement/KeyIdentity.lean:33
Rowspan 6 core · 0 supporting The technical heart linking the *observable* precision differences to the *latent* graph structure (their Lemma 1). ★ rowspan_inclusion_a★ rowspan_inclusion_b★ rowspan_inclusion_c

Linear causal disentanglement: the rowspan inclusion lemma (Lemma 1)

The technical heart linking the observable precision differences to the latent graph structure (their Lemma 1). By key_identity, rowspan(Θₖ − Θ₀) is spanned by the two vectors Hᵀ Bₖᵀ e_{iₖ}, Hᵀ B₀ᵀ e_{iₖ}, both lying in ⟨hᵢ : i ∈ Pa(iₖ)⟩. Full row rank of H makes this containment characterize the parent set:

* (a) rowspan(Θₖ − Θ₀) ⊆ ⟨hᵢ : i ∈ 𝓘⟩ iff Pa(iₖ) ⊆ 𝓘; * (b) rowspan(Θₖ − Θ₀) ⊆ ⟨qᵢ : i ∈ An(iₖ)⟩; * (c) if the intervention changes the precision matrix, 𝓘 is ancestor-closed, and Pa(iₖ) ⊄ 𝓘, then rowspan(Θₖ − Θ₀) ⊄ ⟨qᵢ : i ∈ 𝓘⟩.

The file also defines the three spans used in those statements: rowSpan for rows of an observable precision-difference matrix, hSpan for selected rows of the mixing pseudoinverse H, and qSpan for selected rows of a partial-order RQ factor Q.

def rowSpan reviewed
Causalean.Discovery.LinearDisentanglement

The row span of a p × p matrix: the subspace of Fin p → ℝ spanned by its rows.

Definition (Lean source)
noncomputable def rowSpan (M : Matrix (Fin p) (Fin p) ℝ) : Submodule ℝ (Fin p → ℝ) := span ℝ (range (fun i => M i))
Causalean.Discovery.LinearDisentanglement.rowSpan · Causalean/Discovery/LinearDisentanglement/Rowspan.lean:36
def hSpan reviewed
Causalean.Discovery.LinearDisentanglement

The span of the rows of H indexed by a set 𝓘 ⊆ [d].

Definition (Lean source)
noncomputable def hSpan (S : Solution d p K) (I : Set (Fin d)) : Submodule ℝ (Fin p → ℝ) := span ℝ ((fun i => (S.H i : Fin p → ℝ)) '' I)
Causalean.Discovery.LinearDisentanglement.hSpan · Causalean/Discovery/LinearDisentanglement/Rowspan.lean:40 · uses Solution
def qSpan reviewed
Causalean.Discovery.LinearDisentanglement

The span of the rows of Q (from a partial order RQ decomposition) indexed by 𝓘.

Definition (Lean source)
noncomputable def qSpan (Q : Matrix (Fin d) (Fin p) ℝ) (I : Set (Fin d)) : Submodule ℝ (Fin p → ℝ) := span ℝ ((fun i => (Q i : Fin p → ℝ)) '' I)
Causalean.Discovery.LinearDisentanglement.qSpan · Causalean/Discovery/LinearDisentanglement/Rowspan.lean:44
theorem rowspan_inclusion_a reviewed
Causalean.Discovery.LinearDisentanglement

Lemma 1(a). For a solution S, an intervention k, and a set of latent indices I, provided the intervention actually changes the observational precision matrix, Θₖ ≠ Θ₀ (the paper's genericity / Assumption (b)), the row span of the precision difference Θₖ − Θ₀ is contained in the span of the rows of the mixing pseudoinverse H indexed by I if and only if the parent set of the intervened target node is contained in I.

Formal statement
S :
Solution d p K
k :
Fin K
I :
Set (Fin d)
hk :
S.Theta k ≠ S.Theta0
rowSpan (S.Theta k - S.Theta0) ≤ hSpan S I ↔ S.Pa (S.target k) ⊆ I
Proof (Lean source)
theorem rowspan_inclusion_a (S : Solution d p K) (k : Fin K) (I : Set (Fin d)) (hk : S.Theta k ≠ S.Theta0) : rowSpan (S.Theta k - S.Theta0) ≤ hSpan S I ↔ S.Pa (S.target k) ⊆ I := by constructor · -- Forward direction. Take `j ∈ Pa(iₖ)`; suppose `j ∉ I` for contradiction. intro hsub j hj by_contra hjI -- The two `key_identity` vectors `u = ∑ᵢ (Bₖ)_{iₖ,i} hᵢ`, `v = ∑ᵢ (B₀)_{iₖ,i} hᵢ`. set u : Fin p → ℝ := ∑ i, S.Bint k (S.target k) i • S.H i with hu set v : Fin p → ℝ := ∑ i, S.B0 (S.target k) i • S.H i with hv -- `v ≠ 0`: its `h_{iₖ}` coefficient is `(B₀)_{iₖ,iₖ} > 0`. have hv_ne : v ≠ 0 := by intro h0 exact absurd (coeff_eq_zero_of_sum_eq_zero S _ h0 (S.target k)) (ne_of_gt (S.hB0pos (S.target k))) -- `Θₖ − Θ₀ = u uᵀ − v vᵀ`. have hid : S.Theta k - S.Theta0 = vecMulVec u u - vecMulVec v v := by rw [key_identity, hu, hv, H_mulVec_row, H_mulVec_row] -- The coordinate relation (★): `∀ a, uₐ (Bₖ)_{iₖ,j} = vₐ (B₀)_{iₖ,j}`. have huv := coord_eq_of_rowSpan_le S k I hsub hjI rw [← hu, ← hv] at huv by_cases hji : j = S.target k · -- Case `j = iₖ`: here non-degeneracy `hk` is needed. subst hji -- `(Bₖ)_{j,j} = λₖ`, `(B₀)_{j,j} = β > 0`; (★) becomes `λₖ uₐ = β vₐ`. have hbk : S.Bint k (S.target k) (S.target k) = S.lam k := Bint_target_diag S k set β : ℝ := S.B0 (S.target k) (S.target k) with hβ have hβpos : 0 < β := S.hB0pos (S.target k) set s : ℝ := β / S.lam k with hs -- `u = s • v` from (★) and `λₖ > 0`. have hus : u = s • v := by funext a have ha := huv a rw [hbk] at ha rw [Pi.smul_apply, smul_eq_mul, hs, div_mul_eq_mul_div, eq_div_iff (ne_of_gt (S.hlam k))] linarith [ha] -- Hence `Θₖ − Θ₀ = (s² − 1) • (v vᵀ)`. have hdiff : S.Theta k - S.Theta0 = (s ^ 2 - 1) • vecMulVec v v := by rw [hid, hus] ext a b simp only [Matrix.sub_apply, Matrix.vecMulVec_apply, Matrix.smul_apply, Pi.smul_apply, smul_eq_mul] ring -- Pick `a` with `vₐ ≠ 0`; then row `a` of the difference recovers `v ∈ hSpan S I`. obtain ⟨a, ha⟩ := Function.ne_iff.mp hv_ne rw [Pi.zero_apply] at ha -- `s² ≠ 1`, else `Θₖ = Θ₀`, contradicting `hk`. have hs2 : s ^ 2 - 1 ≠ 0 := by intro h exact hk (sub_eq_zero.mp (by rw [hdiff, h, zero_smul])) have hcoef : (s ^ 2 - 1) * v a ≠ 0 := mul_ne_zero hs2 ha -- Row `a` of `Θₖ − Θ₀` equals `((s²−1) vₐ) • v`, and lies in `hSpan S I`. have hrow_mem : ((s ^ 2 - 1) * v a) • v ∈ hSpan S I := by have : ((s ^ 2 - 1) * v a) • v = (S.Theta k - S.Theta0) a := by funext b rw [hdiff] simp only [Matrix.smul_apply, Matrix.vecMulVec_apply, Pi.smul_apply, smul_eq_mul] ring rw [this] exact hsub (Submodule.subset_span ⟨a, rfl⟩) -- Scale to land `v ∈ hSpan S I`. have hv_mem : v ∈ hSpan S I := by have := Submodule.smul_mem (hSpan S I) ((s ^ 2 - 1) * v a)⁻¹ hrow_mem rwa [smul_smul, inv_mul_cancel₀ hcoef, one_smul] at this -- But `v = ∑ᵢ (B₀)_{iₖ,i} hᵢ` with `(B₀)_{j,j} = β ≠ 0` and `j ∉ I`. Contradiction. exact absurd (coeff_eq_zero_of_sum_mem_hSpan S _ I hv_mem hjI) (ne_of_gt hβpos) · -- Case `j ≠ iₖ` (a proper parent): `(Bₖ)_{iₖ,j} = 0`, `(B₀)_{iₖ,j} ≠ 0`. No `hk` needed. have hbk0 : S.Bint k (S.target k) j = 0 := Bint_target_offdiag S k hji -- `j ∈ pa(iₖ)`, i.e. an edge `j → iₖ`, so `(B₀)_{iₖ,j} ≠ 0`. have hedge : S.Edge j (S.target k) := by rcases hj with h | h · exact absurd h hji · exact h have hb0_ne : S.B0 (S.target k) j ≠ 0 := (S.hB0supp (S.target k) j (Ne.symm hji)).2 hedge -- (★) with `(Bₖ)_{iₖ,j} = 0` gives `vₐ (B₀)_{iₖ,j} = 0`, so `vₐ = 0` for all `a`. have hv0 : v = 0 := by funext a have ha := huv a rw [hbk0, mul_zero] at ha exact (mul_eq_zero.mp ha.symm).resolve_right hb0_ne exact hv_ne hv0 · -- Backward direction (unconditional): `Pa(iₖ) ⊆ I ⟹ rowSpan(Θₖ − Θ₀) ≤ ⟨hᵢ : i∈I⟩`. intro hPa refine le_trans (rowSpan_le_hSpan_Pa S k) ?_ exact Submodule.span_mono (Set.image_mono hPa)
Causalean.Discovery.LinearDisentanglement.rowspan_inclusion_a · Causalean/Discovery/LinearDisentanglement/Rowspan.lean:205 · uses Solution , Pa , Theta , Theta0 , hSpan , rowSpan
theorem rowspan_inclusion_b reviewed
Causalean.Discovery.LinearDisentanglement

Lemma 1(b). For a solution S, an intervention k, and any partial order RQ decomposition of S's latent-direction matrix H, witnessed by R and Q, the row span of the precision difference Θₖ − Θ₀ is contained in the span of the rows of Q indexed by the ancestor set of the intervened target node.

Formal statement
S :
Solution d p K
R :
Matrix (Fin d) (Fin d) ℝ
Q :
Matrix (Fin d) (Fin p) ℝ
hRQ :
IsPORQ S R Q
k :
Fin K
rowSpan (S.Theta k - S.Theta0) ≤ qSpan Q (S.An (S.target k))
Proof (Lean source)
theorem rowspan_inclusion_b (S : Solution d p K) {R : Matrix (Fin d) (Fin d) ℝ} {Q : Matrix (Fin d) (Fin p) ℝ} (hRQ : IsPORQ S R Q) (k : Fin K) : rowSpan (S.Theta k - S.Theta0) ≤ qSpan Q (S.An (S.target k)) := by refine le_trans (rowSpan_le_hSpan_Pa S k) ?_ rw [hSpan, Submodule.span_le] rintro _ ⟨i, hi, rfl⟩ exact qSpan_mono Q (An_subset_An_of_mem_Pa S k hi) (H_row_mem_qSpan_An S hRQ i)
Causalean.Discovery.LinearDisentanglement.rowspan_inclusion_b · Causalean/Discovery/LinearDisentanglement/Rowspan.lean:344 · uses IsPORQ , Solution , An , Theta , Theta0 , qSpan , rowSpan
theorem rowspan_inclusion_c reviewed
Causalean.Discovery.LinearDisentanglement

Lemma 1(c). For a solution S, an intervention k, and a set of latent indices I, given a partial order RQ decomposition of S's latent-direction matrix H, witnessed by R and Q, the non-degeneracy condition Θₖ ≠ Θ₀ (the paper's genericity / Assumption (b)), that I is ancestor-closed, and that the parent set of the intervened target node is not contained in I, then the row span of the precision difference Θₖ − Θ₀ is not contained in the span of the rows of Q indexed by I.

Formal statement
S :
Solution d p K
R :
Matrix (Fin d) (Fin d) ℝ
Q :
Matrix (Fin d) (Fin p) ℝ
hRQ :
IsPORQ S R Q
k :
Fin K
I :
Set (Fin d)
hk :
S.Theta k ≠ S.Theta0
hIclosed :
∀ i ∈ I, S.An i ⊆ I
hPa :
¬ S.Pa (S.target k) ⊆ I
¬ rowSpan (S.Theta k - S.Theta0) ≤ qSpan Q I
Proof (Lean source)
theorem rowspan_inclusion_c (S : Solution d p K) {R : Matrix (Fin d) (Fin d) ℝ} {Q : Matrix (Fin d) (Fin p) ℝ} (hRQ : IsPORQ S R Q) (k : Fin K) (I : Set (Fin d)) (hk : S.Theta k ≠ S.Theta0) (hIclosed : ∀ i ∈ I, S.An i ⊆ I) (hPa : ¬ S.Pa (S.target k) ⊆ I) : ¬ rowSpan (S.Theta k - S.Theta0) ≤ qSpan Q I := by intro hsub obtain ⟨j, hjPa, hjI⟩ := Set.not_subset.mp hPa -- The two `key_identity` vectors `u = ∑ᵢ (Bₖ)_{iₖ,i} hᵢ`, `v = ∑ᵢ (B₀)_{iₖ,i} hᵢ`. set u : Fin p → ℝ := ∑ i, S.Bint k (S.target k) i • S.H i with hu set v : Fin p → ℝ := ∑ i, S.B0 (S.target k) i • S.H i with hv -- `v ≠ 0`: its `h_{iₖ}` coefficient is `(B₀)_{iₖ,iₖ} > 0`. have hv_ne : v ≠ 0 := fun h0 => absurd (coeff_eq_zero_of_sum_eq_zero S _ h0 (S.target k)) (ne_of_gt (S.hB0pos (S.target k))) -- `Θₖ − Θ₀ = u uᵀ − v vᵀ`, and `hk` ⟹ this is nonzero. have hid : S.Theta k - S.Theta0 = vecMulVec u u - vecMulVec v v := by rw [key_identity, hu, hv, H_mulVec_row, H_mulVec_row] have hM : vecMulVec u u - vecMulVec v v ≠ 0 := by rw [← hid]; exact fun h => hk (sub_eq_zero.mp h) -- `v ∈ rowspan(Θₖ−Θ₀)` (Gram extraction), hence `v ∈ ⟨qᵢ : i∈I⟩`. have hv_rowspan : v ∈ rowSpan (S.Theta k - S.Theta0) := by rw [hid]; exact vec_mem_rowSpan_vecMulVec_sub hv_ne hM have hv_qspan : v ∈ qSpan Q I := hsub hv_rowspan -- Inverse-PORQ deployment: `⟨qᵢ : i∈I⟩ ⊆ ⟨hₓ : x ∈ ⋃_{i∈I} An(i)⟩`, so `v` lands there. have key : qSpan Q I ≤ hSpan S (⋃ i ∈ I, S.An i) := by rw [qSpan, Submodule.span_le] rintro _ ⟨i, hiI, rfl⟩ have hqi : (Q i : Fin p → ℝ) ∈ qSpan Q (S.An i) := Submodule.subset_span ⟨i, Set.mem_insert _ _, rfl⟩ rw [← hSpan_An_eq_qSpan_An S hRQ i] at hqi exact Submodule.span_mono (Set.image_mono (fun x hx => Set.mem_biUnion hiI hx)) hqi have hv_hspan : v ∈ hSpan S (⋃ i ∈ I, S.An i) := key hv_qspan -- The `hⱼ`-coefficient of `v` is `(B₀)_{iₖ,j} ≠ 0`: `j ∈ Pa(iₖ)` is either the target -- itself (positive diagonal) or a proper parent (nonzero edge entry). have hb0j : S.B0 (S.target k) j ≠ 0 := by by_cases hji : j = S.target k · rw [hji]; exact ne_of_gt (S.hB0pos (S.target k)) · have hedge : S.Edge j (S.target k) := by rcases hjPa with h | h · exact absurd h hji · exact h exact (S.hB0supp (S.target k) j (Ne.symm hji)).2 hedge -- Split on whether `j` is an ancestor of some `i ∈ I` (i.e. `I_d ≠ ∅`). by_cases hjU : j ∈ ⋃ i ∈ I, S.An i · -- **Descendant case (`I_d ≠ ∅`) is vacuous under `hIclosed`.** If `j ∈ ⋃_{i∈I} An(i)`, -- then `j ∈ An(i₀)` for some `i₀ ∈ I`; since `I` is ancestor-closed (`hIclosed`), -- `An(i₀) ⊆ I`, so `j ∈ I` — contradicting `j ∉ I`. -- -- This hypothesis is exactly the paper's usage condition: Lemma 1(c) is invoked in -- `prop:orthogonal-correctness` only for `I = I_{t-1}`, the already-processed nodes, -- which is ancestor-closed by construction. Without it the statement is FALSE — the -- "diamond" `𝒢` (edges 2→1, 2→0, 1→0, target 0) with the non-closed `I = {0,1}` -- admits a valid `IsPORQ` model where `rowSpan(Θₖ−Θ₀) ⊆ qSpan Q {0,1}` yet -- `Pa(0) = {0,1,2} ⊄ {0,1}`, because the `IsPORQ` axioms pin `R` only as the -- Gram–Schmidt factor of `H` (no `B₀` coupling) and `2 ∈ An(0) ∩ An(1)` is dropped. obtain ⟨i₀, hi₀I, hji₀⟩ : ∃ i ∈ I, j ∈ S.anc i := by simp only [Set.mem_iUnion] at hjU obtain ⟨i₀, hi₀I, hji₀⟩ := hjU refine ⟨i₀, hi₀I, ?_⟩ rcases hji₀ with hji₀ | hji₀ · exact absurd (hji₀ ▸ hi₀I) hjI · exact hji₀ exact hjI (hIclosed i₀ hi₀I (Set.mem_insert_of_mem _ hji₀)) · -- **Non-descendant case (`I_d = ∅`).** `j ∉ ⋃_{i∈I} An(i)`, so the `hⱼ`-coordinate of -- `v ∈ hSpan S (⋃_{i∈I} An i)` vanishes by linear independence of the rows of `H` -- (`coeff_eq_zero_of_sum_mem_hSpan`), contradicting `(B₀)_{iₖ,j} ≠ 0`. have hvmem : (∑ i, S.B0 (S.target k) i • S.H i) ∈ hSpan S (⋃ i ∈ I, S.An i) := by rw [← hv]; exact hv_hspan exact hb0j (coeff_eq_zero_of_sum_mem_hSpan S _ _ hvmem hjU)
Causalean.Discovery.LinearDisentanglement.rowspan_inclusion_c · Causalean/Discovery/LinearDisentanglement/Rowspan.lean:522 · uses IsPORQ , Solution , An , Pa , Theta , Theta0 , qSpan , rowSpan
Sigma­Solutions 2 core · 0 supporting This file proves the solution-orbit direction of the linear-disentanglement identifiability result. ★ sigma_solutions

Linear causal disentanglement: solution orbits under order-preserving permutations

This file proves the solution-orbit direction of the linear-disentanglement identifiability result. The definition Solution.permute constructs the relabelled model: it sends H to Pσ H, conjugates B₀ and every Bₖ by the permutation matrix, and sends each intervention target iₖ to σ(iₖ). The order-preservation hypothesis σ ∈ S(𝒢) is exactly what keeps the transformed observational matrix triangular in the ambient node order.

The theorem sigma_solutions then proves that this transformed solution has the same observable precision matrices Θ₀ and Θₖ. Together with disentanglement_identifiability, it gives the orbit characterization of all solutions.

def permute reviewed
Causalean.Discovery.LinearDisentanglement.Solution

Relabeling a solution by an order-preserving permutation gives another valid solution.

Definition (Lean source)
def permute (S : Solution d p K) (σ : Perm (Fin d)) (hσ : S.InSG σ) : Solution d p K where H := permMat σ * S.H hH := by have hrow : (fun i : Fin d => ((permMat σ * S.H) i : Fin p → ℝ)) = fun i : Fin d => (S.H (σ.symm i) : Fin p → ℝ) := by funext i a exact permMat_mul_apply σ S.H i a rw [hrow] exact S.hH.comp σ.symm σ.symm.injective Edge := fun j i => S.Edge (σ.symm j) (σ.symm i) hAcyc := by intro j i hji have hlt := hσ (σ.symm j) (σ.symm i) hji simpa using hlt B0 := permMat σ * S.B0 * (permMat σ).transpose hB0up := by intro i j hji rw [perm_conj_apply] by_contra hne have hneq : σ.symm i ≠ σ.symm j := by intro h exact (ne_of_gt hji) (by simpa using congrArg σ h) have hedge : S.Edge (σ.symm j) (σ.symm i) := (S.hB0supp (σ.symm i) (σ.symm j) hneq).1 hne have hlt : i < j := by have := hσ (σ.symm j) (σ.symm i) hedge simpa using this exact (not_lt_of_gt hji) hlt hB0pos := by intro i rw [perm_conj_apply] exact S.hB0pos (σ.symm i) hB0supp := by intro i j hij rw [perm_conj_apply] have hneq : σ.symm i ≠ σ.symm j := by intro h exact hij (by simpa using congrArg σ h) exact S.hB0supp (σ.symm i) (σ.symm j) hneq Bint := fun k => permMat σ * S.Bint k * (permMat σ).transpose target := fun k => σ (S.target k) lam := S.lam hlam := S.hlam hInt := by intro k ext i j rw [S.hInt k] simp [perm_conj_apply, Matrix.vecMulVec_apply, stdVec_perm_symm]
Causalean.Discovery.LinearDisentanglement.Solution.permute · Causalean/Discovery/LinearDisentanglement/SigmaSolutions.lean:84 · uses Solution , InSG
theorem sigma_solutions reviewed
Causalean.Discovery.LinearDisentanglement

For a solution S and a permutation σ of the latent coordinates such that σ is order-preserving, i.e. lies in the paper's group S(𝒢), the relabeled solution obtained by applying σ to S produces exactly the same observational precision matrix Θ₀, and for every intervention k the same interventional precision matrix Θₖ, as S itself.

Formal statement
S :
Solution d p K
σ :
Perm (Fin d)
:
S.InSG σ
(S.permute σ hσ).Theta0 = S.Theta0 ∧ ∀ k, (S.permute σ hσ).Theta k = S.Theta k
Proof (Lean source)
theorem sigma_solutions (S : Solution d p K) (σ : Perm (Fin d)) (hσ : S.InSG σ) : (S.permute σ hσ).Theta0 = S.Theta0 ∧ ∀ k, (S.permute σ hσ).Theta k = S.Theta k := by refine ⟨?_, fun k => ?_⟩ · change (permMat σ * S.H).transpose * (permMat σ * S.B0 * (permMat σ).transpose).transpose * (permMat σ * S.B0 * (permMat σ).transpose) * (permMat σ * S.H) = S.Theta0 rw [conj_telescope] rfl · change (permMat σ * S.H).transpose * (permMat σ * S.Bint k * (permMat σ).transpose).transpose * (permMat σ * S.Bint k * (permMat σ).transpose) * (permMat σ * S.H) = S.Theta k rw [conj_telescope] rfl
Causalean.Discovery.LinearDisentanglement.sigma_solutions · Causalean/Discovery/LinearDisentanglement/SigmaSolutions.lean:143 · uses Solution , InSG , Theta , Theta0 , permute
Uniqueness 2 core · 38 supporting This is the hard half of the flagship: any two parameter tuples producing the same family of precision matrices {Θ_k} differ by a single order-preserving relabeling σ ∈ S(𝒢). ★ disentanglement_uniqueness

Linear causal disentanglement: uniqueness (⊆ direction of Theorem 2)

This is the hard half of the flagship: any two parameter tuples producing the same family of precision matrices {Θ_k} differ by a single order-preserving relabeling σ ∈ S(𝒢). The proof follows the constructive argument of the paper:

1. recover the orthogonal factor Q of a partial order RQ decomposition of H, up to S(𝒢) and signs, from the spans rowspan(Θₖ − Θ₀) (rowspan_inclusion); 2. orthogonalize: (Q⁺)ᵀ Θₖ Q⁺ is the Cholesky factor of the latent precision, which recovers R and hence H = R Q up to S(𝒢); 3. read off {Bₖ} from H and {Θₖ} via Cholesky factorization.

The main public declarations expose these stages. exists_change_of_basis recovers the invertible matrix M with H' = M H; gram_identity and gram_diff_transport move the precision equalities down to latent d × d Gram identities; central_rank2_eq rewrites those identities in rank-two outer-product form; exists_orderPerm performs the orthogonal-correctness collapse and reads off the target relabeling; and disentanglement_uniqueness packages the final signed-monomial conclusion.

The order-preservation pinning runs the same triangular-support logic as Causalean.Mathlib.LinearAlgebra.perm_uniqueness: positive diagonals, triangularity, and nonzero support entries force the recovered relabeling to lie in S(𝒢).

def cvec reviewed
Causalean.Discovery.LinearDisentanglement

The perturbation row cₖ : Fin d → ℝ of a perfect intervention: cₖ j = λₖ (eᵢₖ)ⱼ − (B₀)_{iₖ,j}, so that Bₖ = B₀ + e_{iₖ} cₖᵀ.

Definition (Lean source)
def cvec (S : Solution d p K) (k : Fin K) : Fin d → ℝ := fun j => S.lam k * stdVec d (S.target k) j - S.B0 (S.target k) j
Causalean.Discovery.LinearDisentanglement.cvec · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:248 · uses Solution
theorem disentanglement_uniqueness reviewed
Causalean.Discovery.LinearDisentanglement

Uniqueness (⊆ direction of Theorem 2). Let S and S' be two solutions of the linear causal disentanglement model such that each solution's intervention-target map is a bijection onto the latent coordinates, i.e. one intervention per latent node and S's interventions are non-degenerate (the paper's genericity / Assumption 1(b)): every intervened precision matrix Θ_k differs from the observational precision matrix Θ_0. If S and S' share the same observational precision matrix and agree, context by context, on every interventional precision matrix, then S and S' are related by a single order-preserving relabeling σ of the latent coordinates, a nonzero scaling vector μ, and a ±1 sign vector ν, transporting S's latent-direction and structural coefficient matrices onto S''s, with σ carrying S's intervention targets onto S''s.

Formal statement
S S' :
Solution d p K
hcov :
Bijective S.target
hcov' :
Bijective S'.target
hNondeg :
∀ k, S.Theta k ≠ S.Theta0
hΘ0 :
S.Theta0 = S'.Theta0
:
∀ k, S.Theta k = S'.Theta k
∃ (σ : Perm (Fin d)) (μ ν : Fin d → ℝ),
S.InSG σ ∧
(∀ i, μ i ≠ 0) ∧
(∀ i, ν i = 1 ∨ ν i = -1) ∧
S'.H = diagonal μ * permMat σ * S.H ∧
S'.B0 * (diagonal μ * permMat σ) = diagonal ν * permMat σ * S.B0 ∧
(∀ k, S'.Bint k * (diagonal μ * permMat σ) = diagonal ν * permMat σ * S.Bint k) ∧
(∀ k, S'.target k = σ (S.target k))
Proof (Lean source)
theorem disentanglement_uniqueness (S S' : Solution d p K) (hcov : Bijective S.target) (hcov' : Bijective S'.target) (hNondeg : ∀ k, S.Theta k ≠ S.Theta0) (hΘ0 : S.Theta0 = S'.Theta0) (hΘ : ∀ k, S.Theta k = S'.Theta k) : ∃ (σ : Perm (Fin d)) (μ ν : Fin d → ℝ), S.InSG σ ∧ (∀ i, μ i ≠ 0) ∧ (∀ i, ν i = 1 ∨ ν i = -1) ∧ S'.H = diagonal μ * permMat σ * S.H ∧ S'.B0 * (diagonal μ * permMat σ) = diagonal ν * permMat σ * S.B0 ∧ (∀ k, S'.Bint k * (diagonal μ * permMat σ) = diagonal ν * permMat σ * S.Bint k) ∧ (∀ k, S'.target k = σ (S.target k)) := by -- (L1) recover the invertible change-of-basis `M` with `H' = M H`. obtain ⟨M, _, hM⟩ := exists_change_of_basis S S' hΘ0 -- (L4) the orthogonal-correctness collapse (the isolated hard core). obtain ⟨σ, μ, ν, hσ, hμ, hν, hMeq, hB0rel, hBintrel, htarget⟩ := exists_orderPerm S S' hcov hcov' hNondeg hΘ0 hΘ hM refine ⟨σ, μ, ν, hσ, hμ, hν, ?_, ?_, ?_, htarget⟩ · -- `H' = diagonal μ permMat σ H`. rw [hM, hMeq] · -- Signed observational relation after substituting `M`. rwa [hMeq] at hB0rel · -- Signed interventional relations after substituting `M`. intro k rw [← hMeq] exact hBintrel k
Causalean.Discovery.LinearDisentanglement.disentanglement_uniqueness · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:1466 · uses Solution , InSG , Theta , Theta0 , permMat
38 supporting declarations (lemmas, instances)
  • gram_to_orthogonal theorem — (L3) Gram ⟹ orthogonal. If XᵀX = YᵀY with X, Y invertible, then the transition matrix O = Y X⁻¹ is orthogonal: Oᵀ O = 1. This is the algebraic step that turns the equality of Gram matrices BᵀB = (B' M)ᵀ(B' M) into an orthogonality statement about O = B' M B⁻¹.
    q :
    Matrix (Fin q) (Fin q) ℝ
    h :
    Xᵀ * X = Yᵀ * Y
    (Y * X⁻¹)ᵀ * (Y * X⁻¹) = 1
    Proof (Lean source)
    theorem gram_to_orthogonal {q : ℕ} {X Y : Matrix (Fin q) (Fin q) ℝ} [Invertible X] [Invertible Y] (h : Xᵀ * X = Yᵀ * Y) : (Y * X⁻¹)ᵀ * (Y * X⁻¹) = 1 := by rw [Matrix.transpose_mul, Matrix.mul_assoc, ← Matrix.mul_assoc Yᵀ Y, ← h, Matrix.mul_assoc Xᵀ, Matrix.mul_inv_of_invertible, Matrix.mul_one, Matrix.transpose_nonsing_inv, Matrix.inv_mul_of_invertible]
    Causalean.Discovery.LinearDisentanglement.gram_to_orthogonal · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:47
  • B0_blockTriangular theorem — B0 is upper triangular in the BlockTriangular id sense.
    S :
    Solution d p K
    S.B0.BlockTriangular id
    Proof (Lean source)
    theorem B0_blockTriangular (S : Solution d p K) : S.B0.BlockTriangular id := S.hB0up
    Causalean.Discovery.LinearDisentanglement.B0_blockTriangular · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:63
  • Bint_blockTriangular theorem — Bint k is upper triangular: the perfect intervention only rewrites the target row (which keeps the diagonal at λₖ and zeroes the strictly-lower entries already zero in B0).
    S :
    Solution d p K
    k :
    Fin K
    (S.Bint k).BlockTriangular id
    Proof (Lean source)
    theorem Bint_blockTriangular (S : Solution d p K) (k : Fin K) : (S.Bint k).BlockTriangular id := by intro i j hji simp only [id_eq] at hji rw [S.hInt k, Matrix.add_apply, S.hB0up i j hji, zero_add, Matrix.vecMulVec_apply] by_cases hi : i = S.target k · -- target row: off-diagonal lower entries vanish since `j < i = target k` have hjlt : j < S.target k := hi ▸ hji have hjne : j ≠ S.target k := ne_of_lt hjlt rw [S.hB0up (S.target k) j hjlt] simp [stdVec, Pi.single_eq_of_ne hjne] · simp [stdVec, Pi.single_eq_of_ne hi]
    Causalean.Discovery.LinearDisentanglement.Bint_blockTriangular · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:66
  • B0_det_pos theorem — det B0 = ∏ᵢ (B0)ᵢᵢ > 0.
    S :
    Solution d p K
    0 < S.B0.det
    Proof (Lean source)
    theorem B0_det_pos (S : Solution d p K) : 0 < S.B0.det := by rw [Matrix.det_of_upperTriangular (B0_blockTriangular S)] exact Finset.prod_pos (fun i _ => S.hB0pos i)
    Causalean.Discovery.LinearDisentanglement.B0_det_pos · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:82
  • Bint_diag_pos theorem — (Bint k)ᵢᵢ = (B0)ᵢᵢ off the target, and = λₖ on the target — in both cases strictly positive.
    S :
    Solution d p K
    k :
    Fin K
    i :
    Fin d
    0 < S.Bint k i i
    Proof (Lean source)
    theorem Bint_diag_pos (S : Solution d p K) (k : Fin K) (i : Fin d) : 0 < S.Bint k i i := by rw [S.hInt k, Matrix.add_apply, Matrix.vecMulVec_apply] by_cases hi : i = S.target k · rw [hi] simp only [stdVec, Pi.single_eq_same, one_mul] have : S.B0 (S.target k) (S.target k) + (S.lam k * 1 - S.B0 (S.target k) (S.target k)) = S.lam k := by ring rw [this]; exact S.hlam k · simp only [stdVec, Pi.single_eq_of_ne hi, zero_mul, add_zero] exact S.hB0pos i
    Causalean.Discovery.LinearDisentanglement.Bint_diag_pos · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:87
  • Bint_det_pos theorem — det (Bint k) = ∏ᵢ (Bint k)ᵢᵢ > 0.
    S :
    Solution d p K
    k :
    Fin K
    0 < (S.Bint k).det
    Proof (Lean source)
    theorem Bint_det_pos (S : Solution d p K) (k : Fin K) : 0 < (S.Bint k).det := by rw [Matrix.det_of_upperTriangular (Bint_blockTriangular S k)] exact Finset.prod_pos (fun i _ => Bint_diag_pos S k i)
    Causalean.Discovery.LinearDisentanglement.Bint_det_pos · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:100
  • B0_invertible instance — B0 is invertible.
    noncomputable instance B0_invertible (S : Solution d p K) : Invertible S.B0 := S.B0.invertibleOfIsUnitDet (isUnit_iff_ne_zero.mpr (B0_det_pos S).ne')
    Causalean.Discovery.LinearDisentanglement.B0_invertible · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:105
  • Bint_invertible instance — Bint k is invertible.
    noncomputable instance Bint_invertible (S : Solution d p K) (k : Fin K) : Invertible (S.Bint k) := (S.Bint k).invertibleOfIsUnitDet (isUnit_iff_ne_zero.mpr (Bint_det_pos S k).ne')
    Causalean.Discovery.LinearDisentanglement.Bint_invertible · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:109
  • vecMul_H_injective theorem — vecMul · H is injective (full row rank of H).
    S :
    Solution d p K
    Injective (fun v => vecMul v S.H)
    Proof (Lean source)
    theorem vecMul_H_injective (S : Solution d p K) : Injective (fun v => vecMul v S.H) := Matrix.vecMul_injective_iff.mpr (by rw [Matrix.row_def]; exact S.hH)
    Causalean.Discovery.LinearDisentanglement.vecMul_H_injective · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:116
  • HHt_posDef theorem — The latent Gram matrix H Hᵀ is positive definite.
    S :
    Solution d p K
    (S.H * S.H.transpose).PosDef
    Proof (Lean source)
    theorem HHt_posDef (S : Solution d p K) : (S.H * S.H.transpose).PosDef := by have := Matrix.PosDef.mul_conjTranspose_self S.H (vecMul_H_injective S) rwa [Matrix.conjTranspose_eq_transpose_of_trivial] at this
    Causalean.Discovery.LinearDisentanglement.HHt_posDef · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:121
  • HHt_invertible instance — H Hᵀ is invertible.
    noncomputable instance HHt_invertible (S : Solution d p K) : Invertible (S.H * S.H.transpose) := (S.H * S.H.transpose).invertibleOfIsUnitDet ((Matrix.isUnit_iff_isUnit_det _).mp (HHt_posDef S).isUnit)
    Causalean.Discovery.LinearDisentanglement.HHt_invertible · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:126
  • B0H_gram_invertible instance — The Gram matrix (B₀H)(B₀H)ᵀ = B₀ (H Hᵀ) B₀ᵀ is invertible (product of invertibles).
    noncomputable instance B0H_gram_invertible (S : Solution d p K) : Invertible ((S.B0 * S.H) * (S.B0 * S.H).transpose) := by rw [Matrix.transpose_mul, ← Matrix.mul_assoc, Matrix.mul_assoc S.B0] exact ((B0_invertible S).mul (HHt_invertible S)).mul (inferInstanceAs (Invertible S.B0.transpose))
    Causalean.Discovery.LinearDisentanglement.B0H_gram_invertible · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:132
  • H_mul_rightInv theorem — R := Hᵀ (H Hᵀ)⁻¹ is a right inverse of H: H R = 1.
    S :
    Solution d p K
    S.H * (S.H.transpose * (S.H * S.H.transpose)⁻¹) = 1
    Proof (Lean source)
    theorem H_mul_rightInv (S : Solution d p K) : S.H * (S.H.transpose * (S.H * S.H.transpose)⁻¹) = 1 := by rw [← Matrix.mul_assoc, Matrix.mul_inv_of_invertible]
    Causalean.Discovery.LinearDisentanglement.H_mul_rightInv · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:139
  • leftInv_mul_Ht theorem — L := (H Hᵀ)⁻¹ H is a left inverse of Hᵀ: L Hᵀ = 1.
    S :
    Solution d p K
    ((S.H * S.H.transpose)⁻¹ * S.H) * S.H.transpose = 1
    Proof (Lean source)
    theorem leftInv_mul_Ht (S : Solution d p K) : ((S.H * S.H.transpose)⁻¹ * S.H) * S.H.transpose = 1 := by rw [Matrix.mul_assoc, Matrix.inv_mul_of_invertible]
    Causalean.Discovery.LinearDisentanglement.leftInv_mul_Ht · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:144
  • H_eq_recover_mul_Theta0 theorem — The recovery identity H = W Θ₀. With C = B₀ H and Θ₀ = Cᵀ C, the matrix W = B₀⁻¹ (C Cᵀ)⁻¹ C satisfies W Θ₀ = H.
    S :
    Solution d p K
    (S.B0⁻¹ * ((S.B0 * S.H) * (S.B0 * S.H).transpose)⁻¹ * (S.B0 * S.H)) * S.Theta0 = S.H
    Proof (Lean source)
    theorem H_eq_recover_mul_Theta0 (S : Solution d p K) : (S.B0⁻¹ * ((S.B0 * S.H) * (S.B0 * S.H).transpose)⁻¹ * (S.B0 * S.H)) * S.Theta0 = S.H := by have hΘ : S.Theta0 = (S.B0 * S.H).transpose * (S.B0 * S.H) := by simp only [Solution.Theta0, Matrix.transpose_mul, Matrix.mul_assoc] rw [hΘ] set G := (S.B0 * S.H) * (S.B0 * S.H).transpose with hG rw [Matrix.mul_assoc (S.B0⁻¹ * G⁻¹), ← Matrix.mul_assoc (S.B0 * S.H), ← hG, ← Matrix.mul_assoc, Matrix.mul_assoc S.B0⁻¹, Matrix.inv_mul_of_invertible, Matrix.mul_one, ← Matrix.mul_assoc, Matrix.inv_mul_of_invertible, Matrix.one_mul]
    Causalean.Discovery.LinearDisentanglement.H_eq_recover_mul_Theta0 · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:157
  • exists_change_of_basis theorem — (L1) The change-of-basis matrix. There is an invertible M with H' = M H. M is built from the recovery identity for H' (H' = W' Θ₀') by substituting Θ₀' = Θ₀ = Hᵀ B₀ᵀ B₀ H, giving H' = (W' Hᵀ B₀ᵀ B₀) H. Invertibility follows from the symmetric matrix N with H = N H' by right-cancelling the full-row-rank H/H'.
    S S' :
    Solution d p K
    hΘ0 :
    S.Theta0 = S'.Theta0
    ∃ M : Matrix (Fin d) (Fin d) ℝ, IsUnit M ∧ S'.H = M * S.H
    Proof (Lean source)
    theorem exists_change_of_basis (S S' : Solution d p K) (hΘ0 : S.Theta0 = S'.Theta0) : ∃ M : Matrix (Fin d) (Fin d) ℝ, IsUnit M ∧ S'.H = M * S.H := by -- `M := W' Hᵀ B₀ᵀ B₀` from `H' = W' Θ₀' = W' Θ₀ = W' (Hᵀ B₀ᵀ B₀ H)`. set W' := S'.B0⁻¹ * ((S'.B0 * S'.H) * (S'.B0 * S'.H).transpose)⁻¹ * (S'.B0 * S'.H) with hW' set N' := S.B0⁻¹ * ((S.B0 * S.H) * (S.B0 * S.H).transpose)⁻¹ * (S.B0 * S.H) with hN' have hH' : S'.H = (W' * S.H.transpose * S.B0.transpose * S.B0) * S.H := by have h1 : W' * S'.Theta0 = S'.H := H_eq_recover_mul_Theta0 S' rw [← hΘ0, Solution.Theta0] at h1 rw [← h1] simp only [Matrix.mul_assoc] have hH : S.H = (N' * S'.H.transpose * S'.B0.transpose * S'.B0) * S'.H := by have h1 : N' * S.Theta0 = S.H := H_eq_recover_mul_Theta0 S rw [hΘ0, Solution.Theta0] at h1 rw [← h1] simp only [Matrix.mul_assoc] set M := W' * S.H.transpose * S.B0.transpose * S.B0 with hM set N := N' * S'.H.transpose * S'.B0.transpose * S'.B0 with hN refine ⟨M, ?_, hH'⟩ -- `M` is a unit: `M N = 1` via right-cancellation by the full-row-rank `H`. have hMN : M * N = 1 := by have hcomp : (M * N) * S'.H = (1 : Matrix (Fin d) (Fin d) ℝ) * S'.H := by rw [Matrix.mul_assoc, ← hH, ← hH', Matrix.one_mul] -- right-cancel `H'` (it has right inverse `H'ᵀ (H' H'ᵀ)⁻¹`). have hrcancel : ∀ A B : Matrix (Fin d) (Fin d) ℝ, A * S'.H = B * S'.H → A = B := by intro A B hAB have h2 := congrArg (fun X => X * (S'.H.transpose * (S'.H * S'.H.transpose)⁻¹)) hAB simp only [Matrix.mul_assoc] at h2 rwa [H_mul_rightInv S', Matrix.mul_one, Matrix.mul_one] at h2 exact hrcancel _ _ hcomp exact IsUnit.of_mul_eq_one N hMN
    Causalean.Discovery.LinearDisentanglement.exists_change_of_basis · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:169
  • cancel_Ht_H theorem — Left-cancel Hᵀ and right-cancel H in Hᵀ X H = Hᵀ Y H. H has full row rank, so Hᵀ has a left inverse and H a right inverse.
    S :
    Solution d p K
    X Y :
    Matrix (Fin d) (Fin d) ℝ
    h :
    S.H.transpose * X * S.H = S.H.transpose * Y * S.H
    X = Y
    Proof (Lean source)
    theorem cancel_Ht_H (S : Solution d p K) {X Y : Matrix (Fin d) (Fin d) ℝ} (h : S.H.transpose * X * S.H = S.H.transpose * Y * S.H) : X = Y := by set R := S.H.transpose * (S.H * S.H.transpose)⁻¹ with hR set L := (S.H * S.H.transpose)⁻¹ * S.H with hL -- right-cancel `H`: multiply by `R` on the right. have hr : S.H.transpose * X = S.H.transpose * Y := by have h2 := congrArg (fun Z => Z * R) h simp only [Matrix.mul_assoc] at h2 rwa [H_mul_rightInv S, Matrix.mul_one, Matrix.mul_one] at h2 -- left-cancel `Hᵀ`: multiply by `L` on the left. have h3 := congrArg (fun Z => L * Z) hr simp only [← Matrix.mul_assoc] at h3 rwa [leftInv_mul_Ht S, Matrix.one_mul, Matrix.one_mul] at h3
    Causalean.Discovery.LinearDisentanglement.cancel_Ht_H · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:209
  • gram_identity theorem — (L2) The per-context Gram identity. Given H' = M H and Θₖ = Θₖ' for the observational (B₀/B'₀) and interventional (Bₖ/B'ₖ) matrices, the d × d Gram identity BᵀB = (B' M)ᵀ (B' M) holds.
    S S' :
    Solution d p K
    M :
    Matrix (Fin d) (Fin d) ℝ
    hM :
    S'.H = M * S.H
    B B' :
    Matrix (Fin d) (Fin d) ℝ
    :
    S.H.transpose * B.transpose * B * S.H = S'.H.transpose * B'.transpose * B' * S'.H
    B.transpose * B = (B' * M).transpose * (B' * M)
    Proof (Lean source)
    theorem gram_identity (S S' : Solution d p K) {M : Matrix (Fin d) (Fin d) ℝ} (hM : S'.H = M * S.H) {B B' : Matrix (Fin d) (Fin d) ℝ} (hΘ : S.H.transpose * B.transpose * B * S.H = S'.H.transpose * B'.transpose * B' * S'.H) : B.transpose * B = (B' * M).transpose * (B' * M) := by apply cancel_Ht_H S have e1 : S.H.transpose * (B.transpose * B) * S.H = S'.H.transpose * B'.transpose * B' * S'.H := by rw [← hΘ]; simp only [Matrix.mul_assoc] rw [e1, hM] simp only [Matrix.transpose_mul, Matrix.mul_assoc]
    Causalean.Discovery.LinearDisentanglement.gram_identity · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:225
  • Bint_sub_B0 theorem — Rank-one perturbation. Bₖ − B₀ = e_{iₖ} cₖᵀ (Matrix.vecMulVec): a perfect single-node intervention rewrites only the target row of B₀.
    S :
    Solution d p K
    k :
    Fin K
    S.Bint k - S.B0 = vecMulVec (stdVec d (S.target k)) (cvec S k)
    Proof (Lean source)
    theorem Bint_sub_B0 (S : Solution d p K) (k : Fin K) : S.Bint k - S.B0 = vecMulVec (stdVec d (S.target k)) (cvec S k) := by rw [S.hInt k, add_sub_cancel_left]; rfl
    Causalean.Discovery.LinearDisentanglement.Bint_sub_B0 · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:253
  • cvec_ne_zero theorem — (C, target-row form / sub-lemma (B)). The perturbation row is non-degenerate exactly when the intervention changes the precision matrix. Under Θₖ ≠ Θ₀, cₖ ≠ 0 (equivalently Bₖ ≠ B₀): if cₖ = 0 then Bₖ = B₀, hence Θₖ = Θ₀.
    S :
    Solution d p K
    k :
    Fin K
    hk :
    S.Theta k ≠ S.Theta0
    cvec S k ≠ 0
    Proof (Lean source)
    theorem cvec_ne_zero (S : Solution d p K) (k : Fin K) (hk : S.Theta k ≠ S.Theta0) : cvec S k ≠ 0 := by intro hc apply hk have hBeq : S.Bint k = S.B0 := by have := Bint_sub_B0 S k rw [hc, Matrix.vecMulVec_zero] at this exact sub_eq_zero.mp this rw [Solution.Theta, Solution.Theta0, hBeq]
    Causalean.Discovery.LinearDisentanglement.cvec_ne_zero · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:259
  • Bint_transpose_mulVec_target theorem — (B) Target row of Bₖ. (Bₖ)ᵀ *ᵥ e_{iₖ} = λₖ • e_{iₖ}: the iₖ-th row of a perfect-intervention matrix is λₖ e_{iₖ}ᵀ (the intervention zeroes the parent entries and sets the diagonal to λₖ).
    S :
    Solution d p K
    k :
    Fin K
    (S.Bint k).transpose *ᵥ stdVec d (S.target k) = (S.lam k) • stdVec d (S.target k)
    Proof (Lean source)
    theorem Bint_transpose_mulVec_target (S : Solution d p K) (k : Fin K) : (S.Bint k).transpose *ᵥ stdVec d (S.target k) = (S.lam k) • stdVec d (S.target k) := by funext j rw [Matrix.mulVec_transpose, Matrix.vecMul_eq_sum] simp only [stdVec, Finset.sum_apply, Pi.smul_apply, smul_eq_mul, Pi.single_apply] rw [Finset.sum_eq_single (S.target k)] · by_cases hj : j = S.target k · rw [hj, S.hInt k]; simp [Matrix.add_apply, Matrix.vecMulVec_apply, stdVec] · simp only [if_neg hj, mul_zero] rw [S.hInt k]; simp [Matrix.add_apply, Matrix.vecMulVec_apply, stdVec, hj] · intro x _ hx; simp [if_neg hx] · intro hx; exact absurd (Finset.mem_univ _) hx
    Causalean.Discovery.LinearDisentanglement.Bint_transpose_mulVec_target · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:272
  • latent_key_identity theorem — (C) Latent key identity. At the d × d (latent) level the difference of Gram matrices is a difference of two rank-one (outer-product) matrices: BₖᵀBₖ − B₀ᵀB₀ = λₖ² (e_{iₖ} e_{iₖ}ᵀ) − wₖ wₖᵀ where wₖ = B₀ᵀ *ᵥ e_{iₖ} is the iₖ-th row of B₀ (supported on Pa(iₖ)). This is the latent analogue of key_identity, and the structural engine of the orthogonal-correctness core.
    S :
    Solution d p K
    k :
    Fin K
    (S.Bint k).transpose * S.Bint k - S.B0.transpose * S.B0
    = (S.lam k) ^ 2 • vecMulVec (stdVec d (S.target k)) (stdVec d (S.target k))
    - vecMulVec (S.B0.transpose *ᵥ stdVec d (S.target k)) (S.B0.transpose *ᵥ stdVec d (S.target k))
    Proof (Lean source)
    theorem latent_key_identity (S : Solution d p K) (k : Fin K) : (S.Bint k).transpose * S.Bint k - S.B0.transpose * S.B0 = (S.lam k) ^ 2 • vecMulVec (stdVec d (S.target k)) (stdVec d (S.target k)) - vecMulVec (S.B0.transpose *ᵥ stdVec d (S.target k)) (S.B0.transpose *ᵥ stdVec d (S.target k)) := by -- Off-target rows of `Bₖ` agree with `B₀`, so the rank-one decompositions cancel except -- in the target row (mirrors the latent step inside `key_identity`). set i := S.target k with hi have hrow : ∀ l : Fin d, l ≠ i → ((S.Bint k).transpose *ᵥ stdVec d l) = (S.B0.transpose *ᵥ stdVec d l) := by intro l hl have hl' : l ≠ S.target k := hi ▸ hl funext a rw [S.hInt k] simp only [stdVec, Matrix.mulVec_single_one, Matrix.col_apply, Matrix.transpose_apply, Matrix.add_apply, Matrix.vecMulVec_apply, Pi.single_eq_of_ne hl', zero_mul, add_zero] rw [fact_transpose_mul (S.Bint k), fact_transpose_mul S.B0, ← Finset.add_sum_erase _ _ (Finset.mem_univ i), ← Finset.add_sum_erase _ _ (Finset.mem_univ i)] have hcancel : (∑ l ∈ Finset.univ.erase i, vecMulVec ((S.Bint k).transpose *ᵥ stdVec d l) ((S.Bint k).transpose *ᵥ stdVec d l)) = ∑ l ∈ Finset.univ.erase i, vecMulVec (S.B0.transpose *ᵥ stdVec d l) (S.B0.transpose *ᵥ stdVec d l) := by refine Finset.sum_congr rfl (fun l hl => ?_) rw [hrow l (Finset.ne_of_mem_erase hl)] rw [hcancel, Bint_transpose_mulVec_target, Matrix.vecMulVec_smul, Matrix.smul_vecMulVec, smul_smul, ← pow_two] abel
    Causalean.Discovery.LinearDisentanglement.latent_key_identity · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:288
  • transition_orthogonal theorem — (D) The transition factor is orthogonal. From the per-context Gram identity BₖᵀBₖ = (B'ₖ M)ᵀ(B'ₖ M) and invertibility, Oₖ = B'ₖ M Bₖ⁻¹ satisfies Oₖᵀ Oₖ = 1.
    S S' :
    Solution d p K
    M :
    Matrix (Fin d) (Fin d) ℝ
    hM :
    S'.H = M * S.H
    B B' :
    Matrix (Fin d) (Fin d) ℝ
    Invertible (B' * M)
    :
    S.H.transpose * B.transpose * B * S.H = S'.H.transpose * B'.transpose * B' * S'.H
    ((B' * M) * B⁻¹)ᵀ * ((B' * M) * B⁻¹) = 1
    Proof (Lean source)
    theorem transition_orthogonal (S S' : Solution d p K) {M : Matrix (Fin d) (Fin d) ℝ} (hM : S'.H = M * S.H) {B B' : Matrix (Fin d) (Fin d) ℝ} [Invertible B] [Invertible (B' * M)] (hΘ : S.H.transpose * B.transpose * B * S.H = S'.H.transpose * B'.transpose * B' * S'.H) : ((B' * M) * B⁻¹)ᵀ * ((B' * M) * B⁻¹) = 1 := gram_to_orthogonal (gram_identity S S' hM hΘ)
    Causalean.Discovery.LinearDisentanglement.transition_orthogonal · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:323
  • gram_identity_conj theorem — The per-context Gram identity in the symmetric form BᵀB = Mᵀ B'ᵀ B' M (the conjugation form of gram_identity).
    S S' :
    Solution d p K
    M :
    Matrix (Fin d) (Fin d) ℝ
    hM :
    S'.H = M * S.H
    B B' :
    Matrix (Fin d) (Fin d) ℝ
    :
    S.H.transpose * B.transpose * B * S.H = S'.H.transpose * B'.transpose * B' * S'.H
    B.transpose * B = M.transpose * (B'.transpose * B') * M
    Proof (Lean source)
    theorem gram_identity_conj (S S' : Solution d p K) {M : Matrix (Fin d) (Fin d) ℝ} (hM : S'.H = M * S.H) {B B' : Matrix (Fin d) (Fin d) ℝ} (hΘ : S.H.transpose * B.transpose * B * S.H = S'.H.transpose * B'.transpose * B' * S'.H) : B.transpose * B = M.transpose * (B'.transpose * B') * M := by rw [gram_identity S S' hM hΘ, Matrix.transpose_mul] simp only [Matrix.mul_assoc]
    Causalean.Discovery.LinearDisentanglement.gram_identity_conj · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:340
  • gram_diff_transport theorem — (R1) Latent Gram-difference transport. Bₖᵀ Bₖ − B₀ᵀ B₀ = Mᵀ (B'ₖᵀ B'ₖ − B'₀ᵀ B'₀) M. Subtracting the observational conjugation identity from the k-th one.
    S S' :
    Solution d p K
    M :
    Matrix (Fin d) (Fin d) ℝ
    hM :
    S'.H = M * S.H
    hΘ0 :
    S.Theta0 = S'.Theta0
    :
    ∀ k, S.Theta k = S'.Theta k
    k :
    Fin K
    (S.Bint k).transpose * S.Bint k - S.B0.transpose * S.B0
    = M.transpose * ((S'.Bint k).transpose * S'.Bint k - (S'.B0).transpose * S'.B0) * M
    Proof (Lean source)
    theorem gram_diff_transport (S S' : Solution d p K) {M : Matrix (Fin d) (Fin d) ℝ} (hM : S'.H = M * S.H) (hΘ0 : S.Theta0 = S'.Theta0) (hΘ : ∀ k, S.Theta k = S'.Theta k) (k : Fin K) : (S.Bint k).transpose * S.Bint k - S.B0.transpose * S.B0 = M.transpose * ((S'.Bint k).transpose * S'.Bint k - (S'.B0).transpose * S'.B0) * M := by have hk : (S.Bint k).transpose * S.Bint k = M.transpose * ((S'.Bint k).transpose * S'.Bint k) * M := by refine gram_identity_conj S S' hM ?_ have := hΘ k simp only [Solution.Theta, Matrix.mul_assoc] at this ⊢ exact this have h0 : S.B0.transpose * S.B0 = M.transpose * ((S'.B0).transpose * S'.B0) * M := by refine gram_identity_conj S S' hM ?_ simp only [Solution.Theta0, Matrix.mul_assoc] at hΘ0 ⊢ exact hΘ0 rw [hk, h0, Matrix.mul_sub, Matrix.sub_mul]
    Causalean.Discovery.LinearDisentanglement.gram_diff_transport · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:350
  • conj_vecMulVec_transpose theorem — Mᵀ (u vᵀ) M = (Mᵀ u)(Mᵀ v)ᵀ (conjugation of a rank-one matrix by Mᵀ · M).
    q :
    M :
    Matrix (Fin q) (Fin q) ℝ
    u v :
    Fin q → ℝ
    M.transpose * vecMulVec u v * M
    = vecMulVec (M.transpose *ᵥ u) (M.transpose *ᵥ v)
    Proof (Lean source)
    theorem conj_vecMulVec_transpose {q : ℕ} (M : Matrix (Fin q) (Fin q) ℝ) (u v : Fin q → ℝ) : M.transpose * vecMulVec u v * M = vecMulVec (M.transpose *ᵥ u) (M.transpose *ᵥ v) := by rw [Matrix.mul_vecMulVec, Matrix.vecMulVec_mul, ← Matrix.mulVec_transpose]
    Causalean.Discovery.LinearDisentanglement.conj_vecMulVec_transpose · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:370
  • central_rank2_eq theorem — (R1+C) The central rank-≤2 equation. Combining the Gram-difference transport Δₖ = Mᵀ Δ'ₖ M (gram_diff_transport) with the latent key identity (C) on both sides, Δₖ = λₖ²(eₖeₖᵀ) − wₖwₖᵀ and Δ'ₖ = λ'ₖ²(e'ₖe'ₖᵀ) − w'ₖw'ₖᵀ, gives the rank-≤2 identity in fully outer-product form, with the primed outer products transported by Mᵀ: λₖ²(eₖeₖᵀ) − wₖwₖᵀ = λ'ₖ²((Mᵀe'ₖ)(Mᵀe'ₖ)ᵀ) − (Mᵀw'ₖ)(Mᵀw'ₖ)ᵀ.
    S S' :
    Solution d p K
    M :
    Matrix (Fin d) (Fin d) ℝ
    hM :
    S'.H = M * S.H
    hΘ0 :
    S.Theta0 = S'.Theta0
    :
    ∀ k, S.Theta k = S'.Theta k
    k :
    Fin K
    (S.lam k) ^ 2 • vecMulVec (stdVec d (S.target k)) (stdVec d (S.target k))
    - vecMulVec (S.B0.transpose *ᵥ stdVec d (S.target k)) (S.B0.transpose *ᵥ stdVec d (S.target k))
    = (S'.lam k) ^ 2 • vecMulVec (M.transpose *ᵥ stdVec d (S'.target k)) (M.transpose *ᵥ stdVec d (S'.target k))
    - vecMulVec (M.transpose *ᵥ (S'.B0.transpose *ᵥ stdVec d (S'.target k))) (M.transpose *ᵥ (S'.B0.transpose *ᵥ stdVec d (S'.target k)))
    Proof (Lean source)
    theorem central_rank2_eq (S S' : Solution d p K) {M : Matrix (Fin d) (Fin d) ℝ} (hM : S'.H = M * S.H) (hΘ0 : S.Theta0 = S'.Theta0) (hΘ : ∀ k, S.Theta k = S'.Theta k) (k : Fin K) : (S.lam k) ^ 2 • vecMulVec (stdVec d (S.target k)) (stdVec d (S.target k)) - vecMulVec (S.B0.transpose *ᵥ stdVec d (S.target k)) (S.B0.transpose *ᵥ stdVec d (S.target k)) = (S'.lam k) ^ 2 • vecMulVec (M.transpose *ᵥ stdVec d (S'.target k)) (M.transpose *ᵥ stdVec d (S'.target k)) - vecMulVec (M.transpose *ᵥ (S'.B0.transpose *ᵥ stdVec d (S'.target k))) (M.transpose *ᵥ (S'.B0.transpose *ᵥ stdVec d (S'.target k))) := by have htrans := gram_diff_transport S S' hM hΘ0 hΘ k rw [latent_key_identity, latent_key_identity] at htrans rw [htrans, Matrix.mul_sub, Matrix.sub_mul, Matrix.mul_smul, Matrix.smul_mul, conj_vecMulVec_transpose, conj_vecMulVec_transpose]
    Causalean.Discovery.LinearDisentanglement.central_rank2_eq · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:376
  • transpose_mulVec_stdVec theorem — Mᵀ *ᵥ eᵢ is the i-th row of M, viewed as a vector: (Mᵀ *ᵥ eᵢ) j = Mᵢⱼ. This is the bridge that turns the transported outer products of central_rank2_eq into statements about the rows of M.
    n :
    M :
    Matrix (Fin n) (Fin n) ℝ
    i :
    Fin n
    M.transpose *ᵥ stdVec n i = (fun j => M i j)
    Proof (Lean source)
    theorem transpose_mulVec_stdVec {n : ℕ} (M : Matrix (Fin n) (Fin n) ℝ) (i : Fin n) : M.transpose *ᵥ stdVec n i = (fun j => M i j) := by funext j rw [Matrix.mulVec_transpose, stdVec, Matrix.single_vecMul, one_smul, Matrix.row_apply]
    Causalean.Discovery.LinearDisentanglement.transpose_mulVec_stdVec · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:396
  • B0_source_row theorem — (R3 ingredient) The iₖ-th row of B₀ at a source. If iₖ is a *source* node (no parents: (B₀)_{iₖ,j} = 0 for every j ≠ iₖ), then wₖ = B₀ᵀ *ᵥ e_{iₖ} = β e_{iₖ} with β = (B₀)_{iₖ,iₖ} > 0. This collapses the latent key identity (C) to the rank-one Δₖ = (λₖ² − β²) e_{iₖ} e_{iₖ}ᵀ, the base case of the monomial induction (R3): the transported equation then forces the i'ₖ-th row of M to be a multiple of e_{iₖ}.
    S :
    Solution d p K
    i :
    Fin d
    hsrc :
    ∀ j
    if
    j ≠ i
    then
    S.B0 i j = 0
    S.B0.transpose *ᵥ stdVec d i = (S.B0 i i) • stdVec d i
    Proof (Lean source)
    theorem B0_source_row (S : Solution d p K) (i : Fin d) (hsrc : ∀ j, j ≠ i → S.B0 i j = 0) : S.B0.transpose *ᵥ stdVec d i = (S.B0 i i) • stdVec d i := by funext j rw [Matrix.mulVec_transpose, Matrix.vecMul_eq_sum] simp only [stdVec, Finset.sum_apply, Pi.smul_apply, smul_eq_mul] rw [Finset.sum_eq_single i] · rw [Pi.single_eq_same, one_mul] by_cases hj : j = i · subst hj; rw [Pi.single_eq_same, mul_one] · rw [Pi.single_eq_of_ne hj, mul_zero]; exact hsrc j hj · intro x _ hx; rw [Pi.single_eq_of_ne hx, zero_mul] · intro hx; exact absurd (Finset.mem_univ _) hx
    Causalean.Discovery.LinearDisentanglement.B0_source_row · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:404
  • permMat_mulVec_stdVec theorem — permMat σ *ᵥ eᵢ = e_{σ i}: the permutation matrix sends the i-th basis vector to the σ(i)-th.
    σ :
    Perm (Fin d)
    i :
    Fin d
    permMat σ *ᵥ stdVec d i = stdVec d (σ i)
    Proof (Lean source)
    theorem permMat_mulVec_stdVec (σ : Perm (Fin d)) (i : Fin d) : permMat σ *ᵥ stdVec d i = stdVec d (σ i) := by rw [stdVec, Matrix.mulVec_single_one] funext a simp only [permMat, Matrix.col_apply, Matrix.of_apply, stdVec, Pi.single_apply]
    Causalean.Discovery.LinearDisentanglement.permMat_mulVec_stdVec · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:430
  • diag_permMat_mulVec_stdVec theorem — A signed permutation matrix sends eᵢ to the signed basis vector ν (σ i) • e_{σ i}.
    σ :
    Perm (Fin d)
    ν :
    Fin d → ℝ
    i :
    Fin d
    (diagonal ν * permMat σ) *ᵥ stdVec d i = (ν (σ i)) • stdVec d (σ i)
    Proof (Lean source)
    theorem diag_permMat_mulVec_stdVec (σ : Perm (Fin d)) (ν : Fin d → ℝ) (i : Fin d) : (diagonal ν * permMat σ) *ᵥ stdVec d i = (ν (σ i)) • stdVec d (σ i) := by rw [← Matrix.mulVec_mulVec, permMat_mulVec_stdVec] funext a rw [Matrix.mulVec_diagonal] simp only [Pi.smul_apply, smul_eq_mul] by_cases ha : a = σ i · subst ha rw [stdVec, Pi.single_eq_same, mul_one] · rw [stdVec, Pi.single_eq_of_ne ha, mul_zero, mul_zero]
    Causalean.Discovery.LinearDisentanglement.diag_permMat_mulVec_stdVec · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:438
  • target_readoff theorem — Signed target read-off. Suppose the collapse holds for context k and the observational context: B'ₖ M = diagonal ν permMat σ Bₖ and B'₀ M = diagonal ν permMat σ B₀, with Θₖ ≠ Θ₀. Then the primed target equals the relabelled target: i'ₖ = σ(iₖ).
    S S' :
    Solution d p K
    M :
    Matrix (Fin d) (Fin d) ℝ
    σ :
    Perm (Fin d)
    ν :
    Fin d → ℝ
    k :
    Fin K
    hk :
    S.Theta k ≠ S.Theta0
    :
    ∀ i, ν i = 1 ∨ ν i = -1
    hBk :
    S'.Bint k * M = diagonal ν * permMat σ * S.Bint k
    hB0 :
    S'.B0 * M = diagonal ν * permMat σ * S.B0
    S'.target k = σ (S.target k)
    Proof (Lean source)
    theorem target_readoff (S S' : Solution d p K) {M : Matrix (Fin d) (Fin d) ℝ} {σ : Perm (Fin d)} {ν : Fin d → ℝ} (k : Fin K) (hk : S.Theta k ≠ S.Theta0) (hν : ∀ i, ν i = 1 ∨ ν i = -1) (hBk : S'.Bint k * M = diagonal ν * permMat σ * S.Bint k) (hB0 : S'.B0 * M = diagonal ν * permMat σ * S.B0) : S'.target k = σ (S.target k) := by -- Subtract the observational relation: -- `(B'ₖ − B'₀) M = diagonal ν permMat σ (Bₖ − B₀)`. have hsub : (S'.Bint k - S'.B0) * M = diagonal ν * permMat σ * (S.Bint k - S.B0) := by rw [Matrix.sub_mul, Matrix.mul_sub, hBk, hB0] -- Rewrite both sides as rank-one (outer-product) matrices. rw [Bint_sub_B0, Bint_sub_B0, Matrix.vecMulVec_mul, Matrix.mul_vecMulVec, diag_permMat_mulVec_stdVec] at hsub -- Pick an index `b` where `cₖ` is non-zero. obtain ⟨b, hb⟩ := Function.ne_iff.mp (cvec_ne_zero S k hk) rw [Pi.zero_apply] at hb -- Compare the `(σ iₖ, b)` entries of the two rank-one matrices. have hentry := congrFun (congrFun hsub (σ (S.target k))) b simp only [Matrix.vecMulVec_apply, Pi.smul_apply, smul_eq_mul, stdVec, Pi.single_eq_same] at hentry have hνne : ν (σ (S.target k)) ≠ 0 := by rcases hν (σ (S.target k)) with h | h <;> rw [h] <;> norm_num -- LHS `(e_{i'ₖ})_{σ iₖ} · (Mᵀ c'ₖ)_b = (cₖ)_b ≠ 0` forces `(e_{i'ₖ})_{σ iₖ} ≠ 0`. by_contra hne rw [Pi.single_eq_of_ne (fun h => hne h.symm), zero_mul] at hentry exact (mul_ne_zero (mul_ne_zero hνne one_ne_zero) hb) hentry.symm
    Causalean.Discovery.LinearDisentanglement.target_readoff · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:462
  • sum_smul_vecMulVec_stdVec_eq_diagonal theorem — ∑ n, (c n) • (e_n e_nᵀ) = diagonal c (the rank-one standard-basis outer products sum to a diagonal matrix).
    n :
    c :
    Fin n → ℝ
    (∑ i, (c i) • vecMulVec (stdVec n i) (stdVec n i)) = diagonal c
    Proof (Lean source)
    theorem sum_smul_vecMulVec_stdVec_eq_diagonal {n : ℕ} (c : Fin n → ℝ) : (∑ i, (c i) • vecMulVec (stdVec n i) (stdVec n i)) = diagonal c := by ext a b simp only [Matrix.sum_apply, Matrix.smul_apply, Matrix.vecMulVec_apply, stdVec, Pi.single_apply, smul_eq_mul, Matrix.diagonal_apply] rw [Finset.sum_eq_single a] · by_cases hab : a = b · subst hab; simp · simp [hab, Ne.symm hab] · intro x _ hx; simp [Ne.symm hx] · intro hx; exact absurd (Finset.mem_univ a) hx
    Causalean.Discovery.LinearDisentanglement.sum_smul_vecMulVec_stdVec_eq_diagonal · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:506
  • sum_latent_diff_unprimed theorem — (S1) The unprimed SUM TRICK. Let kof be a section of the surjective target map (S.target (kof n) = n). Summing the latent key identity over kof gives ∑ₙ (B_{kof n}ᵀ B_{kof n} − B₀ᵀB₀) = diagonal(d) − B₀ᵀB₀ with d n = (λ_{kof n})².
    S :
    Solution d p K
    kof :
    Fin d → Fin K
    hkof :
    ∀ n, S.target (kof n) = n
    (∑ n, ((S.Bint (kof n)).transpose * S.Bint (kof n) - S.B0.transpose * S.B0))
    = diagonal (fun n => (S.lam (kof n)) ^ 2) - S.B0.transpose * S.B0
    Proof (Lean source)
    theorem sum_latent_diff_unprimed (S : Solution d p K) (kof : Fin d → Fin K) (hkof : ∀ n, S.target (kof n) = n) : (∑ n, ((S.Bint (kof n)).transpose * S.Bint (kof n) - S.B0.transpose * S.B0)) = diagonal (fun n => (S.lam (kof n)) ^ 2) - S.B0.transpose * S.B0 := by -- Rewrite each summand by the latent key identity. have hterm : ∀ n, (S.Bint (kof n)).transpose * S.Bint (kof n) - S.B0.transpose * S.B0 = (S.lam (kof n)) ^ 2 • vecMulVec (stdVec d n) (stdVec d n) - vecMulVec (S.B0.transpose *ᵥ stdVec d n) (S.B0.transpose *ᵥ stdVec d n) := by intro n rw [latent_key_identity S (kof n), hkof n] rw [Finset.sum_congr rfl (fun n _ => hterm n)] have hdistrib : (∑ n, ((S.lam (kof n)) ^ 2 • vecMulVec (stdVec d n) (stdVec d n) - vecMulVec (S.B0.transpose *ᵥ stdVec d n) (S.B0.transpose *ᵥ stdVec d n))) = (∑ n, (S.lam (kof n)) ^ 2 • vecMulVec (stdVec d n) (stdVec d n)) - ∑ n, vecMulVec (S.B0.transpose *ᵥ stdVec d n) (S.B0.transpose *ᵥ stdVec d n) := Finset.sum_sub_distrib .. rw [hdistrib, sum_smul_vecMulVec_stdVec_eq_diagonal, ← fact_transpose_mul S.B0]
    Causalean.Discovery.LinearDisentanglement.sum_latent_diff_unprimed · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:520
  • orthogonal_of_diag_conj theorem — (S2) Diagonal conjugation ⟹ orthogonal rescaling. From Mᵀ diag(d') M = diag(d) with d, d' strictly positive, the matrix O = diag(fun i => √(d' i)) * M * diag(fun i => (√(d i))⁻¹) is orthogonal: Oᵀ O = 1.
    q :
    Matrix (Fin q) (Fin q) ℝ
    Fin q → ℝ
    hd :
    ∀ i, 0 < dv i
    hd' :
    ∀ i, 0 < dv' i
    hconj :
    M.transpose * diagonal dv' * M = diagonal dv
    (diagonal (fun i => sqrt (dv' i)) * M * diagonal (fun i => (sqrt (dv i))⁻¹)).transpose * (diagonal (fun i => sqrt (dv' i)) * M * diagonal (fun i => (sqrt (dv i))⁻¹))
    = 1
    Proof (Lean source)
    theorem orthogonal_of_diag_conj {q : ℕ} {M : Matrix (Fin q) (Fin q) ℝ} {dv dv' : Fin q → ℝ} (hd : ∀ i, 0 < dv i) (hd' : ∀ i, 0 < dv' i) (hconj : M.transpose * diagonal dv' * M = diagonal dv) : (diagonal (fun i => sqrt (dv' i)) * M * diagonal (fun i => (sqrt (dv i))⁻¹)).transpose * (diagonal (fun i => sqrt (dv' i)) * M * diagonal (fun i => (sqrt (dv i))⁻¹)) = 1 := by -- `diag(√d')ᵀ diag(√d') = diag(d')` and `diag(1/√d) diag(d) diag(1/√d) = 1`. have hsqrt' : diagonal (fun i => sqrt (dv' i)) * diagonal (fun i => sqrt (dv' i)) = diagonal dv' := by rw [Matrix.diagonal_mul_diagonal] congr 1; funext i exact Real.mul_self_sqrt (le_of_lt (hd' i)) have hdpos : ∀ i, sqrt (dv i) ≠ 0 := fun i => (Real.sqrt_pos.mpr (hd i)).ne' have hkill : diagonal (fun i => (sqrt (dv i))⁻¹) * diagonal dv * diagonal (fun i => (sqrt (dv i))⁻¹) = 1 := by rw [Matrix.diagonal_mul_diagonal, Matrix.diagonal_mul_diagonal] rw [show (fun i => (sqrt (dv i))⁻¹ * dv i * (sqrt (dv i))⁻¹) = (fun _ => (1 : ℝ)) from ?_, Matrix.diagonal_one] funext i have hsq : sqrt (dv i) * sqrt (dv i) = dv i := Real.mul_self_sqrt (le_of_lt (hd i)) have : (sqrt (dv i))⁻¹ * dv i * (sqrt (dv i))⁻¹ = (sqrt (dv i))⁻¹ * (sqrt (dv i) * sqrt (dv i)) * (sqrt (dv i))⁻¹ := by rw [hsq] rw [this] field_simp rw [div_self (hdpos i)] rw [Matrix.transpose_mul, Matrix.transpose_mul, Matrix.diagonal_transpose, Matrix.diagonal_transpose] -- Regroup everything right-associated, insert `D₁ D₁ = diag(d')`, then `Mᵀ diag(d') M = diag(d)`. -- Target: D₂ (Mᵀ (D₁ D₁) M) D₂ = D₂ (Mᵀ diag(d') M) D₂ = D₂ diag(d) D₂ = 1. have key : diagonal (fun i => (sqrt (dv i))⁻¹) * (M.transpose * diagonal dv' * M) * diagonal (fun i => (sqrt (dv i))⁻¹) = 1 := by rw [hconj]; exact hkill -- Normalize `key` to right-associated form, insert `diag(d') = D₁ D₁`, finish. have key' := key simp only [Matrix.mul_assoc] at key' ⊢ rw [← hsqrt'] at key' simp only [Matrix.mul_assoc] at key' exact key'
    Causalean.Discovery.LinearDisentanglement.orthogonal_of_diag_conj · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:651
  • permMat_apply_symm theorem — (permMat σ) a c = 1 iff c = σ.symm a, else 0 (column read-off of permMat).
    σ :
    Perm (Fin d)
    a c :
    Fin d
    permMat σ a c = if c = σ.symm a then (1 : ℝ) else 0
    Proof (Lean source)
    theorem permMat_apply_symm (σ : Perm (Fin d)) (a c : Fin d) : permMat σ a c = if c = σ.symm a then (1 : ℝ) else 0 := by simp only [permMat, Matrix.of_apply] by_cases h : c = σ.symm a · subst h; simp [Equiv.apply_symm_apply] · rw [if_neg h, if_neg] exact fun hac => h (by rw [hac, Equiv.symm_apply_apply])
    Causalean.Discovery.LinearDisentanglement.permMat_apply_symm · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:741
  • permMat_conj_apply theorem — Permutation conjugation entry. (permMat σ * X * (permMat σ)ᵀ) a b = X (σ.symm a) (σ.symm b): conjugating X by permMat σ relabels rows and columns by σ.symm.
    σ :
    Perm (Fin d)
    X :
    Matrix (Fin d) (Fin d) ℝ
    a b :
    Fin d
    (permMat σ * X * (permMat σ).transpose) a b = X (σ.symm a) (σ.symm b)
    Proof (Lean source)
    theorem permMat_conj_apply (σ : Perm (Fin d)) (X : Matrix (Fin d) (Fin d) ℝ) (a b : Fin d) : (permMat σ * X * (permMat σ).transpose) a b = X (σ.symm a) (σ.symm b) := by rw [Matrix.mul_apply] have hstep : ∀ c, (permMat σ * X) a c = X (σ.symm a) c := by intro c rw [Matrix.mul_apply] rw [Finset.sum_eq_single (σ.symm a)] · rw [permMat_apply_symm, if_pos rfl, one_mul] · intro x _ hx; rw [permMat_apply_symm, if_neg hx, zero_mul] · intro h; exact absurd (Finset.mem_univ _) h simp_rw [hstep, Matrix.transpose_apply, permMat_apply_symm] rw [Finset.sum_eq_single (σ.symm b)] · rw [if_pos rfl, mul_one] · intro x _ hx; rw [if_neg hx, mul_zero] · intro h; exact absurd (Finset.mem_univ _) h
    Causalean.Discovery.LinearDisentanglement.permMat_conj_apply · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:750
  • exists_orderPerm theorem — (L4) Orthogonal correctness (full statement).
    S S' :
    Solution d p K
    hcov :
    Bijective S.target
    hcov' :
    Bijective S'.target
    hNondeg :
    ∀ k, S.Theta k ≠ S.Theta0
    hΘ0 :
    S.Theta0 = S'.Theta0
    :
    ∀ k, S.Theta k = S'.Theta k
    M :
    Matrix (Fin d) (Fin d) ℝ
    hM :
    S'.H = M * S.H
    ∃ (σ : Perm (Fin d)) (μ ν : Fin d → ℝ),
    S.InSG σ ∧
    (∀ i, μ i ≠ 0) ∧
    (∀ i, ν i = 1 ∨ ν i = -1) ∧
    M = diagonal μ * permMat σ ∧
    S'.B0 * M = diagonal ν * permMat σ * S.B0 ∧
    (∀ k, S'.Bint k * M = diagonal ν * permMat σ * S.Bint k) ∧
    (∀ k, S'.target k = σ (S.target k))
    Proof (Lean source)
    theorem exists_orderPerm (S S' : Solution d p K) (hcov : Bijective S.target) (hcov' : Bijective S'.target) (hNondeg : ∀ k, S.Theta k ≠ S.Theta0) (hΘ0 : S.Theta0 = S'.Theta0) (hΘ : ∀ k, S.Theta k = S'.Theta k) {M : Matrix (Fin d) (Fin d) ℝ} (hM : S'.H = M * S.H) : ∃ (σ : Perm (Fin d)) (μ ν : Fin d → ℝ), S.InSG σ ∧ (∀ i, μ i ≠ 0) ∧ (∀ i, ν i = 1 ∨ ν i = -1) ∧ M = diagonal μ * permMat σ ∧ S'.B0 * M = diagonal ν * permMat σ * S.B0 ∧ (∀ k, S'.Bint k * M = diagonal ν * permMat σ * S.Bint k) ∧ (∀ k, S'.target k = σ (S.target k)) := by obtain ⟨σ, μ, ν, hσ, hμ, hν, hMeq, hB0rel, hBintrel⟩ := orthogonal_collapse S S' hcov hcov' hNondeg hΘ0 hΘ hM exact ⟨σ, μ, ν, hσ, hμ, hν, hMeq, hB0rel, hBintrel, fun k => target_readoff S S' k (hNondeg k) hν (hBintrel k) hB0rel⟩
    Causalean.Discovery.LinearDisentanglement.exists_orderPerm · Causalean/Discovery/LinearDisentanglement/Uniqueness.lean:1437