1-- The determinant annihilates every vector in the left kernel (ZF).2-- Part of Cumulus, the Grace17 mathematics library.3-- Copyright (c) 2026 Grant Passmore.4-- Released under the Apache License 2.0. See LICENSE in the repository root.5-- Authors: Grant Passmore67module Grace.Cumulus.Algebra.Ring.DeterminantKernel8import Adjugate910defgraph rowVectorWeights (I A add zero neg mul one v M j : Set) (i ∈ I) : A :=11 call2(mul, v(i), M[i, j])12requiresring : IsCommRing(A, add, zero, neg, mul, one)13requires vector : v : I → A14requires matrix : RingMatrix(I, A, M)15requires column : j ∈ I16proof17have value := FunctionBetween.apply_closed at v, I, A, i with*18have entry := RingMatrix.entry at I, A, M, i, j with*19have ordinary := IsCommRing.ring withring20exact IsRing.mul_closed with ordinary, value, entry2122notationscoped rowVectorWeights(I, R, v, M, j) => rowVectorWeights(I, R.carrier, R.add, R.zero, R.neg, R.mul, R.one, v, M, j)2324def rowVectorEntry (I A add zero neg mul one v M j : Set) : Set:=25 finiteRingSum(I, A, add, zero, neg, mul, one, rowVectorWeights(I, A, add, zero, neg, mul, one, v, M, j))2627notationscoped rowVectorEntry(I, R, v, M, j) => rowVectorEntry(I, R.carrier, R.add, R.zero, R.neg, R.mul, R.one, v, M, j)2829predicate LeftKernelVector (I A add zero neg mul one M v : Set) :=30 FunctionBetween(v, I, A) ∧∀ j ∈ I, rowVectorEntry(I, A, add, zero, neg, mul, one, v, M, j) = zero3132notationscoped LeftKernelVector(I, R, M, v) => LeftKernelVector(I, R.carrier, R.add, R.zero, R.neg, R.mul, R.one, M, v)3334-- This finite double sum contracts v, M, and a column of adj(M).35defunique adjugateContractionEntry (I A add zero neg mul one v M j p : Set) (a ∈ A) :=36∃ i ∈ I, ∃ k ∈ I, p = (i, k) ∧37 a = call2(mul, v(i), call2(mul, M[i, k], adjugate(I, A, add, zero, neg, mul, one, M)[k, j]))38requiresring : IsCommRing(A, add, zero, neg, mul, one)39requires finite : Finite(I)40requires vector : v : I → A41requires matrix : RingMatrix(I, A, M)42requires column : j ∈ I43requires pair : p ∈ I × I44proof45obtain⟨bound, i, hi, k, hk, shape⟩:= product.membership.forward at46 I, I, p with*47have adj := adjugate.matrix at I, A, add, zero, neg, mul, one, M with*48have coefficient := FunctionBetween.apply_closed at v, I, A, i with*49haveleft:= RingMatrix.entry at I, A, M, i, k with*50haveright:= RingMatrix.entry at I, A, adjugate(I, A, add, zero, neg, mul, one, M), k, j with*51have ordinary := IsCommRing.ring withring52have product := IsRing.mul_closed with ordinary, left, right53have value := IsRing.mul_closed with ordinary, coefficient, product54refine⟨call2(mul, v(i), call2(mul, M[i, k], adjugate(I, A, add, zero, neg, mul, one, M)[k, j])), value, i, hi, k, hk, shape, rfl⟩55uniqueness56intros u hu first w hw second57obtain⟨i, hi, k, hk, shapeU, valueU⟩:= first58obtain⟨l, hl, r, hr, shapeW, valueW⟩:= second59have equal : (i, k) = (l, r)60cc61have coordinates := orderedPair.injective at i, k, l, r with equal62cc6364defgraph adjugateContraction (I A add zero neg mul one v M j : Set) (p ∈ I × I) : A :=65 adjugateContractionEntry(I, A, add, zero, neg, mul, one, v, M, j, p)66requiresring : IsCommRing(A, add, zero, neg, mul, one)67requires finite : Finite(I)68requires vector : v : I → A69requires matrix : RingMatrix(I, A, M)70requires column : j ∈ I71proof72have selected := adjugateContractionEntry.selected at I, A, add, zero, neg, mul, one, v, M, j, p with*73exact selected.left7475notationscoped adjugateContraction(I, R, v, M, j) => adjugateContraction(I, R.carrier, R.add, R.zero, R.neg, R.mul, R.one, v, M, j)7677section Contraction78variable (I A add zero neg mul one v M j : Set)79notationring R := (A, add, zero, neg, mul, one)80hypothesisring : IsCommRing(R)81hypothesis finite : Finite(I)82hypothesis vector : v : I → R83hypothesis matrix : RingMatrix(I, R, M)84hypothesis column : j ∈ I8586theorem adjugateContraction.pair (i k : Set)87requires row : i ∈ I88requires index : k ∈ I89shows adjugateContraction(I, R, v, M, j)[i, k] = v(i) * (M[i, k] * adjugate(I, R, M)[k, j])90proof91have pair := product.pair at i, k, I, I with*92have selected := adjugateContractionEntry.selected at I, A, add, zero, neg, mul, one, v, M, j, (i, k) with*93obtain⟨l, hl, r, hr, shape, result⟩:= selected.right94have coordinates := orderedPair.injective with shape95have value := adjugateContraction.value at I, A, add, zero, neg, mul, one, v, M, j, (i, k) with*96cc9798theorem adjugateContraction.row (i : Set)99requires row : i ∈ I100shows finiteRowSums(I, I, A, add, zero, neg, mul, one, adjugateContraction(I, R, v, M, j))(i) =101 v(i) * matrixProductEntry(I, R, M, adjugate(I, R, M), i, j)102proof103have contraction := adjugateContraction.function at I, A, add, zero, neg, mul, one, v, M, j with*104have rows := finiteRowWeights.function at I, I, A, adjugateContraction(I, R, v, M, j), i with*105have adj := adjugate.matrix with*106have products := matrixProductWeights.function at I, A, add, zero, neg, mul, one, M, adjugate(I, R, M), i, j with*107have scalar := FunctionBetween.apply_closed at v, I, A, i with*108have entries : ∀ k ∈ I, finiteRowWeights(I, I, A, adjugateContraction(I, R, v, M, j), i)(k) = v(i) * matrixProductWeights(I, R, M, adjugate(I, R, M), i, j)(k)109intros k hk110haveleft:= finiteRowWeights.value at I, I, A, adjugateContraction(I, R, v, M, j), i, k with*111haveright:= matrixProductWeights.value at I, A, add, zero, neg, mul, one, M, adjugate(I, R, M), i, j, k with*112have pair := adjugateContraction.pair at I, A, add, zero, neg, mul, one, v, M, j, i, k with*113cc114have scale := finiteRingSum.scale_eq with*115have value := finiteRowSums.value at I, I, A, add, zero, neg, mul, one, adjugateContraction(I, R, v, M, j), i with*116unfold matrixProductEntry117cc118119theorem adjugateContraction.column_zero (k : Set)120requires index : k ∈ I121requires vanishes : rowVectorEntry(I, R, v, M, k) = R[0]122shows finiteRowSums(I, I, A, add, zero, neg, mul, one, transposedMatrix(I, I, A, adjugateContraction(I, R, v, M, j)))(k) = R[0]123proof124have contraction := adjugateContraction.function at I, A, add, zero, neg, mul, one, v, M, j with*125have transposed := transposedMatrix.function at I, I, A, adjugateContraction(I, R, v, M, j) with*126have columns := finiteRowWeights.function at I, I, A, transposedMatrix(I, I, A, adjugateContraction(I, R, v, M, j)), k with*127have weights := rowVectorWeights.function at I, A, add, zero, neg, mul, one, v, M, k with*128have adj := adjugate.matrix with*129have scalar := RingMatrix.entry at I, A, adjugate(I, R, M), k, j with*130have entries : ∀ i ∈ I, finiteRowWeights(I, I, A, transposedMatrix(I, I, A, adjugateContraction(I, R, v, M, j)), k)(i) = adjugate(I, R, M)[k, j] * rowVectorWeights(I, R, v, M, k)(i)131intros i hi132haveleft:= finiteRowWeights.value at I, I, A, transposedMatrix(I, I, A, adjugateContraction(I, R, v, M, j)), k, i with*133have transpose := transposedMatrix.pair at I, I, A, adjugateContraction(I, R, v, M, j), i, k with*134have pair := adjugateContraction.pair at I, A, add, zero, neg, mul, one, v, M, j, i, k with*135haveright:= rowVectorWeights.value at I, A, add, zero, neg, mul, one, v, M, k, i with*136have coefficient := FunctionBetween.apply_closed at v, I, A, i with*137have entry := RingMatrix.entry at I, A, M, i, k with*138rw [left, transpose, pair, right]139ring140have scale := finiteRingSum.scale_eq with*141have value := finiteRowSums.value at I, I, A, add, zero, neg, mul, one, transposedMatrix(I, I, A, adjugateContraction(I, R, v, M, j)), k with*142unfold rowVectorEntry at vanishes143rw [value, scale, vanishes]144ring145146end Contraction147148section Annihilation149variable (A add zero neg mul one : Set)150notationring R := (A, add, zero, neg, mul, one)151hypothesisring : IsCommRing(R)152153theorem determinant.annihilates_left_kernel (n : Nat) (M v j : Set)154requires matrix : RingMatrix(n, R, M)155requires kernel : LeftKernelVector(n, R, M, v)156requires index : j ∈ n157shows determinant(n, R, M) * v(j) = R[0]158proof159have vector := kernel.left160have annihilates := kernel.right161have contraction := adjugateContraction.function at n, A, add, zero, neg, mul, one, v, M, j with*162have rows := finiteRowSums.function at n, n, A, add, zero, neg, mul, one, adjugateContraction(n, R, v, M, j) with*163have transposed := transposedMatrix.function at n, n, A, adjugateContraction(n, R, v, M, j) with*164have columns := finiteRowSums.function at n, n, A, add, zero, neg, mul, one, transposedMatrix(n, n, A, adjugateContraction(n, R, v, M, j)) with*165have zeros : ∀ k ∈ n, finiteRowSums(n, n, A, add, zero, neg, mul, one, transposedMatrix(n, n, A, adjugateContraction(n, R, v, M, j)))(k) = R[0]166intros k hk167have vanishes := annihilates with k, hk168exact adjugateContraction.column_zero with*169have zeroSum := finiteRingSum.zero at n, A, add, zero, neg, mul, one, finiteRowSums(n, n, A, add, zero, neg, mul, one, transposedMatrix(n, n, A, adjugateContraction(n, R, v, M, j))) with*170have ordinary := IsCommRing.ring withring171have zeroIn := IsRing.zero_in_carrier with ordinary172have determinantIn := determinant.closed at A, add, zero, neg, mul, one, M, n with*173have valueIn := FunctionBetween.apply_closed at v, n, A, j with*174have productIn := IsRing.mul_closed with ordinary, determinantIn, valueIn175have single := coefficientSingle.function at n, A, zero, determinant(n, R, M) * v(j), j with*176have rowEntries : ∀ i ∈ n, finiteRowSums(n, n, A, add, zero, neg, mul, one, adjugateContraction(n, R, v, M, j))(i) = coefficientSingle(n, A, zero, determinant(n, R, M) * v(j), j)(i)177intros i hi178have row := adjugateContraction.row at n, A, add, zero, neg, mul, one, v, M, j, i with*179have identity := adjugate.right_identity at A, add, zero, neg, mul, one, n, M, i, j with*180have coefficient := FunctionBetween.apply_closed at v, n, A, i with*181have alternatives : i = j ∨ i ≠ j bytauto182cases alternatives with same | different183 . have delta := singleValue.same at A, zero, determinant(n, R, M), j, i with*184have selected := coefficientSingle.same at n, A, zero, determinant(n, R, M) * v(j), j with*185rw [row, identity, delta, same, selected]186ring187 . have delta := singleValue.other at A, zero, determinant(n, R, M), j, i with*188have selected := coefficientSingle.other at n, A, zero, determinant(n, R, M) * v(j), j, i with*189rw [row, identity, delta, selected]190ring191have rowSum := finiteRingSum.congr at n, A, add, zero, neg, mul, one, finiteRowSums(n, n, A, add, zero, neg, mul, one, adjugateContraction(n, R, v, M, j)), coefficientSingle(n, A, zero, determinant(n, R, M) * v(j), j) with*192have selectedSum := finiteRingSum.single at n, A, add, zero, neg, mul, one, determinant(n, R, M) * v(j), j with*193have interchange := finiteDoubleSum.interchange at n, n, A, add, zero, neg, mul, one, adjugateContraction(n, R, v, M, j) with*194unfold finiteDoubleSum at interchange195cc196197theorem determinant.zero_of_nonzero_left_kernel (n : Nat) (M v j : Set)198requires domain : IsIntegralDomain(R)199requires matrix : RingMatrix(n, R, M)200requires kernel : LeftKernelVector(n, R, M, v)201requires index : j ∈ n202requires nonzero : v(j) ≠ R[0]203shows determinant(n, R, M) = R[0]204proof205have annihilates := determinant.annihilates_left_kernel with*206have vector := kernel.left207have value := FunctionBetween.apply_closed with*208have noZero := IsIntegralDomain.no_zero_divisors with domain209have alternatives := noZero with determinant(n, R, M), v(j), value, annihilates210tauto211212end Annihilation213