Discovery.Linear­Disentanglement

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

Quantitative 40 core · 33 supporting · 4 submodules Paper-independent finite-dimensional stability tools for approximate simultaneous congruence diagonalization: residuals, affine-minor separation, explicit operator-norm bounds, and compact exclusion radii. Collinear­Ambiguity 17 core · 22 supporting · 4 submodules Constructive non-identifiability witnesses for finite real simultaneous-congruence families when a selected two-coordinate diagonal-shift cloud is affine-collinear.
Model 7 core · 1 supporting Formalization of the setup of Squires, Seigal, Bhate & Uhler, *Linear Causal Disentanglement via Interventions* (ICML 2023), arXiv:2211.16467. ★ Solution★ InSG★ permMat_mul_transpose

Linear causal disentanglement: model layer

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

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

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

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

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

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

abbrev stdVec reviewed
Causalean.Discovery.LinearDisentanglement

For a nonnegative integer dimension and an index in that dimension, the standard basis vector is the real vector that equals one at the specified index and zero at every other index.

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

For a nonnegative integer dimension and a permutation of its indices, the permutation matrix is the real square matrix whose entry in row i and column j is one exactly when i is the image of j under the permutation, and is zero otherwise.

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

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

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

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts, the observational precision matrix is the p-by-p real matrix obtained by sandwiching the observational structural matrix's Gram matrix between the mixing pseudoinverse and its transpose.

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

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts and an intervention context, the interventional precision matrix is the p-by-p real matrix obtained by sandwiching that context's structural-matrix Gram matrix between the mixing pseudoinverse and its transpose.

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

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts and a permutation of the latent-node indices, the order-preserving relabeling condition holds exactly when every directed edge from a parent to a child remains ordered with the child's relabeled index strictly smaller than the parent's.

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

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

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

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

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

def pa reviewed
Causalean.Discovery.LinearDisentanglement.Solution

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts and a latent node, the parent set is the set of latent nodes having a directed edge into that node.

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

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts and a latent node, the inclusive parent set is the union of that node's parent set and the node itself.

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

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts and a latent node, the ancestor set is the set of latent nodes from which a nonempty directed path leads to that node.

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

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts and a latent node, the inclusive ancestor set is the union of that node's ancestor set and the node itself.

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

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts and two latent nodes, the strict causal precedence relation holds exactly when the second node is a strict ancestor of the first.

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

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts and two latent nodes, the weak causal precedence relation holds exactly when the nodes coincide or the second is a strict ancestor of the first.

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

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

Definition (Lean source)
d :
shared
p :
shared
K :
shared
S :
Solution d p K
R :
Matrix (Fin d) (Fin d) ℝ
Q :
Matrix (Fin d) (Fin p) ℝ
factor :
S.H = R * Q

H factors as R Q.

diag_nonneg :
∀ i, 0 ≤ R i i

R has non-negative diagonal.

supp :
∀ i j
if
¬ S.preceq i j
then
R i j = 0

R is supported on the partial order: Rᵢⱼ = 0 unless i ⪯ j.

unit :
∀ i, (Q i) ⬝ᵥ (Q i) = 1

Each row of Q has unit norm.

orth :
∀ i j
if
S.prec i j
then
(Q i) ⬝ᵥ (Q j) = 0

Rows of Q are orthogonal along the strict order.

Causalean.Discovery.LinearDisentanglement.IsPORQ · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:136 · uses Solution
def porqResidual reviewed
Causalean.Discovery.LinearDisentanglement

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts and a latent node, the partial-order RQ residual is that node's mixing-pseudoinverse row, viewed as a Euclidean vector, minus its orthogonal projection onto the span of residuals of all strict ancestors. The ancestor-residual span used in this subtraction is generated by the residuals of precisely those strict ancestors.

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

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts and a latent node, the strict-ancestor residual span is the real linear subspace generated by the partial-order RQ residuals of all strict ancestors of that node.

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

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

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

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

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

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

Flagship Theorem 2.

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

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

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

theorem disentanglement_identifiability reviewed
Causalean.Discovery.LinearDisentanglement

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

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

Linear causal disentanglement: the key rank-one identity

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

theorem key_identity reviewed
Causalean.Discovery.LinearDisentanglement

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

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

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

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

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

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

def rowSpan reviewed
Causalean.Discovery.LinearDisentanglement

For a nonnegative integer observed dimension and a real square matrix of that dimension, the row span is the real linear subspace generated by all rows of the matrix.

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

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts and a set of latent-node indices, the selected mixing-row span is the real linear subspace generated by the mixing-pseudoinverse rows indexed by that set.

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

For a nonnegative latent dimension, a nonnegative observed dimension, a real matrix with d rows and p columns, and a set of latent-row indices, the selected RQ-row span is the real linear subspace generated by the rows indexed by that set.

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

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

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

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

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

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

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

Linear causal disentanglement: solution orbits under order-preserving permutations

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

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

def permute reviewed
Causalean.Discovery.LinearDisentanglement.Solution

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts, a permutation of its latent-node indices, and the condition that this permutation preserves the directed node order, the permuted solution is a linear causal disentanglement solution obtained by relabeling every latent coordinate with that permutation. Its mixing pseudoinverse is the row-permuted original, its observational structural matrix is the original conjugated by the permutation matrix, its interventional structural matrices are conjugated in the same way, and its intervention targets are relabeled by the permutation.

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

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

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

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

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

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

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

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

def cvec reviewed
Causalean.Discovery.LinearDisentanglement

For a linear causal disentanglement solution with d latent variables, p observed variables, and K intervention contexts and an intervention context, the perfect-intervention perturbation row assigns to each latent node the intervention scaling times the standard-basis indicator of the target node, minus the corresponding entry of the observational structural matrix.

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

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

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