Discovery.LinearDisentanglement
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.
Standard basis (column) vector e_i : Fin d → ℝ, e_i j = ⟦j = i⟧.
The permutation matrix of σ, with (permMat σ) i j = 1 if i = σ j and 0 otherwise — the convention (P_σ)_{ij} = ⟦i = σ(j)⟧ of the paper.
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)
The observational precision matrix Θ₀ = Hᵀ B₀ᵀ B₀ H ∈ ℝ^{p×p}.
The interventional precision matrix Θ_k = Hᵀ Bₖᵀ Bₖ H ∈ ℝ^{p×p}.
The order-preserving relabelings S(𝒢): permutations σ with σ i < σ j for every edge j → i.
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.
Proof (Lean source)
1 supporting declaration (lemmas, instances)
-
permMat_transpose_multheorem — permMat σ is orthogonal: (permMat σ)ᵀ * permMat σ = 1.Proof (Lean source)
theorem permMat_transpose_mul {d : ℕ} (σ : Perm (Fin d)) : (permMat σ).transpose * permMat σ = 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 (σ i)] · simp [σ.injective.eq_iff] · intro j _ hj simp [hj] · intro h; exact absurd (Finset.mem_univ _) h
PartialOrderRQ 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".
Parents of i: nodes j with an edge j → i.
Pa(i) = pa(i) ∪ {i}.
Ancestors of i: nodes j with a directed path j ⇝ i (transitive closure of the edge relation).
An(i) = an(i) ∪ {i}.
The partial order ≺_𝒢: i ≺ j iff j is an ancestor of i.
The reflexive closure ⪯_𝒢 of ≺_𝒢.
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)
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)
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)
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
Proof (Lean source)
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
Proof (Lean source)
16 supporting declarations (lemmas, instances)
-
prec_lttheorem — 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. -
not_prec_selftheorem — ≺ is irreflexive: a node is never its own strict ancestor.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) -
inner_toLp_eq_dotProducttheorem — 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.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 -
dotProduct_self_postheorem — 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.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) -
eq_of_mul_eq_mul_row_indeptheorem — 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.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 -
row_indep_of_mul_row_indeptheorem — 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.hypothesesconclusionLinearIndependent ℝ Q.rowProof (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 -
porq_diag_postheorem — The diagonal of any PORQ factor is strictly positive.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) -
porq_row_splittheorem — 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.hypothesesconclusionS.H i = R i i • Q i + ∑ k ∈ Finset.univ.erase i, R i k • Q kProof (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)] -
porqResidual_eqtheorem — Unfolding equation for porqResidual: it is toLp hᵢ minus its orthogonal projection onto the ancestor span porqAncSpan i.hypothesesconclusionporqResidual S iProof (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] -
porqResidual_mem_ancSpantheorem — Each strict-ancestor residual lies in the ancestor span porqAncSpan i.hypothesesconclusionporqResidual S k ∈ porqAncSpan S iProof (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⟩ -
porqResidual_mem_orthogonaltheorem — The i-th residual is orthogonal to its ancestor span: porqResidual i ∈ (Wᵢ)ᗮ.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 _ -
porqResidual_orthogonaltheorem — 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ᵢ)ᗮ.hypothesesconclusionProof (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) -
toLp_H_linearIndependenttheorem — The images of the rows of H under toLp are linearly independent (full row rank of H transported through the linear isomorphism toLp).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 -
porqResidual_mem_HspanLEtheorem — 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 ⪯).hypothesesconclusionProof (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 -
porqResidual_ne_zerotheorem — 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).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' -
porq_rowCoeffstheorem — 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).hypothesesconclusion∃ c : Fin d → ℝ,(∀ j, ¬ S.preceq i j → c j = 0) ∧c i = ‖porqResidual S i‖ ∧= ∑ 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]
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.
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
Proof (Lean source)
KeyIdentity 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.
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
Proof (Lean source)
1 supporting declaration (lemmas, instances)
-
fact_transpose_multheorem — Fact (rank-one decomposition). For any square B, BᵀB = Σᵢ (Bᵀeᵢ)⊗², where v⊗² = v vᵀ (Matrix.vecMulVec v v).hypothesesProof (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]
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.
The row span of a p × p matrix: the subspace of Fin p → ℝ spanned by its rows.
The span of the rows of H indexed by a set 𝓘 ⊆ [d].
The span of the rows of Q (from a partial order RQ decomposition) indexed by 𝓘.
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
Proof (Lean source)
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
Proof (Lean source)
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
Proof (Lean source)
SigmaSolutions 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.
Relabeling a solution by an order-preserving permutation gives another valid solution.
Definition (Lean source)
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
Proof (Lean source)
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(𝒢).
The perturbation row cₖ : Fin d → ℝ of a perfect intervention: cₖ j = λₖ (eᵢₖ)ⱼ − (B₀)_{iₖ,j}, so that Bₖ = B₀ + e_{iₖ} cₖᵀ.
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
Proof (Lean source)
38 supporting declarations (lemmas, instances)
-
gram_to_orthogonaltheorem — (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⁻¹.hypothesesconclusion(Y * X⁻¹)ᵀ * (Y * X⁻¹) = 1Proof (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] -
B0_blockTriangulartheorem — B0 is upper triangular in the BlockTriangular id sense.Proof (Lean source)
theorem B0_blockTriangular (S : Solution d p K) : S.B0.BlockTriangular id := S.hB0up -
Bint_blockTriangulartheorem — 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).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] -
B0_det_postheorem — det B0 = ∏ᵢ (B0)ᵢᵢ > 0.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) -
Bint_diag_postheorem — (Bint k)ᵢᵢ = (B0)ᵢᵢ off the target, and = λₖ on the target — in both cases strictly positive.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 -
Bint_det_postheorem — det (Bint k) = ∏ᵢ (Bint k)ᵢᵢ > 0.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) -
B0_invertibleinstance — 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') -
Bint_invertibleinstance — 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') -
vecMul_H_injectivetheorem — vecMul · H is injective (full row rank of 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) -
HHt_posDeftheorem — The latent Gram matrix H Hᵀ is positive definite.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 -
HHt_invertibleinstance — 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) -
B0H_gram_invertibleinstance — 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)) -
H_mul_rightInvtheorem — R := Hᵀ (H Hᵀ)⁻¹ is a right inverse of H: H R = 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] -
leftInv_mul_Httheorem — L := (H Hᵀ)⁻¹ H is a left inverse of Hᵀ: L Hᵀ = 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] -
H_eq_recover_mul_Theta0theorem — The recovery identity H = W Θ₀. With C = B₀ H and Θ₀ = Cᵀ C, the matrix W = B₀⁻¹ (C Cᵀ)⁻¹ C satisfies W Θ₀ = H.hypothesesS :Solution d p Kconclusion(S.B0⁻¹ * ((S.B0 * S.H) * (S.B0 * S.H).transpose)⁻¹ * (S.B0 * S.H)) * S.Theta0 = S.HProof (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] -
exists_change_of_basistheorem — (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'.hypothesesS S' :Solution d p KhΘ0 :S.Theta0 = S'.Theta0Proof (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 -
cancel_Ht_Htheorem — 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.hypothesesS :Solution d p Kh :S.H.transpose * X * S.H = S.H.transpose * Y * S.HconclusionX = YProof (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 -
gram_identitytheorem — (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.hypothesesS S' :Solution d p KhM :S'.H = M * S.HhΘ :S.H.transpose * B.transpose * B * S.H = S'.H.transpose * B'.transpose * B' * S'.HconclusionB.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] -
Bint_sub_B0theorem — Rank-one perturbation. Bₖ − B₀ = e_{iₖ} cₖᵀ (Matrix.vecMulVec): a perfect single-node intervention rewrites only the target row of B₀.hypotheses -
cvec_ne_zerotheorem — (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 Θₖ = Θ₀.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] -
Bint_transpose_mulVec_targettheorem — (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 λₖ).hypothesesProof (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 -
latent_key_identitytheorem — (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.hypothesesProof (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 -
transition_orthogonaltheorem — (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.hypothesesS S' :Solution d p KhM :S'.H = M * S.HB B' :hΘ :S.H.transpose * B.transpose * B * S.H = S'.H.transpose * B'.transpose * B' * S'.Hconclusion((B' * M) * B⁻¹)ᵀ * ((B' * M) * B⁻¹) = 1Proof (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Θ) -
gram_identity_conjtheorem — The per-context Gram identity in the symmetric form BᵀB = Mᵀ B'ᵀ B' M (the conjugation form of gram_identity).hypothesesS S' :Solution d p KhM :S'.H = M * S.HhΘ :S.H.transpose * B.transpose * B * S.H = S'.H.transpose * B'.transpose * B' * S'.HconclusionB.transpose * B = M.transpose * (B'.transpose * B') * MProof (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] -
gram_diff_transporttheorem — (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.hypothesesProof (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] -
conj_vecMulVec_transposetheorem — Mᵀ (u vᵀ) M = (Mᵀ u)(Mᵀ v)ᵀ (conjugation of a rank-one matrix by Mᵀ · M).hypotheses -
central_rank2_eqtheorem — (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'ₖ)ᵀ.hypothesesS S' :Solution d p KhM :S'.H = M * S.HhΘ0 :S.Theta0 = S'.Theta0hΘ :∀ k, S.Theta k = S'.Theta kk :Fin KProof (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] -
transpose_mulVec_stdVectheorem — 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.hypothesesconclusionM.transpose *ᵥ stdVec n i = (fun j => M i j) -
B0_source_rowtheorem — (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ₖ}.hypothesesProof (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 -
permMat_mulVec_stdVectheorem — permMat σ *ᵥ eᵢ = e_{σ i}: the permutation matrix sends the i-th basis vector to the σ(i)-th. -
diag_permMat_mulVec_stdVectheorem — A signed permutation matrix sends eᵢ to the signed basis vector ν (σ i) • e_{σ i}.hypothesesProof (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] -
target_readofftheorem — 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ₖ).hypothesesS S' :Solution d p Kν :Fin d → ℝk :Fin Khk :S.Theta k ≠ S.Theta0hν :∀ i, ν i = 1 ∨ ν i = -1conclusionS'.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 -
sum_smul_vecMulVec_stdVec_eq_diagonaltheorem — ∑ n, (c n) • (e_n e_nᵀ) = diagonal c (the rank-one standard-basis outer products sum to a diagonal matrix).hypothesesn :ℕc :Fin n → ℝ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 -
sum_latent_diff_unprimedtheorem — (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})².hypothesesProof (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] -
orthogonal_of_diag_conjtheorem — (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.hypotheseshd :∀ i, 0 < dv ihd' :∀ i, 0 < dv' iProof (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' -
permMat_apply_symmtheorem — (permMat σ) a c = 1 iff c = σ.symm a, else 0 (column read-off of permMat).hypothesesconclusionpermMat σ a c = if c = σ.symm a then (1 : ℝ) else 0Proof (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]) -
permMat_conj_applytheorem — Permutation conjugation entry. (permMat σ * X * (permMat σ)ᵀ) a b = X (σ.symm a) (σ.symm b): conjugating X by permMat σ relabels rows and columns by σ.symm.hypothesesProof (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 -
exists_orderPermtheorem — (L4) Orthogonal correctness (full statement).hypothesesProof (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⟩