Discovery.Linear­Disentanglement

Identifiability of latent causal variables that follow a linear structural equation model and are observed only through an unknown full-rank linear mixing, from the precision matrices of one observational and several sin

Quantitative 20 core · 12 supporting · 3 submodules Perturbation bound for simultaneous diagonalization by congruence. Simultaneous­Congruence 37 core · 43 supporting · 8 submodules Two results on recovering a unit-diagonal matrix B that makes B·Aₑ·Bᵀ diagonal for every environment e.
Model 7 core · 1 supporting Formalization of the setup of Squires, Seigal, Bhate & Uhler, *Linear Causal Disentanglement via Interventions* (ICML 2023), arXiv:2211.16467. ★ Solution★ InSG★ permMat_mul_transpose

Linear causal disentanglement: model layer

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

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

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

We take an unnormalized algebraic relaxation of the model surrounding the paper's Theorem 2: the data is a tuple (H, {B_k}, {i_k}, {λ_k}), and uniqueness compares two such tuples that produce the same family {Θ_k}. The paper's row normalization (Assumption 1(c)), which removes scaling and sign ambiguity, is not encoded here. No Moore–Penrose pseudoinverse is needed: H enters only through LinearIndependent ℝ H (full row rank) and the formula for Θ_k.

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

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

abbrev stdVec reviewed
Causalean.Discovery.LinearDisentanglement

The standard basis vector isolates coordinate i in dimension d, the algebraic representation of a single-node intervention target.

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

The permutation matrix relabels latent coordinates according to permutation σ in dimension d, with one unit entry in each row and column.

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:64
structure Solution reviewed
Causalean.Discovery.LinearDisentanglement

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

Definition (Lean source)
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:69
def Theta0 reviewed
Causalean.Discovery.LinearDisentanglement.Solution

The observational precision matrix is the observable Gram form induced by the baseline structural matrix and mixing pseudoinverse of solution S, with latent dimension d, observed dimension p, and intervention count K.

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:114 · uses Solution
def Theta reviewed
Causalean.Discovery.LinearDisentanglement.Solution

The precision matrix in intervention context k is the observable Gram form generated by context k of solution S, with latent dimension d, observed dimension p, and intervention count K.

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:120 · uses Solution
def InSG reviewed
Causalean.Discovery.LinearDisentanglement.Solution

A latent relabeling preserves the causal order when permutation σ keeps every child below its parent in solution S, with latent dimension d, observed dimension p, and intervention count K.

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:126 · uses Solution
theorem permMat_mul_transpose reviewed
Causalean.Discovery.LinearDisentanglement

A permutation matrix is orthogonal, so relabeling by permutation σ in dimension d preserves the Gram geometry used by the observed precision matrices.

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:136 · 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 and Appendix D, Propositio ★ IsPORQ★ porq_exists★ porq_unique

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

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

def pa reviewed
Causalean.Discovery.LinearDisentanglement.Solution

The parent set collects the direct latent causes of node i in solution S, with latent dimension d, observed dimension p, and intervention count K.

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:38 · uses Solution
def Pa reviewed
Causalean.Discovery.LinearDisentanglement.Solution

The inclusive parent set adds node i itself to its direct causes in solution S, with latent dimension d, observed dimension p, and intervention count K.

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:43 · uses Solution
def anc reviewed
Causalean.Discovery.LinearDisentanglement.Solution

The ancestor set collects every latent node with a nonempty causal path to node i in solution S, with latent dimension d, observed dimension p, and intervention count K.

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:48 · uses Solution
def An reviewed
Causalean.Discovery.LinearDisentanglement.Solution

The inclusive ancestor set adds node i to every cause upstream of it in solution S, with latent dimension d, observed dimension p, and intervention count K.

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:53 · uses Solution
def prec reviewed
Causalean.Discovery.LinearDisentanglement.Solution

Strict causal precedence records that node j is a strict ancestor of node i in solution S, with latent dimension d, observed dimension p, and intervention count K.

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:58 · uses Solution
def preceq reviewed
Causalean.Discovery.LinearDisentanglement.Solution

Weak causal precedence allows node j to equal or strictly precede node i in solution S, with latent dimension d, observed dimension p, and intervention count K.

Definition (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:63 · 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:151 · uses Solution
def porqResidual reviewed
Causalean.Discovery.LinearDisentanglement

The partial-order RQ residual isolates the component of node i's mixing row orthogonal to all strict-ancestor residuals in solution S; that ancestor span is generated recursively. The construction uses latent dimension d, observed dimension p, and intervention count K.

Definition (Lean source)
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:227 · uses Solution
def porqAncSpan reviewed
Causalean.Discovery.LinearDisentanglement

The strict-ancestor residual span is the information already assigned to causes upstream of node i in solution S, with latent dimension d, observed dimension p, and intervention count K.

Definition (Lean source)
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:245 · uses Solution
theorem porq_exists reviewed
Causalean.Discovery.LinearDisentanglement

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

Formal statement
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:445 · uses IsPORQ , Solution
theorem porq_unique reviewed
Causalean.Discovery.LinearDisentanglement

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

Formal statement
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:514 · 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:68
  • 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:77
  • 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:90
  • 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 := Finset.sum_nonneg (fun j _ => mul_self_nonneg _) rcases lt_or_eq_of_le hnn with h | h · exact h · exfalso have hzero := (Finset.sum_eq_zero_iff_of_nonneg (fun j _ => mul_self_nonneg (x j))).mp h.symm i (Finset.mem_univ i) exact hi (by simpa using (mul_self_eq_zero).mp hzero)
    Causalean.Discovery.LinearDisentanglement.dotProduct_self_pos · Causalean/Discovery/LinearDisentanglement/PartialOrderRQ.lean:102
  • 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:115
  • 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:128
  • 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:169
  • 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:213
  • 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:253
  • 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:260
  • 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:267
  • 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:273
  • 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:283
  • 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:294
  • 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:324
  • 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:362
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:45 · 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:36
Rowspan 6 core · 0 supporting This file links the *observable* precision differences to the *latent* graph structure. ★ rowspan_inclusion_a★ rowspan_inclusion_b★ rowspan_inclusion_c_of_ancestor_closed

Linear causal disentanglement: rowspan inclusions and the corrected third clause

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

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

The printed clause quantifies over arbitrary 𝓘; the result formalized here deliberately does not claim that unrestricted statement.

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

def rowSpan reviewed
Causalean.Discovery.LinearDisentanglement

The row span captures every observable direction identified by the rows of matrix M in observed dimension p.

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:43
def hSpan reviewed
Causalean.Discovery.LinearDisentanglement

The selected mixing-row span is the observable subspace generated by latent index set I through solution S, with latent dimension d, observed dimension p, and intervention count K.

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:48 · uses Solution
def qSpan reviewed
Causalean.Discovery.LinearDisentanglement

The selected RQ-row span is the observable subspace carried by rows I of matrix Q, from latent dimension d into observed dimension p.

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:54
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:216 · 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:355 · uses IsPORQ , Solution , An , Theta , Theta0 , qSpan , rowSpan
theorem rowspan_inclusion_c_of_ancestor_closed reviewed
Causalean.Discovery.LinearDisentanglement

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

Formal statement
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_of_ancestor_closed (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`. -- 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_of_ancestor_closed · Causalean/Discovery/LinearDisentanglement/Rowspan.lean:533 · 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 Θₖ. It constructs pure permutation ambiguities only; the separate unnormalized uniqueness theorem permits additional signed diagonal scalings, whose converse construction is not proved here.

def permute reviewed
Causalean.Discovery.LinearDisentanglement.Solution

The permuted solution relabels every latent coordinate without changing the modeled system: permutation σ must preserve the directed order of solution S, with latent dimension d, observed dimension p, and intervention count K. Its mixing rows, structural matrices, and intervention targets are all transported consistently.

Definition (Lean source)
d :
ℕ
shared
p :
ℕ
shared
K :
ℕ
shared
S :
Solution d p K
σ :
Perm (Fin d)
hσ :
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:87 · uses Solution , InSG
theorem sigma_solutions reviewed
Causalean.Discovery.LinearDisentanglement

Order-preserving latent relabeling leaves every observable precision matrix unchanged, so solution S and its relabeling by permutation σ are observationally indistinguishable whenever σ preserves the causal order. The result covers latent dimension d, observed dimension p, and intervention count K.

Formal statement
d :
ℕ
shared
p :
ℕ
shared
K :
ℕ
shared
S :
Solution d p K
σ :
Perm (Fin d)
hσ :
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:147 · uses Solution , InSG , Theta , Theta0 , permute
Uniqueness_Part1 2 core · 36 supporting This first proof file develops the algebraic infrastructure for conditional unnormalized uniqueness. ★ central_rank2_eq

Linear causal disentanglement: conditional unnormalized uniqueness, part 1

This first proof file develops the algebraic infrastructure for conditional unnormalized uniqueness. It proves invertibility of the structural matrices and the latent Gram matrix, obtains the change of basis H' = M H, transports the observable precision equalities to latent Gram identities, and rewrites them as rank-two identities.

It also supplies the target read-off lemmas and the summed Gram-difference identity used by Uniqueness_Part2, which continues with diagonal conjugation and orthogonalization. The overall route and final theorem are summarized in Uniqueness.

def cvec reviewed
Causalean.Discovery.LinearDisentanglement

The perfect-intervention perturbation row isolates how context k replaces the target equation of solution S, with latent dimension d, observed dimension p, and intervention count K.

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_Part1.lean:255 · uses Solution
theorem central_rank2_eq reviewed
Causalean.Discovery.LinearDisentanglement

Equal observable precision families force the central rank-two latent identity, which equates intervention effects after change of basis. It compares solutions S,S' and their dimensions, latent transformation M, the loading relation, observational and interventional precision equality, and selected context k.

Formal statement
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
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)))
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_Part1.lean:384 · uses Solution , Theta , Theta0 , stdVec
36 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_Part1.lean:37
  • 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_Part1.lean:53
  • 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_Part1.lean:56
  • 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_Part1.lean:72
  • 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_Part1.lean:77
  • 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_Part1.lean:90
  • B0_invertible instance — The observational structural matrix is invertible, so baseline latent equations can be solved for every linear disentanglement solution and its latent, observed, and context dimensions; the certificate comes from its positive determinant.
    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_Part1.lean:95
  • Bint_invertible instance — Every interventional structural matrix is invertible, so the perturbed latent equations remain solvable in context k of any linear disentanglement solution and its latent, observed, and context dimensions; the certificate comes from its positive determinant.
    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_Part1.lean:103
  • 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_Part1.lean:115
  • 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_Part1.lean:120
  • HHt_invertible instance — The latent Gram matrix of the mixing pseudoinverse is invertible, allowing observed relations to be pulled back to latent space for any linear disentanglement solution and its dimensions; the certificate follows from positive definiteness.
    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_Part1.lean:125
  • B0H_gram_invertible instance — The observational structural-loading Gram matrix is invertible, enabling precision identities to recover latent transformations for any linear disentanglement solution and its dimensions; its certificate composes the component inverses.
    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_Part1.lean:135
  • 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_Part1.lean:146
  • 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_Part1.lean:151
  • 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_Part1.lean:164
  • 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_Part1.lean:176
  • 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_Part1.lean:216
  • 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) ℝ
    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)
    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_Part1.lean:232
  • 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_Part1.lean:261
  • 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_Part1.lean:267
  • 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_Part1.lean:280
  • 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_Part1.lean:296
  • 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)
    hΘ :
    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_Part1.lean:331
  • 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) ℝ
    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
    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_Part1.lean:348
  • 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
    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
    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_Part1.lean:358
  • 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_Part1.lean:378
  • 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_Part1.lean:410
  • 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_Part1.lean:418
  • 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_Part1.lean:444
  • 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_Part1.lean:452
  • diagonal_mul_apply theorem — Left multiplication by a diagonal matrix rescales rows.
    d :
    ℕ
    shared
    ν :
    Fin d → ℝ
    X :
    Matrix (Fin d) (Fin d) ℝ
    a b :
    Fin d
    (diagonal ν * X) a b = ν a * X a b
    Proof (Lean source)
    theorem diagonal_mul_apply (ν : Fin d → ℝ) (X : Matrix (Fin d) (Fin d) ℝ) (a b : Fin d) : (diagonal ν * X) a b = ν a * X a b := by rw [Matrix.mul_apply] rw [Finset.sum_eq_single a] · rw [Matrix.diagonal_apply_eq] · intro x _ hx; rw [Matrix.diagonal_apply_ne _ (Ne.symm hx), zero_mul] · intro h; exact absurd (Finset.mem_univ _) h
    Causalean.Discovery.LinearDisentanglement.diagonal_mul_apply · Causalean/Discovery/LinearDisentanglement/Uniqueness_Part1.lean:466
  • 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
    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)
    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_Part1.lean:476
  • 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_Part1.lean:520
  • 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_Part1.lean:534
  • sum_latent_diff_primed_reindexed theorem — (S1') The primed SUM TRICK with reindexed targets. If the primed targets along the same section kof are σ n, summing the primed latent key identities gives a diagonal whose m-th entry comes from the unique preimage σ.symm m.
    d :
    ℕ
    shared
    p :
    ℕ
    shared
    K :
    ℕ
    shared
    S' :
    Solution d p K
    kof :
    Fin d → Fin K
    σ :
    Perm (Fin d)
    hσtarget :
    ∀ n, S'.target (kof n) = σ n
    (∑ n, ((S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0))
    = diagonal (fun m => (S'.lam (kof (σ.symm m))) ^ 2) - S'.B0.transpose * S'.B0
    Proof (Lean source)
    theorem sum_latent_diff_primed_reindexed (S' : Solution d p K) (kof : Fin d → Fin K) (σ : Perm (Fin d)) (hσtarget : ∀ n, S'.target (kof n) = σ n) : (∑ n, ((S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0)) = diagonal (fun m => (S'.lam (kof (σ.symm m))) ^ 2) - S'.B0.transpose * S'.B0 := by have hterm : ∀ n, (S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0 = (S'.lam (kof n)) ^ 2 • vecMulVec (stdVec d (σ n)) (stdVec d (σ n)) - vecMulVec (S'.B0.transpose *ᵥ stdVec d (σ n)) (S'.B0.transpose *ᵥ stdVec d (σ n)) := by intro n rw [latent_key_identity S' (kof n), hσtarget n] rw [Finset.sum_congr rfl (fun n _ => hterm n)] have hdistrib : (∑ n, ((S'.lam (kof n)) ^ 2 • vecMulVec (stdVec d (σ n)) (stdVec d (σ n)) - vecMulVec (S'.B0.transpose *ᵥ stdVec d (σ n)) (S'.B0.transpose *ᵥ stdVec d (σ n)))) = (∑ n, (S'.lam (kof n)) ^ 2 • vecMulVec (stdVec d (σ n)) (stdVec d (σ n))) - ∑ n, vecMulVec (S'.B0.transpose *ᵥ stdVec d (σ n)) (S'.B0.transpose *ᵥ stdVec d (σ n)) := Finset.sum_sub_distrib .. rw [hdistrib] have hdiag : (∑ n, (S'.lam (kof n)) ^ 2 • vecMulVec (stdVec d (σ n)) (stdVec d (σ n))) = ∑ m, (S'.lam (kof (σ.symm m))) ^ 2 • vecMulVec (stdVec d m) (stdVec d m) := by let f : Fin d → Matrix (Fin d) (Fin d) ℝ := fun m => (S'.lam (kof (σ.symm m))) ^ 2 • vecMulVec (stdVec d m) (stdVec d m) simpa [f] using (Equiv.sum_comp σ f) have hgram : (∑ n, vecMulVec (S'.B0.transpose *ᵥ stdVec d (σ n)) (S'.B0.transpose *ᵥ stdVec d (σ n))) = ∑ m, vecMulVec (S'.B0.transpose *ᵥ stdVec d m) (S'.B0.transpose *ᵥ stdVec d m) := by let f : Fin d → Matrix (Fin d) (Fin d) ℝ := fun m => vecMulVec (S'.B0.transpose *ᵥ stdVec d m) (S'.B0.transpose *ᵥ stdVec d m) simpa [f] using (Equiv.sum_comp σ f) rw [hdiag, hgram, sum_smul_vecMulVec_stdVec_eq_diagonal, ← fact_transpose_mul S'.B0]
    Causalean.Discovery.LinearDisentanglement.sum_latent_diff_primed_reindexed · Causalean/Discovery/LinearDisentanglement/Uniqueness_Part1.lean:557
  • sum_gram_diff_transport theorem — Summing the per-context Gram-difference transport pulls the common conjugation Mᵀ · _ · M outside the finite sum.
    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
    hΘ :
    ∀ k, S.Theta k = S'.Theta k
    kof :
    Fin d → Fin K
    (∑ n, ((S.Bint (kof n)).transpose * S.Bint (kof n) - S.B0.transpose * S.B0))
    = M.transpose * (∑ n, ((S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0)) * M
    Proof (Lean source)
    theorem sum_gram_diff_transport (S S' : Solution d p K) {M : Matrix (Fin d) (Fin d) ℝ} (hM : S'.H = M * S.H) (hΘ0 : S.Theta0 = S'.Theta0) (hΘ : ∀ k, S.Theta k = S'.Theta k) (kof : Fin d → Fin K) : (∑ n, ((S.Bint (kof n)).transpose * S.Bint (kof n) - S.B0.transpose * S.B0)) = M.transpose * (∑ n, ((S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0)) * M := by calc (∑ n, ((S.Bint (kof n)).transpose * S.Bint (kof n) - S.B0.transpose * S.B0)) = ∑ n, M.transpose * (((S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0)) * M := by exact Finset.sum_congr rfl (fun n _ => gram_diff_transport S S' hM hΘ0 hΘ (kof n)) _ = M.transpose * (∑ n, ((S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0)) * M := by rw [Finset.mul_sum, Finset.sum_mul]
    Causalean.Discovery.LinearDisentanglement.sum_gram_diff_transport · Causalean/Discovery/LinearDisentanglement/Uniqueness_Part1.lean:601
Uniqueness_Part2 1 core · 8 supporting This second proof file turns the summed Gram-difference identity from Uniqueness_Part1 into a diagonal conjugation identity and then an orthogonal change of coordinates. ★ signed_cholesky

Linear causal disentanglement: conditional unnormalized uniqueness, part 2

This second proof file turns the summed Gram-difference identity from Uniqueness_Part1 into a diagonal conjugation identity and then an orthogonal change of coordinates. It provides the permutation-conjugation calculations and signed Cholesky lemmas needed to read monomial structure from that orthogonality.

Uniqueness_Part3 uses these tools in the model-specific geometric collapse and extracts the order-preserving permutation. The overall route and final theorem are summarized in Uniqueness.

theorem signed_cholesky reviewed
Causalean.Discovery.LinearDisentanglement

An upper-triangular Gram factor is unique up to row signs, making the remaining ambiguity explicit for matrices P,B in dimension q. This uses upper-triangularity of both factors, positive diagonal for the reference, and equality of their Gram matrices.

Formal statement
q :
ℕ
Matrix (Fin q) (Fin q) ℝ
hPu :
∀ i j
if
j < i
then
P i j = 0
hBu :
∀ i j
if
j < i
then
B i j = 0
hBp :
∀ i, 0 < B i i
hgram :
Pᵀ * P = Bᵀ * B
∃ s : Fin q → ℝ,
conclusion 1
i :
s i = 1 ∨ s i = -1
conclusion 2
i :
s i * B i i = P i i
conclusion 3
P = diagonal s * B
Proof (Lean source)
theorem signed_cholesky {q : ℕ} {P B : Matrix (Fin q) (Fin q) ℝ} (hPu : ∀ i j, j < i → P i j = 0) (hBu : ∀ i j, j < i → B i j = 0) (hBp : ∀ i, 0 < B i i) (hgram : Pᵀ * P = Bᵀ * B) : ∃ s : Fin q → ℝ, (∀ i, s i = 1 ∨ s i = -1) ∧ (∀ i, s i * B i i = P i i) ∧ P = diagonal s * B := by classical have hBu' : B.BlockTriangular id := hBu have hBdet : (0 : ℝ) < B.det := by rw [Matrix.det_of_upperTriangular hBu']; exact Finset.prod_pos (fun i _ => hBp i) haveI : Invertible B := B.invertibleOfIsUnitDet (isUnit_iff_ne_zero.mpr hBdet.ne') have hBinvU : B⁻¹.BlockTriangular id := Matrix.blockTriangular_inv_of_blockTriangular hBu' have hBinv_diag : ∀ i, B⁻¹ i i = (B i i)⁻¹ := by intro i have hmul : (B⁻¹ * B) i i = 1 := by rw [Matrix.inv_mul_of_invertible]; simp rw [Matrix.mul_apply, Finset.sum_eq_single i] at hmul · field_simp [(hBp i).ne'] at hmul ⊢; linarith [hmul] · intro k _ hki rcases lt_or_gt_of_ne hki with hk | hk · rw [hBinvU (by simpa using hk), zero_mul] · rw [hBu k i hk, mul_zero] · intro h; exact absurd (Finset.mem_univ i) h -- `W := P * B⁻¹` is orthogonal and upper-triangular. set W : Matrix (Fin q) (Fin q) ℝ := P * B⁻¹ with hW have hWupp : ∀ i j, j < i → W i j = 0 := (Matrix.BlockTriangular.mul (hPu) hBinvU) have hWortho : Wᵀ * W = 1 := by rw [hW, Matrix.transpose_mul, Matrix.mul_assoc, ← Matrix.mul_assoc Pᵀ P, hgram, Matrix.mul_assoc Bᵀ, Matrix.mul_inv_of_invertible, Matrix.mul_one, Matrix.transpose_nonsing_inv, Matrix.inv_mul_of_invertible] obtain ⟨hWoff, hWsign⟩ := orthogonal_upperTri_signed_diag hWortho hWupp -- The sign vector is the diagonal of `W`. refine ⟨fun i => W i i, hWsign, ?_, ?_⟩ · -- `W i i · B i i = P i i`. intro i change W i i * B i i = P i i have hWii : W i i = P i i * (B i i)⁻¹ := by rw [hW, Matrix.mul_apply, Finset.sum_eq_single i] · rw [hBinv_diag i] · intro k _ hki rcases lt_or_gt_of_ne hki with hk | hk · rw [hPu i k (by simpa using hk), zero_mul] · rw [hBinvU (by simpa using hk), mul_zero] · intro h; exact absurd (Finset.mem_univ i) h rw [hWii] field_simp [(hBp i).ne'] · -- `P = diagonal (W ·ᵢᵢ) * B`, since `W = diagonal` and `P = W B`. have hWdiagonal : W = diagonal (fun i => W i i) := by ext a b by_cases hab : a = b · subst hab; rw [Matrix.diagonal_apply_eq] · rw [Matrix.diagonal_apply_ne _ hab, hWoff a b hab] have hPWB : P = W * B := by rw [hW, Matrix.mul_assoc, Matrix.inv_mul_of_invertible, Matrix.mul_one] rw [hPWB] conv_lhs => rw [hWdiagonal]
Causalean.Discovery.LinearDisentanglement.signed_cholesky · Causalean/Discovery/LinearDisentanglement/Uniqueness_Part2.lean:266
8 supporting declarations (lemmas, instances)
  • diagonal_conj_from_sum_trick theorem — (S1+R1) The SUM TRICK after transport. The summed transport identity cancels the observational Gram term and leaves a diagonal conjugation.
    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
    hΘ :
    ∀ k, S.Theta k = S'.Theta k
    kof :
    Fin d → Fin K
    hkof :
    ∀ n, S.target (kof n) = n
    σ :
    Perm (Fin d)
    hσtarget :
    ∀ n, S'.target (kof n) = σ n
    M.transpose * diagonal (fun m => (S'.lam (kof (σ.symm m))) ^ 2) * M
    = diagonal (fun n => (S.lam (kof n)) ^ 2)
    Proof (Lean source)
    theorem diagonal_conj_from_sum_trick (S S' : Solution d p K) {M : Matrix (Fin d) (Fin d) ℝ} (hM : S'.H = M * S.H) (hΘ0 : S.Theta0 = S'.Theta0) (hΘ : ∀ k, S.Theta k = S'.Theta k) (kof : Fin d → Fin K) (hkof : ∀ n, S.target (kof n) = n) (σ : Perm (Fin d)) (hσtarget : ∀ n, S'.target (kof n) = σ n) : M.transpose * diagonal (fun m => (S'.lam (kof (σ.symm m))) ^ 2) * M = diagonal (fun n => (S.lam (kof n)) ^ 2) := by have hunprimed := sum_latent_diff_unprimed S kof hkof have hprimed := sum_latent_diff_primed_reindexed S' kof σ hσtarget have htransport := sum_gram_diff_transport S S' hM hΘ0 hΘ kof have h0 : S.B0.transpose * S.B0 = M.transpose * (S'.B0.transpose * S'.B0) * M := by refine gram_identity_conj S S' hM ?_ simp only [Solution.Theta0, Matrix.mul_assoc] at hΘ0 ⊢ exact hΘ0 have hsub : diagonal (fun n => (S.lam (kof n)) ^ 2) - S.B0.transpose * S.B0 = M.transpose * diagonal (fun m => (S'.lam (kof (σ.symm m))) ^ 2) * M - S.B0.transpose * S.B0 := by calc diagonal (fun n => (S.lam (kof n)) ^ 2) - S.B0.transpose * S.B0 = (∑ n, ((S.Bint (kof n)).transpose * S.Bint (kof n) - S.B0.transpose * S.B0)) := hunprimed.symm _ = M.transpose * (∑ n, ((S'.Bint (kof n)).transpose * S'.Bint (kof n) - S'.B0.transpose * S'.B0)) * M := htransport _ = M.transpose * (diagonal (fun m => (S'.lam (kof (σ.symm m))) ^ 2) - S'.B0.transpose * S'.B0) * M := by rw [hprimed] _ = M.transpose * diagonal (fun m => (S'.lam (kof (σ.symm m))) ^ 2) * M - S.B0.transpose * S.B0 := by rw [Matrix.mul_sub, Matrix.sub_mul, h0] exact (sub_left_inj.mp hsub).symm
    Causalean.Discovery.LinearDisentanglement.diagonal_conj_from_sum_trick · Causalean/Discovery/LinearDisentanglement/Uniqueness_Part2.lean:36
  • 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_Part2.lean:78
  • conj_readoff theorem — Signed conjugation read-off. Given M = diagonal μ permMat σ and B' M = diagonal ν permMat σ B, recover B' = diagonal ν permMat σ B (permMat σ)ᵀ diagonal μ⁻¹.
    d :
    ℕ
    Perm (Fin d)
    Fin d → ℝ
    shared
    hμ :
    ∀ i, μ i ≠ 0
    M B B' :
    Matrix (Fin d) (Fin d) ℝ
    hMeq :
    M = diagonal μ * permMat σ
    hrel :
    B' * M = diagonal ν * permMat σ * B
    B'
    = diagonal ν * permMat σ * B * (permMat σ).transpose * diagonal (fun i => (μ i)⁻¹)
    Proof (Lean source)
    theorem conj_readoff {σ : Perm (Fin d)} {μ ν : Fin d → ℝ} (hμ : ∀ i, μ i ≠ 0) {M B B' : Matrix (Fin d) (Fin d) ℝ} (hMeq : M = diagonal μ * permMat σ) (hrel : B' * M = diagonal ν * permMat σ * B) : B' = diagonal ν * permMat σ * B * (permMat σ).transpose * diagonal (fun i => (μ i)⁻¹) := by have hMinv : M * ((permMat σ).transpose * diagonal (fun i => (μ i)⁻¹)) = 1 := by rw [hMeq]; exact diagPerm_mul_inv σ hμ have hMU : M⁻¹ = (permMat σ).transpose * diagonal (fun i => (μ i)⁻¹) := Matrix.inv_eq_right_inv hMinv have hMMinv : M * M⁻¹ = 1 := by have hu : IsUnit M := IsUnit.of_mul_eq_one _ hMinv exact Matrix.mul_nonsing_inv M (by rwa [Matrix.isUnit_iff_isUnit_det] at hu) have hsplit : B' = (B' * M) * M⁻¹ := by rw [Matrix.mul_assoc, hMMinv, Matrix.mul_one] rw [hsplit, hrel, hMU] simp only [Matrix.mul_assoc]
    Causalean.Discovery.LinearDisentanglement.conj_readoff · Causalean/Discovery/LinearDisentanglement/Uniqueness_Part2.lean:148
  • 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_Part2.lean:168
  • 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_Part2.lean:177
  • permMat_conj_apply' theorem — Inverse-permutation conjugation entry. ((permMat σ)ᵀ * X * permMat σ) a b = X (σ a) (σ b): conjugating X by (permMat σ)ᵀ relabels rows and columns by σ.
    d :
    ℕ
    shared
    σ :
    Perm (Fin d)
    X :
    Matrix (Fin d) (Fin d) ℝ
    a b :
    Fin d
    ((permMat σ).transpose * X * permMat σ) a b = X (σ a) (σ b)
    Proof (Lean source)
    theorem permMat_conj_apply' (σ : Perm (Fin d)) (X : Matrix (Fin d) (Fin d) ℝ) (a b : Fin d) : ((permMat σ).transpose * X * permMat σ) a b = X (σ a) (σ b) := by rw [permMat_transpose_eq] have h2 : permMat σ = (permMat σ⁻¹).transpose := by rw [permMat_transpose_eq]; simp rw [h2, permMat_conj_apply σ⁻¹ X a b] rfl
    Causalean.Discovery.LinearDisentanglement.permMat_conj_apply' · Causalean/Discovery/LinearDisentanglement/Uniqueness_Part2.lean:206
  • permMat_mul_diagonal theorem — Diagonal-through-permutation. permMat σ * diagonal s = diagonal (s ∘ σ.symm) * permMat σ: moving a diagonal rescaling across a permutation matrix relabels its entries by σ.symm.
    d :
    ℕ
    shared
    σ :
    Perm (Fin d)
    s :
    Fin d → ℝ
    permMat σ * diagonal s = diagonal (fun i => s (σ.symm i)) * permMat σ
    Proof (Lean source)
    theorem permMat_mul_diagonal (σ : Perm (Fin d)) (s : Fin d → ℝ) : permMat σ * diagonal s = diagonal (fun i => s (σ.symm i)) * permMat σ := by ext a b rw [Matrix.mul_diagonal, Matrix.diagonal_mul, permMat_apply_symm] by_cases h : b = σ.symm a · subst h; rw [if_pos rfl]; simp · rw [if_neg h, mul_zero, zero_mul]
    Causalean.Discovery.LinearDisentanglement.permMat_mul_diagonal · Causalean/Discovery/LinearDisentanglement/Uniqueness_Part2.lean:218
  • orthogonal_upperTri_signed_diag theorem — Orthogonal upper-triangular ⟹ signed diagonal. An orthogonal (Wᵀ W = 1) upper-triangular matrix is diagonal with ±1 entries. This is the signed analogue of orthogonal_upperTri_pos_diag_eq_one (which assumed a positive diagonal and concluded W = 1): without the sign condition the diagonal entries are pinned only up to sign.
    q :
    ℕ
    Matrix (Fin q) (Fin q) ℝ
    hortho :
    Wᵀ * W = 1
    hupp :
    ∀ i j
    if
    j < i
    then
    W i j = 0
    conclusion 1
    i j :
    i ≠ j
    W i j = 0
    conclusion 2
    i :
    W i i = 1 ∨ W i i = -1
    Proof (Lean source)
    theorem orthogonal_upperTri_signed_diag {q : ℕ} {W : Matrix (Fin q) (Fin q) ℝ} (hortho : Wᵀ * W = 1) (hupp : ∀ i j, j < i → W i j = 0) : (∀ i j, i ≠ j → W i j = 0) ∧ (∀ i, W i i = 1 ∨ W i i = -1) := by classical haveI : Invertible W := invertibleOfLeftInverse _ _ hortho have hinv : W⁻¹ = Wᵀ := Matrix.inv_eq_left_inv hortho have hWupp : W.BlockTriangular id := hupp have hWTupp : (Wᵀ).BlockTriangular id := hinv ▸ Matrix.blockTriangular_inv_of_blockTriangular hWupp have hWTlow : (Wᵀ).BlockTriangular toDual := hWupp.transpose -- `Wᵀ` is both upper- and lower-triangular, hence diagonal. have hWTdiag : ∀ i j, i ≠ j → (Wᵀ) i j = 0 := by intro i j hij rcases lt_or_gt_of_ne hij with h | h · exact hWTlow (by simpa using h) · exact hWTupp (by simpa using h) -- Off-diagonal entries of `W` vanish (transpose of `Wᵀ`'s vanishing entries). have hWdiag : ∀ i j, i ≠ j → W i j = 0 := by intro i j hij have : (Wᵀ) j i = 0 := hWTdiag j i (Ne.symm hij) rwa [Matrix.transpose_apply] at this refine ⟨hWdiag, ?_⟩ -- Diagonal entries square to one. intro i have hcol := congrFun (congrFun hortho i) i rw [Matrix.one_apply_eq, Matrix.mul_apply] at hcol simp only [Matrix.transpose_apply] at hcol rw [Finset.sum_eq_single i] at hcol · rw [← sq] at hcol; exact sq_eq_one_iff.mp hcol · intro l _ hli have : W l i = 0 := hWdiag l i hli rw [this, mul_zero] · intro h; exact absurd (Finset.mem_univ _) h
    Causalean.Discovery.LinearDisentanglement.orthogonal_upperTri_signed_diag · Causalean/Discovery/LinearDisentanglement/Uniqueness_Part2.lean:228
Uniqueness_Part3 1 core · 0 supporting This third proof file contains the model-specific geometric core. ★ exists_orderPerm

Linear causal disentanglement: conditional unnormalized uniqueness, part 3

This third proof file contains the model-specific geometric core. Starting from the orthogonalized change of basis supplied by Uniqueness_Part2, it proves that the change of basis is monomial, pins its permutation to the graph order, and reads off the transformed structural matrices and intervention targets.

Its public result exists_orderPerm packages that collapse. Uniqueness_Part4 substitutes it into the recovered change of basis to state the final conditional uniqueness theorem. The overall route is summarized in Uniqueness.

theorem exists_orderPerm reviewed
Causalean.Discovery.LinearDisentanglement

Two observationally equivalent solutions differ only by an order-preserving relabeling, nonzero coordinate scales, and row signs, giving the full recoverable ambiguity. For solutions S,S' and their dimensions, this requires bijective targeting in both solutions, nondegenerate interventions, equal observational and interventional precision families, and a latent transformation M satisfying the loading relation.

Formal statement
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
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 σ
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_Part3.lean:576 · uses Solution , InSG , Theta , Theta0 , permMat
Uniqueness_Part4 1 core · 0 supporting This final proof file packages the geometric collapse from Uniqueness_Part3 as conditional unnormalized uniqueness up to an order-preserving relabeling and signed diagonal scaling. ★ disentanglement_uniqueness_up_to_signed_scaling_of_nondegenerate

Linear causal disentanglement: conditional unnormalized uniqueness, part 4

This final proof file packages the geometric collapse from Uniqueness_Part3 as conditional unnormalized uniqueness up to an order-preserving relabeling and signed diagonal scaling. It contains only the public theorem that substitutes the monomial change of basis into the latent-direction and structural-matrix relations.

The result is weaker than the normalized permutation-only conclusion of the paper's Theorem 2. The complete route is summarized in Uniqueness.

theorem disentanglement_uniqueness_up_to_signed_scaling_of_nondegenerate reviewed
Causalean.Discovery.LinearDisentanglement

Conditional unnormalized uniqueness up to signed scaling. Let two solutions S and S' of the linear causal disentanglement model have intervention-target maps that are bijections onto the latent coordinates, i.e. one intervention per latent node and suppose S's interventions are non-degenerate: every intervened precision matrix Θ_k differs from the observational precision matrix Θ_0. If S and S' share the same observational precision matrix and agree, context by context, on every interventional precision matrix, then S and S' are related by a single order-preserving relabeling σ of the latent coordinates, a nonzero scaling vector μ, and a ±1 sign vector ν, transporting S's latent-direction and structural coefficient matrices onto S''s, with σ carrying S's intervention targets onto S''s.

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
hΘ :
∀ 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_up_to_signed_scaling_of_nondegenerate (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_up_to_signed_scaling_of_nondegenerate · Causalean/Discovery/LinearDisentanglement/Uniqueness_Part4.lean:37 · uses Solution , InSG , Theta , Theta0 , permMat