logicaffeine_proof/
linalg.rs1use logicaffeine_base::Rational;
12
13pub fn matvec(a: &[Vec<Rational>], x: &[Rational]) -> Vec<Rational> {
15 a.iter()
16 .map(|row| {
17 row.iter()
18 .zip(x)
19 .fold(Rational::zero(), |acc, (aij, xj)| acc.add(&aij.mul(xj)))
20 })
21 .collect()
22}
23
24pub fn residual_is_zero(a: &[Vec<Rational>], x: &[Rational], b: &[Rational]) -> bool {
26 let ax = matvec(a, x);
27 ax.len() == b.len() && ax.iter().zip(b).all(|(l, r)| l == r)
28}
29
30pub fn solve(a: &[Vec<Rational>], b: &[Rational]) -> Option<Vec<Rational>> {
33 let n = a.len();
34 if n == 0 || a.iter().any(|r| r.len() != n) || b.len() != n {
35 return None;
36 }
37 let mut m: Vec<Vec<Rational>> = a
39 .iter()
40 .zip(b)
41 .map(|(row, bi)| {
42 let mut r = row.clone();
43 r.push(bi.clone());
44 r
45 })
46 .collect();
47 for col in 0..n {
48 let piv = (col..n).find(|&r| !m[r][col].is_zero())?;
50 m.swap(col, piv);
51 let inv = m[col][col].recip()?; for j in col..=n {
54 m[col][j] = m[col][j].mul(&inv);
55 }
56 for r in 0..n {
58 if r == col {
59 continue;
60 }
61 let f = m[r][col].clone();
62 if f.is_zero() {
63 continue;
64 }
65 for j in col..=n {
66 m[r][j] = m[r][j].sub(&f.mul(&m[col][j]));
67 }
68 }
69 }
70 Some((0..n).map(|i| m[i][n].clone()).collect())
71}
72
73pub fn det(a: &[Vec<Rational>]) -> Rational {
75 let n = a.len();
76 if n == 0 || a.iter().any(|r| r.len() != n) {
77 return Rational::zero();
78 }
79 let mut m: Vec<Vec<Rational>> = a.to_vec();
80 let mut d = Rational::one();
81 for col in 0..n {
82 let piv = match (col..n).find(|&r| !m[r][col].is_zero()) {
83 Some(p) => p,
84 None => return Rational::zero(),
85 };
86 if piv != col {
87 m.swap(col, piv);
88 d = d.negated();
89 }
90 d = d.mul(&m[col][col]);
91 let inv = m[col][col].recip().expect("nonzero pivot");
92 for r in (col + 1)..n {
93 let f = m[r][col].mul(&inv);
94 if f.is_zero() {
95 continue;
96 }
97 for j in col..n {
98 m[r][j] = m[r][j].sub(&f.mul(&m[col][j]));
99 }
100 }
101 }
102 d
103}
104
105pub struct Certified {
108 pub x: Vec<Rational>,
109 pub residual_zero: bool,
111 pub nonsingular: bool,
113}
114
115pub fn certify_solve(a: &[Vec<Rational>], b: &[Rational]) -> Option<Certified> {
117 let x = solve(a, b)?;
118 let residual_zero = residual_is_zero(a, &x, b);
119 let nonsingular = !det(a).is_zero();
120 Some(Certified {
121 x,
122 residual_zero,
123 nonsingular,
124 })
125}
126
127pub struct Lu {
130 pub perm: Vec<usize>,
131 pub l: Vec<Vec<Rational>>,
132 pub u: Vec<Vec<Rational>>,
133}
134
135pub fn lu(a: &[Vec<Rational>]) -> Option<Lu> {
137 let n = a.len();
138 if n == 0 || a.iter().any(|r| r.len() != n) {
139 return None;
140 }
141 let mut u = a.to_vec();
142 let mut l: Vec<Vec<Rational>> = (0..n)
143 .map(|i| {
144 (0..n)
145 .map(|j| {
146 if i == j {
147 Rational::one()
148 } else {
149 Rational::zero()
150 }
151 })
152 .collect()
153 })
154 .collect();
155 let mut perm: Vec<usize> = (0..n).collect();
156 for k in 0..n {
157 let piv = (k..n).find(|&r| !u[r][k].is_zero())?;
158 if piv != k {
159 u.swap(k, piv);
160 perm.swap(k, piv);
161 for j in 0..k {
163 let tmp = l[k][j].clone();
164 l[k][j] = l[piv][j].clone();
165 l[piv][j] = tmp;
166 }
167 }
168 let inv = u[k][k].recip()?;
169 for i in (k + 1)..n {
170 let f = u[i][k].mul(&inv);
171 l[i][k] = f.clone();
172 for j in k..n {
173 u[i][j] = u[i][j].sub(&f.mul(&u[k][j]));
174 }
175 }
176 }
177 Some(Lu { perm, l, u })
178}
179
180pub fn matmul(a: &[Vec<Rational>], b: &[Vec<Rational>]) -> Vec<Vec<Rational>> {
182 let (n, m, p) = (a.len(), b.len(), b[0].len());
183 (0..n)
184 .map(|i| {
185 (0..p)
186 .map(|j| (0..m).fold(Rational::zero(), |acc, k| acc.add(&a[i][k].mul(&b[k][j]))))
187 .collect()
188 })
189 .collect()
190}
191
192pub fn lu_is_exact(a: &[Vec<Rational>], f: &Lu) -> bool {
194 let pa: Vec<Vec<Rational>> = f.perm.iter().map(|&i| a[i].clone()).collect();
195 matmul(&f.l, &f.u) == pa
196}
197
198pub fn identity(n: usize) -> Vec<Vec<Rational>> {
200 (0..n)
201 .map(|i| {
202 (0..n)
203 .map(|j| {
204 if i == j {
205 Rational::one()
206 } else {
207 Rational::zero()
208 }
209 })
210 .collect()
211 })
212 .collect()
213}
214
215pub fn transpose(a: &[Vec<Rational>]) -> Vec<Vec<Rational>> {
217 if a.is_empty() {
218 return Vec::new();
219 }
220 let (n, m) = (a.len(), a[0].len());
221 (0..m)
222 .map(|j| (0..n).map(|i| a[i][j].clone()).collect())
223 .collect()
224}
225
226pub fn inverse(a: &[Vec<Rational>]) -> Option<Vec<Vec<Rational>>> {
228 let n = a.len();
229 if n == 0 || a.iter().any(|r| r.len() != n) {
230 return None;
231 }
232 let mut m: Vec<Vec<Rational>> = a
234 .iter()
235 .enumerate()
236 .map(|(i, row)| {
237 let mut r = row.clone();
238 for j in 0..n {
239 r.push(if i == j {
240 Rational::one()
241 } else {
242 Rational::zero()
243 });
244 }
245 r
246 })
247 .collect();
248 for col in 0..n {
249 let piv = (col..n).find(|&r| !m[r][col].is_zero())?;
250 m.swap(col, piv);
251 let inv = m[col][col].recip()?;
252 for j in col..(2 * n) {
253 m[col][j] = m[col][j].mul(&inv);
254 }
255 for r in 0..n {
256 if r == col {
257 continue;
258 }
259 let f = m[r][col].clone();
260 if f.is_zero() {
261 continue;
262 }
263 for j in col..(2 * n) {
264 m[r][j] = m[r][j].sub(&f.mul(&m[col][j]));
265 }
266 }
267 }
268 Some(
269 (0..n)
270 .map(|i| (0..n).map(|j| m[i][n + j].clone()).collect())
271 .collect(),
272 )
273}
274
275pub fn rank(a: &[Vec<Rational>]) -> usize {
277 if a.is_empty() {
278 return 0;
279 }
280 let (rows, cols) = (a.len(), a[0].len());
281 let mut m = a.to_vec();
282 let mut prow = 0usize;
283 for col in 0..cols {
284 if prow == rows {
285 break;
286 }
287 let piv = match (prow..rows).find(|&r| !m[r][col].is_zero()) {
288 Some(p) => p,
289 None => continue,
290 };
291 m.swap(prow, piv);
292 let inv = m[prow][col].recip().expect("nonzero pivot");
293 for r in 0..rows {
294 if r == prow {
295 continue;
296 }
297 let f = m[r][col].mul(&inv);
298 if f.is_zero() {
299 continue;
300 }
301 for j in col..cols {
302 m[r][j] = m[r][j].sub(&f.mul(&m[prow][j]));
303 }
304 }
305 prow += 1;
306 }
307 prow
308}
309
310pub fn is_inverse(a: &[Vec<Rational>], b: &[Vec<Rational>]) -> bool {
312 matmul(a, b) == identity(a.len())
313}
314
315#[cfg(test)]
316#[path = "../../logicaffeine_tests/unit/logicaffeine_proof/linalg/tests.rs"]
317mod tests;