haar_lib/linalg/mod_p/
pfaffian.rs1use crate::misc::swap::swap_vv;
9use crate::num::ff::*;
10
11fn swap<T>(a: &mut [Vec<T>], i: usize, j: usize)
12where
13 T: FFElem,
14{
15 assert!(i < j);
16
17 for k in 0..i {
18 swap_vv(a, i, k, j, k);
19 }
20
21 for k in i + 1..j {
22 swap_vv(a, k, i, j, k);
23 a[k][i] = -a[k][i];
24 a[j][k] = -a[j][k];
25 }
26
27 for k in j + 1..a.len() {
28 swap_vv(a, k, i, k, j);
29 }
30
31 a[j][i] = -a[j][i];
32}
33
34fn add<T>(a: &mut [Vec<T>], s: T, i: usize, j: usize)
35where
36 T: FFElem,
37{
38 assert!(i < j);
39
40 for k in 0..i {
41 unsafe {
42 let t = *a.get_unchecked(i).get_unchecked(k);
43 *a.get_unchecked_mut(j).get_unchecked_mut(k) += t * s;
44 }
45 }
46
47 for k in i + 1..j {
48 unsafe {
49 let t = *a.get_unchecked(k).get_unchecked(i);
50 *a.get_unchecked_mut(j).get_unchecked_mut(k) -= t * s;
51 }
52 }
53
54 for k in j + 1..a.len() {
55 unsafe {
56 let t = *a.get_unchecked(k).get_unchecked(i);
57 *a.get_unchecked_mut(k).get_unchecked_mut(j) += t * s;
58 }
59 }
60}
61
62pub fn pfaffian<F>(mut a: Vec<Vec<F::Element>>, modulo: &F) -> F::Element
68where
69 F: FF,
70 F::Element: FFElem + std::fmt::Debug,
71{
72 let n = a.len();
73
74 assert_eq!(n % 2, 0);
75 assert!(a.iter().all(|r| r.len() == n));
76
77 for i in 0..n {
78 for j in 0..n {
79 assert_eq!(a[i][j], -a[j][i]);
80 }
81 }
82
83 for (i, r) in a.iter_mut().enumerate() {
84 r.truncate(i + 1);
85 }
86
87 let mut minus = false;
88 for i in (0..n).step_by(2) {
89 if a[i + 1][i].value() == 0 {
90 if let Some(j) = (i + 2..n).find(|&j| a[j][i].value() != 0) {
91 swap(&mut a, i + 1, j);
92 minus = !minus;
93 } else {
94 return modulo.zero();
95 }
96 }
97
98 assert_ne!(a[i + 1][i], modulo.zero());
99 let t = a[i + 1][i].inv();
100 for j in i + 2..n {
101 let c = -a[j][i] * t;
102 add(&mut a, c, i + 1, j);
103
104 let c = a[j][i + 1] * t;
105 add(&mut a, c, i, j);
106 }
107 }
108
109 let mut ret = modulo.one();
110
111 for i in (0..n).step_by(2) {
112 ret *= -a[i + 1][i];
113 }
114 if minus {
115 ret = -ret;
116 }
117
118 ret
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use crate::num::modint::*;
125
126 fn check(a: Vec<Vec<i64>>, m: u32, ans: u32) {
127 let m = ModIntBuilder::new(m);
128 let a = a
129 .into_iter()
130 .map(|b| b.into_iter().map(|x| m.from_i64(x)).collect())
131 .collect();
132 assert_eq!(pfaffian(a, &m).value(), ans);
133 }
134
135 #[test]
136 fn test() {
137 check(
138 vec![
139 vec![0, 1, 2, 3],
140 vec![-1, 0, 4, 5],
141 vec![-2, -4, 0, 6],
142 vec![-3, -5, -6, 0],
143 ],
144 998244353,
145 8,
146 );
147 check(vec![vec![0, 1], vec![-1, 0]], 998244353, 1);
148 }
149}