1use crate::linalg;
19use logicaffeine_base::Rational;
20use logicaffeine_kernel::{
21 infer_type, normalize, prelude::StandardLibrary, Context, Literal, Term,
22};
23
24fn global(s: &str) -> Term {
25 Term::Global(s.to_string())
26}
27fn app(f: Term, x: Term) -> Term {
28 Term::App(Box::new(f), Box::new(x))
29}
30fn app2(f: Term, x: Term, y: Term) -> Term {
31 app(app(f, x), y)
32}
33fn lit(n: i64) -> Term {
34 Term::Lit(Literal::Int(n))
35}
36fn add(a: Term, b: Term) -> Term {
37 app2(global("add"), a, b)
38}
39fn mul(a: Term, b: Term) -> Term {
40 app2(global("mul"), a, b)
41}
42fn eq_int(l: Term, r: Term) -> Term {
44 app(app(app(global("Eq"), global("Int")), l), r)
45}
46
47pub fn ring_context() -> Context {
50 StandardLibrary::context().clone()
51}
52
53fn gcd(a: i128, b: i128) -> i128 {
54 let (mut a, mut b) = (a.abs(), b.abs());
55 while b != 0 {
56 let t = a % b;
57 a = b;
58 b = t;
59 }
60 a
61}
62fn lcm(a: i128, b: i128) -> Option<i128> {
63 if a == 0 || b == 0 {
64 return Some(0);
65 }
66 let g = gcd(a, b);
67 (a / g).checked_mul(b).map(|v| v.abs())
68}
69fn fit(v: i128) -> Option<i64> {
70 if (i64::MIN as i128..=i64::MAX as i128).contains(&v) {
71 Some(v as i64)
72 } else {
73 None
74 }
75}
76fn nd(r: &Rational) -> Option<(i128, i128)> {
78 Some((
79 r.numerator().to_i64()? as i128,
80 r.denominator().to_i64()? as i128,
81 ))
82}
83
84fn clear_vec(v: &[Rational]) -> Option<(Vec<i128>, i128)> {
87 let nds: Vec<(i128, i128)> = v.iter().map(nd).collect::<Option<_>>()?;
88 let mut d: i128 = 1;
89 for &(_, den) in &nds {
90 d = lcm(d, den)?;
91 }
92 let ints: Vec<i128> = nds
93 .iter()
94 .map(|&(num, den)| num.checked_mul(d / den))
95 .collect::<Option<_>>()?;
96 Some((ints, d))
97}
98
99pub fn prove_dot(ctx: &Context, ps: &[Rational], qs: &[Rational], r: &Rational) -> Option<Term> {
105 let k = ps.len();
106 if k == 0 || qs.len() != k {
107 return None;
108 }
109 let (pn, dp) = clear_vec(ps)?;
110 let (qn, dq) = clear_vec(qs)?;
111 let (rn, rd) = nd(r)?;
112 let mut sum = mul(lit(fit(pn[0])?), lit(fit(qn[0])?));
113 for i in 1..k {
114 sum = add(sum, mul(lit(fit(pn[i])?), lit(fit(qn[i])?)));
115 }
116 let lhs = mul(lit(fit(rd)?), sum);
117 let rhs = lit(fit(dp.checked_mul(dq)?.checked_mul(rn)?)?);
118 let proof = crate::arith::prove_int_eq(ctx, &lhs, &rhs)?;
120 let ty = infer_type(ctx, &proof).ok()?;
121 if normalize(ctx, &ty) != normalize(ctx, &eq_int(lhs, rhs)) {
122 return None;
123 }
124 Some(proof)
125}
126
127pub fn prove_nonzero(ctx: &Context, r: &Rational) -> Option<Term> {
131 let inv = r.recip()?; prove_dot(
133 ctx,
134 std::slice::from_ref(r),
135 std::slice::from_ref(&inv),
136 &Rational::one(),
137 )
138}
139
140pub fn prove_solution(
143 ctx: &Context,
144 a: &[Vec<Rational>],
145 x: &[Rational],
146 b: &[Rational],
147) -> Option<Vec<Term>> {
148 let n = a.len();
149 if n == 0 || a.iter().any(|r| r.len() != n) || x.len() != n || b.len() != n {
150 return None;
151 }
152 (0..n).map(|i| prove_dot(ctx, &a[i], x, &b[i])).collect()
153}
154
155pub fn prove_lu(ctx: &Context, a: &[Vec<Rational>]) -> Option<(linalg::Lu, Vec<Vec<Term>>)> {
158 let f = linalg::lu(a)?;
159 let n = a.len();
160 let pa: Vec<Vec<Rational>> = f.perm.iter().map(|&i| a[i].clone()).collect();
161 let ucols = linalg::transpose(&f.u); let mut proofs = Vec::with_capacity(n);
163 for i in 0..n {
164 let mut row = Vec::with_capacity(n);
165 for j in 0..n {
166 row.push(prove_dot(ctx, &f.l[i], &ucols[j], &pa[i][j])?);
167 }
168 proofs.push(row);
169 }
170 Some((f, proofs))
171}
172
173pub fn prove_inverse(
176 ctx: &Context,
177 a: &[Vec<Rational>],
178) -> Option<(Vec<Vec<Rational>>, Vec<Vec<Term>>)> {
179 let ainv = linalg::inverse(a)?;
180 let n = a.len();
181 let id = linalg::identity(n);
182 let invcols = linalg::transpose(&ainv); let mut proofs = Vec::with_capacity(n);
184 for i in 0..n {
185 let mut row = Vec::with_capacity(n);
186 for j in 0..n {
187 row.push(prove_dot(ctx, &a[i], &invcols[j], &id[i][j])?);
188 }
189 proofs.push(row);
190 }
191 Some((ainv, proofs))
192}
193
194pub struct SystemCert {
199 pub x: Vec<Rational>,
200 pub solve_proofs: Vec<Term>,
201 pub det: Rational,
202 pub nonsingular_proof: Term,
203}
204
205pub fn certify_system(ctx: &Context, a: &[Vec<Rational>], b: &[Rational]) -> Option<SystemCert> {
207 let x = linalg::solve(a, b)?;
208 let solve_proofs = prove_solution(ctx, a, &x, b)?;
209 let det = linalg::det(a);
210 let nonsingular_proof = prove_nonzero(ctx, &det)?;
211 Some(SystemCert {
212 x,
213 solve_proofs,
214 det,
215 nonsingular_proof,
216 })
217}
218
219pub fn prove_solve(
222 ctx: &Context,
223 a: &[Vec<Rational>],
224 b: &[Rational],
225) -> Option<(Vec<Rational>, Vec<Term>)> {
226 let x = linalg::solve(a, b)?;
227 let proofs = prove_solution(ctx, a, &x, b)?;
228 Some((x, proofs))
229}
230
231#[cfg(test)]
232#[path = "../../logicaffeine_tests/unit/logicaffeine_proof/linalg_certify/tests.rs"]
233mod tests;