Discovery.LinearDisentanglement
Identifiability of latent causal variables that follow a linear structural equation model and are observed only through an unknown full-rank linear mixing, from the precision matrices of one observational and several sin
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 an unnormalized algebraic relaxation of the model surrounding the
paper's Theorem 2: the data is a tuple (H, {B_k}, {i_k}, {λ_k}), and uniqueness
compares two such tuples that produce the same family {Θ_k}. The paper's row
normalization (Assumption 1(c)), which removes scaling and sign ambiguity, is not
encoded here. 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.
The standard basis vector isolates coordinate i in dimension d, the algebraic representation of a single-node intervention target.
The permutation matrix relabels latent coordinates according to permutation σ in dimension d, with one unit entry in each row and column.
An unnormalized algebraic relaxation of a linear causal disentanglement model with d latent variables, p observed variables, and K interventional contexts 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 is the observable Gram form induced by the baseline structural matrix and mixing pseudoinverse of solution S, with latent dimension d, observed dimension p, and intervention count K.
The precision matrix in intervention context k is the observable Gram form generated by context k of solution S, with latent dimension d, observed dimension p, and intervention count K.
A latent relabeling preserves the causal order when permutation σ keeps every child below its parent in solution S, with latent dimension d, observed dimension p, and intervention count K.
A permutation matrix is orthogonal, so relabeling by permutation σ in dimension d preserves the Gram geometry used by the observed precision matrices.
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 and Appendix D, Propositio ★ 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 and Appendix D, Proposition 7): 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 (Appendix D,
Proposition 7), proved there by the Gram–Schmidt-style construction in
Algorithm "Partial Order RQ Decomposition".
The parent set collects the direct latent causes of node i in solution S, with latent dimension d, observed dimension p, and intervention count K.
The inclusive parent set adds node i itself to its direct causes in solution S, with latent dimension d, observed dimension p, and intervention count K.
The ancestor set collects every latent node with a nonempty causal path to node i in solution S, with latent dimension d, observed dimension p, and intervention count K.
The inclusive ancestor set adds node i to every cause upstream of it in solution S, with latent dimension d, observed dimension p, and intervention count K.
Strict causal precedence records that node j is a strict ancestor of node i in solution S, with latent dimension d, observed dimension p, and intervention count K.
Weak causal precedence allows node j to equal or strictly precede node i in solution S, with latent dimension d, observed dimension p, and intervention count K.
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)
H factors as R Q.
R has non-negative diagonal.
R is supported on the partial order: Rᵢⱼ = 0 unless i ⪯ j.
Each row of Q has unit norm.
Rows of Q are orthogonal along the strict order.
The partial-order RQ residual isolates the component of node i's mixing row orthogonal to all strict-ancestor residuals in solution S; that ancestor span is generated recursively. The construction uses latent dimension d, observed dimension p, and intervention count K.
Definition (Lean source)
The strict-ancestor residual span is the information already assigned to causes upstream of node i in solution S, with latent dimension d, observed dimension p, and intervention count K.
Definition (Lean source)
Existence of the partial order RQ decomposition (Appendix D, Proposition 7). 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 D, Proposition 7). 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.hypothesesconclusioni < j -
not_prec_selftheorem — ≺ is irreflexive: a node is never its own strict ancestor.hypothesesconclusion¬ S.prec i iProof (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 := Finset.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.hypothesesconclusionR = 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 -
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.hypothesesconclusion0 < R i iProof (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ᵢ)ᗮ.hypothesesconclusionporqResidual 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 _ -
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ᵢ)ᗮ.hypothesesconclusion(inner ℝ (porqResidual S i) (porqResidual S k) : ℝ) = 0Proof (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).hypothesesconclusionProof (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).hypothesesconclusionporqResidual S i ≠ 0Proof (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 → ℝ,conclusion 1j :¬ S.preceq i jc j = 0conclusion 2c i = ‖porqResidual S i‖conclusion 3= ∑ 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]
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 This file links the *observable* precision differences to the *latent* graph structure. ★ rowspan_inclusion_a★ rowspan_inclusion_b★ rowspan_inclusion_c_of_ancestor_closed
Linear causal disentanglement: rowspan inclusions and the corrected third clause
This file links the observable precision differences to the latent graph
structure. 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ₖ)⟩;
* an ancestor-closed correction to the paper's printed Lemma 1(c): if the intervention
changes the precision matrix, 𝓘 is ancestor-closed, and Pa(iₖ) ⊄ 𝓘, then
rowspan(Θₖ − Θ₀) ⊄ ⟨qᵢ : i ∈ 𝓘⟩.
The printed clause quantifies over arbitrary 𝓘; the result formalized here deliberately
does not claim that unrestricted statement.
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 captures every observable direction identified by the rows of matrix M in observed dimension p.
The selected mixing-row span is the observable subspace generated by latent index set I through solution S, with latent dimension d, observed dimension p, and intervention count K.
The selected RQ-row span is the observable subspace carried by rows I of matrix Q, from latent dimension d into observed dimension p.
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)
Ancestor-closed correction to the paper's 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 Θₖ. It constructs pure permutation
ambiguities only; the separate unnormalized uniqueness theorem permits additional
signed diagonal scalings, whose converse construction is not proved here.
The permuted solution relabels every latent coordinate without changing the modeled system: permutation σ must preserve the directed order of solution S, with latent dimension d, observed dimension p, and intervention count K. Its mixing rows, structural matrices, and intervention targets are all transported consistently.
Definition (Lean source)
Order-preserving latent relabeling leaves every observable precision matrix unchanged, so solution S and its relabeling by permutation σ are observationally indistinguishable whenever σ preserves the causal order. The result covers latent dimension d, observed dimension p, and intervention count K.
Formal statement
Proof (Lean source)
Uniqueness_Part1 2 core · 36 supporting This first proof file develops the algebraic infrastructure for conditional unnormalized uniqueness. ★ central_rank2_eq
Linear causal disentanglement: conditional unnormalized uniqueness, part 1
This first proof file develops the algebraic infrastructure for conditional
unnormalized uniqueness. It proves invertibility of the structural matrices and
the latent Gram matrix, obtains the change of basis H' = M H, transports the
observable precision equalities to latent Gram identities, and rewrites them as
rank-two identities.
It also supplies the target read-off lemmas and the summed Gram-difference identity
used by Uniqueness_Part2, which continues with diagonal conjugation and
orthogonalization. The overall route and final theorem are summarized in
Uniqueness.
The perfect-intervention perturbation row isolates how context k replaces the target equation of solution S, with latent dimension d, observed dimension p, and intervention count K.
Equal observable precision families force the central rank-two latent identity, which equates intervention effects after change of basis. It compares solutions S,S' and their dimensions, latent transformation M, the loading relation, observational and interventional precision equality, and selected context k.
Formal statement
Proof (Lean source)
36 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.hypothesesconclusionS.B0.BlockTriangular idProof (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).hypothesesconclusion(S.Bint k).BlockTriangular idProof (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.hypothesesconclusion0 < S.Bint k i iProof (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.hypothesesconclusion0 < (S.Bint k).detProof (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 — The observational structural matrix is invertible, so baseline latent equations can be solved for every linear disentanglement solution and its latent, observed, and context dimensions; the certificate comes from its positive determinant. -
Bint_invertibleinstance — Every interventional structural matrix is invertible, so the perturbed latent equations remain solvable in context k of any linear disentanglement solution and its latent, observed, and context dimensions; the certificate comes from its positive determinant.parametersinstancegiven by(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).hypothesesProof (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.hypothesesconclusion(S.H * S.H.transpose).PosDefProof (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 — The latent Gram matrix of the mixing pseudoinverse is invertible, allowing observed relations to be pulled back to latent space for any linear disentanglement solution and its dimensions; the certificate follows from positive definiteness.parametersinstancegiven by(S.H * S.H.transpose).invertibleOfIsUnitDet ((Matrix.isUnit_iff_isUnit_det _).mp (HHt_posDef S).isUnit) -
B0H_gram_invertibleinstance — The observational structural-loading Gram matrix is invertible, enabling precision identities to recover latent transformations for any linear disentanglement solution and its dimensions; its certificate composes the component inverses.parametersinstancegiven byby 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.hypothesesconclusionS.H * (S.H.transpose * (S.H * S.H.transpose)⁻¹) = 1Proof (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.hypothesesconclusion((S.H * S.H.transpose)⁻¹ * S.H) * S.H.transpose = 1Proof (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.hypothesesconclusion(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'.hypothesesProof (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.hypothesesd :sharedℕp :sharedℕK :sharedℕS :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.hypothesesd :sharedℕp :sharedℕK :sharedℕS 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 Θₖ = Θ₀.hypothesesconclusioncvec S k ≠ 0Proof (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.hypothesesd :sharedℕp :sharedℕK :sharedℕS 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).hypothesesd :sharedℕp :sharedℕK :sharedℕS 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 -
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.hypotheses -
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] -
diagonal_mul_applytheorem — Left multiplication by a diagonal matrix rescales rows.hypothesesconclusion(diagonal ν * X) a b = ν a * X a bProof (Lean source)
theorem diagonal_mul_apply (ν : Fin d → ℝ) (X : Matrix (Fin d) (Fin d) ℝ) (a b : Fin d) : (diagonal ν * X) a b = ν a * X a b := by rw [Matrix.mul_apply] rw [Finset.sum_eq_single a] · rw [Matrix.diagonal_apply_eq] · intro x _ hx; rw [Matrix.diagonal_apply_ne _ (Ne.symm hx), zero_mul] · intro h; exact absurd (Finset.mem_univ _) h -
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ₖ).hypothesesd :sharedℕp :sharedℕK :sharedℕS 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] -
sum_latent_diff_primed_reindexedtheorem — (S1') The primed SUM TRICK with reindexed targets. If the primed targets along the same section kof are σ n, summing the primed latent key identities gives a diagonal whose m-th entry comes from the unique preimage σ.symm m.hypothesesProof (Lean source)
theorem sum_latent_diff_primed_reindexed (S' : Solution d p K) (kof : Fin d → Fin K) (σ : Perm (Fin d)) (hσtarget : ∀ n, S'.target (kof n) = σ n) : (∑ n, ((S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0)) = diagonal (fun m => (S'.lam (kof (σ.symm m))) ^ 2) - S'.B0.transpose * S'.B0 := by 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), hσtarget 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] have hdiag : (∑ n, (S'.lam (kof n)) ^ 2 • vecMulVec (stdVec d (σ n)) (stdVec d (σ n))) = ∑ m, (S'.lam (kof (σ.symm m))) ^ 2 • vecMulVec (stdVec d m) (stdVec d m) := by let f : Fin d → Matrix (Fin d) (Fin d) ℝ := fun m => (S'.lam (kof (σ.symm m))) ^ 2 • vecMulVec (stdVec d m) (stdVec d m) simpa [f] using (Equiv.sum_comp σ f) have hgram : (∑ n, vecMulVec (S'.B0.transpose *ᵥ stdVec d (σ n)) (S'.B0.transpose *ᵥ stdVec d (σ n))) = ∑ m, vecMulVec (S'.B0.transpose *ᵥ stdVec d m) (S'.B0.transpose *ᵥ stdVec d m) := by let f : Fin d → Matrix (Fin d) (Fin d) ℝ := fun m => vecMulVec (S'.B0.transpose *ᵥ stdVec d m) (S'.B0.transpose *ᵥ stdVec d m) simpa [f] using (Equiv.sum_comp σ f) rw [hdiag, hgram, sum_smul_vecMulVec_stdVec_eq_diagonal, ← fact_transpose_mul S'.B0] -
sum_gram_diff_transporttheorem — Summing the per-context Gram-difference transport pulls the common conjugation Mᵀ · _ · M outside the finite sum.hypothesesd :sharedℕp :sharedℕK :sharedℕS S' :Solution d p KhM :S'.H = M * S.HhΘ0 :S.Theta0 = S'.Theta0hΘ :∀ k, S.Theta k = S'.Theta kProof (Lean source)
theorem sum_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) (kof : Fin d → Fin K) : (∑ n, ((S.Bint (kof n)).transpose * S.Bint (kof n) - S.B0.transpose * S.B0)) = M.transpose * (∑ n, ((S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0)) * M := by calc (∑ n, ((S.Bint (kof n)).transpose * S.Bint (kof n) - S.B0.transpose * S.B0)) = ∑ n, M.transpose * (((S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0)) * M := by exact Finset.sum_congr rfl (fun n _ => gram_diff_transport S S' hM hΘ0 hΘ (kof n)) _ = M.transpose * (∑ n, ((S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0)) * M := by rw [Finset.mul_sum, Finset.sum_mul]
Uniqueness_Part2 1 core · 8 supporting This second proof file turns the summed Gram-difference identity from Uniqueness_Part1 into a diagonal conjugation identity and then an orthogonal change of coordinates. ★ signed_cholesky
Linear causal disentanglement: conditional unnormalized uniqueness, part 2
This second proof file turns the summed Gram-difference identity from
Uniqueness_Part1 into a diagonal conjugation identity and then an orthogonal
change of coordinates. It provides the permutation-conjugation calculations and
signed Cholesky lemmas needed to read monomial structure from that orthogonality.
Uniqueness_Part3 uses these tools in the model-specific geometric collapse and
extracts the order-preserving permutation. The overall route and final theorem are
summarized in Uniqueness.
An upper-triangular Gram factor is unique up to row signs, making the remaining ambiguity explicit for matrices P,B in dimension q. This uses upper-triangularity of both factors, positive diagonal for the reference, and equality of their Gram matrices.
Formal statement
Proof (Lean source)
8 supporting declarations (lemmas, instances)
-
diagonal_conj_from_sum_tricktheorem — (S1+R1) The SUM TRICK after transport. The summed transport identity cancels the observational Gram term and leaves a diagonal conjugation.hypothesesd :sharedℕp :sharedℕK :sharedℕS S' :Solution d p KhM :S'.H = M * S.HhΘ0 :S.Theta0 = S'.Theta0hΘ :∀ k, S.Theta k = S'.Theta khkof :∀ n, S.target (kof n) = nhσtarget :∀ n, S'.target (kof n) = σ nProof (Lean source)
theorem diagonal_conj_from_sum_trick (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) (kof : Fin d → Fin K) (hkof : ∀ n, S.target (kof n) = n) (σ : Perm (Fin d)) (hσtarget : ∀ n, S'.target (kof n) = σ n) : M.transpose * diagonal (fun m => (S'.lam (kof (σ.symm m))) ^ 2) * M = diagonal (fun n => (S.lam (kof n)) ^ 2) := by have hunprimed := sum_latent_diff_unprimed S kof hkof have hprimed := sum_latent_diff_primed_reindexed S' kof σ hσtarget have htransport := sum_gram_diff_transport S S' hM hΘ0 hΘ kof 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 have hsub : diagonal (fun n => (S.lam (kof n)) ^ 2) - S.B0.transpose * S.B0 = M.transpose * diagonal (fun m => (S'.lam (kof (σ.symm m))) ^ 2) * M - S.B0.transpose * S.B0 := by calc diagonal (fun n => (S.lam (kof n)) ^ 2) - S.B0.transpose * S.B0 = (∑ n, ((S.Bint (kof n)).transpose * S.Bint (kof n) - S.B0.transpose * S.B0)) := hunprimed.symm _ = M.transpose * (∑ n, ((S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0)) * M := htransport _ = M.transpose * (diagonal (fun m => (S'.lam (kof (σ.symm m))) ^ 2) - S'.B0.transpose * S'.B0) * M := by rw [hprimed] _ = M.transpose * diagonal (fun m => (S'.lam (kof (σ.symm m))) ^ 2) * M - S.B0.transpose * S.B0 := by rw [Matrix.mul_sub, Matrix.sub_mul, h0] exact (sub_left_inj.mp hsub).symm -
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' -
conj_readofftheorem — Signed conjugation read-off. Given M = diagonal μ permMat σ and B' M = diagonal ν permMat σ B, recover B' = diagonal ν permMat σ B (permMat σ)ᵀ diagonal μ⁻¹.hypotheseshμ :∀ i, μ i ≠ 0Proof (Lean source)
theorem conj_readoff {σ : Perm (Fin d)} {μ ν : Fin d → ℝ} (hμ : ∀ i, μ i ≠ 0) {M B B' : Matrix (Fin d) (Fin d) ℝ} (hMeq : M = diagonal μ * permMat σ) (hrel : B' * M = diagonal ν * permMat σ * B) : B' = diagonal ν * permMat σ * B * (permMat σ).transpose * diagonal (fun i => (μ i)⁻¹) := by have hMinv : M * ((permMat σ).transpose * diagonal (fun i => (μ i)⁻¹)) = 1 := by rw [hMeq]; exact diagPerm_mul_inv σ hμ have hMU : M⁻¹ = (permMat σ).transpose * diagonal (fun i => (μ i)⁻¹) := Matrix.inv_eq_right_inv hMinv have hMMinv : M * M⁻¹ = 1 := by have hu : IsUnit M := IsUnit.of_mul_eq_one _ hMinv exact Matrix.mul_nonsing_inv M (by rwa [Matrix.isUnit_iff_isUnit_det] at hu) have hsplit : B' = (B' * M) * M⁻¹ := by rw [Matrix.mul_assoc, hMMinv, Matrix.mul_one] rw [hsplit, hrel, hMU] simp only [Matrix.mul_assoc] -
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 -
permMat_conj_apply'theorem — Inverse-permutation conjugation entry. ((permMat σ)ᵀ * X * permMat σ) a b = X (σ a) (σ b): conjugating X by (permMat σ)ᵀ relabels rows and columns by σ.hypothesesProof (Lean source)
theorem permMat_conj_apply' (σ : Perm (Fin d)) (X : Matrix (Fin d) (Fin d) ℝ) (a b : Fin d) : ((permMat σ).transpose * X * permMat σ) a b = X (σ a) (σ b) := by rw [permMat_transpose_eq] have h2 : permMat σ = (permMat σ⁻¹).transpose := by rw [permMat_transpose_eq]; simp rw [h2, permMat_conj_apply σ⁻¹ X a b] rfl -
permMat_mul_diagonaltheorem — Diagonal-through-permutation. permMat σ * diagonal s = diagonal (s ∘ σ.symm) * permMat σ: moving a diagonal rescaling across a permutation matrix relabels its entries by σ.symm.hypothesesProof (Lean source)
theorem permMat_mul_diagonal (σ : Perm (Fin d)) (s : Fin d → ℝ) : permMat σ * diagonal s = diagonal (fun i => s (σ.symm i)) * permMat σ := by ext a b rw [Matrix.mul_diagonal, Matrix.diagonal_mul, permMat_apply_symm] by_cases h : b = σ.symm a · subst h; rw [if_pos rfl]; simp · rw [if_neg h, mul_zero, zero_mul] -
orthogonal_upperTri_signed_diagtheorem — Orthogonal upper-triangular ⟹ signed diagonal. An orthogonal (Wᵀ W = 1) upper-triangular matrix is diagonal with ±1 entries. This is the signed analogue of orthogonal_upperTri_pos_diag_eq_one (which assumed a positive diagonal and concluded W = 1): without the sign condition the diagonal entries are pinned only up to sign.hypothesesconclusionconclusion 1i j :i ≠ jW i j = 0conclusion 2i :W i i = 1 ∨ W i i = -1Proof (Lean source)
theorem orthogonal_upperTri_signed_diag {q : ℕ} {W : Matrix (Fin q) (Fin q) ℝ} (hortho : Wᵀ * W = 1) (hupp : ∀ i j, j < i → W i j = 0) : (∀ i j, i ≠ j → W i j = 0) ∧ (∀ i, W i i = 1 ∨ W i i = -1) := by classical haveI : Invertible W := invertibleOfLeftInverse _ _ hortho have hinv : W⁻¹ = Wᵀ := Matrix.inv_eq_left_inv hortho have hWupp : W.BlockTriangular id := hupp have hWTupp : (Wᵀ).BlockTriangular id := hinv ▸ Matrix.blockTriangular_inv_of_blockTriangular hWupp have hWTlow : (Wᵀ).BlockTriangular toDual := hWupp.transpose -- `Wᵀ` is both upper- and lower-triangular, hence diagonal. have hWTdiag : ∀ i j, i ≠ j → (Wᵀ) i j = 0 := by intro i j hij rcases lt_or_gt_of_ne hij with h | h · exact hWTlow (by simpa using h) · exact hWTupp (by simpa using h) -- Off-diagonal entries of `W` vanish (transpose of `Wᵀ`'s vanishing entries). have hWdiag : ∀ i j, i ≠ j → W i j = 0 := by intro i j hij have : (Wᵀ) j i = 0 := hWTdiag j i (Ne.symm hij) rwa [Matrix.transpose_apply] at this refine ⟨hWdiag, ?_⟩ -- Diagonal entries square to one. intro i have hcol := congrFun (congrFun hortho i) i rw [Matrix.one_apply_eq, Matrix.mul_apply] at hcol simp only [Matrix.transpose_apply] at hcol rw [Finset.sum_eq_single i] at hcol · rw [← sq] at hcol; exact sq_eq_one_iff.mp hcol · intro l _ hli have : W l i = 0 := hWdiag l i hli rw [this, mul_zero] · intro h; exact absurd (Finset.mem_univ _) h
Uniqueness_Part3 1 core · 0 supporting This third proof file contains the model-specific geometric core. ★ exists_orderPerm
Linear causal disentanglement: conditional unnormalized uniqueness, part 3
This third proof file contains the model-specific geometric core. Starting from
the orthogonalized change of basis supplied by Uniqueness_Part2, it proves that
the change of basis is monomial, pins its permutation to the graph order, and
reads off the transformed structural matrices and intervention targets.
Its public result exists_orderPerm packages that collapse. Uniqueness_Part4
substitutes it into the recovered change of basis to state the final conditional
uniqueness theorem. The overall route is summarized in Uniqueness.
Two observationally equivalent solutions differ only by an order-preserving relabeling, nonzero coordinate scales, and row signs, giving the full recoverable ambiguity. For solutions S,S' and their dimensions, this requires bijective targeting in both solutions, nondegenerate interventions, equal observational and interventional precision families, and a latent transformation M satisfying the loading relation.
Formal statement
Proof (Lean source)
Uniqueness_Part4 1 core · 0 supporting This final proof file packages the geometric collapse from Uniqueness_Part3 as conditional unnormalized uniqueness up to an order-preserving relabeling and signed diagonal scaling. ★ disentanglement_uniqueness_up_to_signed_scaling_of_nondegenerate
Linear causal disentanglement: conditional unnormalized uniqueness, part 4
This final proof file packages the geometric collapse from Uniqueness_Part3 as
conditional unnormalized uniqueness up to an order-preserving relabeling and
signed diagonal scaling. It contains only the public theorem that substitutes the
monomial change of basis into the latent-direction and structural-matrix relations.
The result is weaker than the normalized permutation-only conclusion of the
paper's Theorem 2. The complete route is summarized in Uniqueness.
Conditional unnormalized uniqueness up to signed scaling. Let two solutions S and S' of the linear causal disentanglement model have intervention-target maps that are bijections onto the latent coordinates, i.e. one intervention per latent node and suppose S's interventions are non-degenerate: 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.