flag_algebra/
density.rs

1//! Computing coefficients of a flag algebra operator.
2
3use crate::flag::Flag;
4use crate::iterators::*;
5use sprs::{CsMat, TriMatI, CSC, CSR};
6
7fn induce_and_reduce<F: Flag>(type_size: usize, f: &F, subset: &[usize]) -> F {
8    f.induce(subset).canonical_typed(type_size)
9}
10
11/// Returns a matrix `m`
12/// where `m[i,j]` is equal to
13/// `count_subflags(type_size, f_vec[i], g_vec[j])`.
14///
15/// This more efficient than iterating `count_subflags`.
16///
17/// The output is a sparse matrix in CSR form.
18pub fn count_subflag_tabulate<F: Flag>(type_size: usize, f_vec: &[F], g_vec: &[F]) -> CsMat<u32> {
19    let k = f_vec[0].size();
20    let n = g_vec[0].size();
21    assert!(type_size <= k && k <= n);
22    //
23    let mut res = CsMat::empty(CSR, f_vec.len());
24    for g in g_vec {
25        let mut column = vec![0; f_vec.len()];
26        //
27        let mut iter = Choose::with_fixed_part(n, k, type_size);
28        while let Some(subset) = iter.next() {
29            let f0 = induce_and_reduce(type_size, g, subset);
30            match f_vec.binary_search(&f0) {
31                Ok(i) => column[i] += 1,
32                Err(_) => {
33                    assert!(!F::HEREDITARY, "Flag not found: {:?}", &f0);
34                }
35            };
36        }
37        res = res.append_outer(&column)
38    }
39    res
40}
41
42/// Returns a vector `m` of length `g_vec.len()` of
43/// `f1_vec.len()`x`f2_vec.len()` matrices
44/// such that
45/// `m[i][j,k]` is equal to
46/// `count_split(type_size, f1_vec[j], f2_vec[k], g_vec[i])`.
47///
48/// This more efficient than iterating `count_split`.
49///
50/// The matrices are in CSR form.
51// So that Trans(f1) * M * f2 is correct
52// reminder: CSR: outer = rows, inner = cols
53// so it is a outer x inner matrix (rows x cols)
54pub fn count_split_tabulate<F: Flag>(
55    type_size: usize,
56    f1_vec: &[F],
57    f2_vec: &[F],
58    g_vec: &[F],
59) -> Vec<CsMat<u32>> {
60    let k1 = f1_vec[0].size();
61    let k2 = f2_vec[0].size();
62    let n = g_vec[0].size();
63    assert!(type_size <= k1 && k1 <= n);
64    assert_eq!(n, k1 + k2 - type_size);
65    //
66    let shape = (f1_vec.len(), f2_vec.len());
67    let storage = if shape.0 < shape.1 { CSR } else { CSC };
68    //
69    let mut res: Vec<CsMat<u32>> = Vec::with_capacity(g_vec.len());
70
71    for g in g_vec {
72        let mut tri_mat = TriMatI::new(shape); //with capacity ?
73        let mut iter = Split::with_fixed_part(n, k1, type_size);
74        while let Some((subset1, subset2)) = iter.next() {
75            let f1 = induce_and_reduce(type_size, g, subset1);
76            let f2 = induce_and_reduce(type_size, g, subset2);
77            if let (Ok(i1), Ok(i2)) = (f1_vec.binary_search(&f1), f2_vec.binary_search(&f2)) {
78                tri_mat.add_triplet(i1, i2, 1);
79            } else if F::HEREDITARY {
80                panic!("Flag not found")
81            };
82        }
83        let mat = if storage == CSR {
84            tri_mat.to_csr()
85        } else {
86            tri_mat.to_csc()
87        };
88        res.push(mat)
89    }
90    res
91}
92
93/// Returns a vector `t` where `t[i]` is the index of the unlabeled version
94/// of `input_vec[i]`.
95///
96/// The unlabeling is performed while keeping the image of `eta` as the new type.
97pub fn unlabeling_tabulate<F: Flag>(
98    eta: &[usize],
99    input_vec: &[F],
100    output_vec: &[F],
101) -> Vec<usize> {
102    let type_size = eta.len();
103    let mut res = Vec::new();
104    for flag in input_vec {
105        let unlabeled = flag.select_type(eta).canonical_typed(type_size);
106        // FIXME
107        if type_size == 0 {
108            debug_assert_eq!(unlabeled, unlabeled.canonical_typed(0));
109            debug_assert_eq!(unlabeled, unlabeled.canonical());
110        }
111        res.push(
112            output_vec.binary_search(&unlabeled).unwrap_or_else(|_| {
113                panic!("Flag not found (type {}): {:?}", type_size, &unlabeled)
114            }),
115        )
116    }
117    res
118}
119
120/*
121/// Returns a vector `t` where `t[i]` is the number of ways to relabel
122/// `input_vec[i]` while keeping the same (labeled) flag.
123///
124/// The relabeling is performed while fixing the image of `eta` as the new type.
125pub fn old_unlabeling_count_tabulate<F: Flag>(
126    eta: &[usize],
127    type_size: usize,
128    input_vec: &[F],
129) -> Vec<u32> {
130    assert!(!input_vec.is_empty());
131    let mut res = vec![0; input_vec.len()];
132    for (i, flag) in input_vec.iter().enumerate() {
133        let flag2 = flag.select_type(eta).canonical_typed(type_size);
134        let mut iter = Injection::with_fixed_part(flag2.size(), type_size, eta.len());
135        while let Some(phi) = iter.next() {
136            let f = flag2.select_type(phi).canonical_typed(type_size);
137            if f == flag2 {
138                res[i] += 1;
139            }
140        }
141    }
142    res
143}
144*/
145
146pub fn unlabeling_count_tabulate<F: Flag>(
147    eta: &[usize],
148    type_size: usize,
149    input_vec: &[F],
150) -> Vec<u32> {
151    // Can be further optimized
152    assert!(!input_vec.is_empty());
153    let mut res = vec![0; input_vec.len()];
154    for (i, flag) in input_vec.iter().enumerate() {
155        let flag2 = flag.select_type(eta).canonical_typed(eta.len());
156        let aut_typed = flag.stabilizer(type_size).count() as u32;
157        let aut = flag2.stabilizer(eta.len()).count() as u32;
158        assert_eq!(aut % aut_typed, 0);
159        res[i] = aut / aut_typed;
160    }
161    res
162}
163
164/// Classify the typed flags in `input_vec` with type size `type_size` according to their
165/// modulo the symmetries of the type.
166///
167/// The output is a vector `class` where `class[i] == class[j]` if and only if  the rooted
168/// flags `input_vec[i]` and `input_vec[j]` are equal modulo permutations of the type.
169/// The values in `class` are integers form 0 to "number of classes" - 1 assigned in increasing
170/// order.
171pub fn invariant_classes<F: Flag>(eta: &[usize], type_size: usize, input_vec: &[F]) -> Vec<usize> {
172    // This can be simplified by computing normal form from a partition corresponding to
173    // {type, rest} if such a feature is exposed in the canonize crate
174    assert!(!input_vec.is_empty());
175    let n = input_vec[0].size();
176    assert!(type_size <= n);
177    if !eta.is_empty() {
178        unimplemented!()
179    };
180    let type_set: Vec<_> = (0..type_size).collect();
181    let type_flag = input_vec[0].induce(&type_set).canonical_typed(eta.len());
182    // Precompute the symmetry group of the type
183    let type_automorphisms: Vec<_> = type_flag
184        .stabilizer(eta.len())
185        .map(|mut f| {
186            f.extend(type_size..n);
187            f
188        })
189        .collect();
190    // class[i] always contain Some(c) if the class of input_vec[i] is found and is c
191    let mut class = vec![None; input_vec.len()];
192    let mut n_classes = 0;
193    for (i, flag) in input_vec.iter().enumerate() {
194        if class[i].is_none() {
195            let new_class = n_classes;
196            n_classes += 1;
197            for phi in &type_automorphisms {
198                let flag2 = flag.apply_morphism(phi).canonical_typed(type_size);
199                let id = input_vec.binary_search(&flag2).expect("Flag not found");
200                match class[id] {
201                    Some(class_id) => assert_eq!(class_id, new_class),
202                    None => class[id] = Some(new_class),
203                }
204            }
205        }
206    }
207    class.into_iter().map(|c| c.unwrap()).collect()
208}
209
210/// Matrices to separate the invariant and anti-invariant parts of a quantum flag.
211pub fn class_matrices(class: &[usize]) -> (CsMat<i64>, CsMat<i64>) {
212    assert!(!class.is_empty());
213    let n_classes = *class.iter().max().unwrap() + 1;
214    let n_flags = class.len();
215    let mut invariant = TriMatI::new((n_flags, n_classes));
216    let mut antiinvariant = TriMatI::new((n_flags, n_flags - n_classes));
217    // first_of_class contains the first element of the class i, if met.
218    // If class is valid, the classes are encounter in the order of their id
219    let mut first_of_class = Vec::new();
220    for (i, &c) in class.iter().enumerate() {
221        // build the invariant matrix
222        invariant.add_triplet(i, c, 1);
223        // build the antiinvariant matrix
224        if c < first_of_class.len() {
225            let col = i - first_of_class.len();
226            antiinvariant.add_triplet(i, col, 1);
227            antiinvariant.add_triplet(first_of_class[c], col, -1);
228        } else {
229            assert_eq!(c, first_of_class.len());
230            first_of_class.push(i)
231        }
232    }
233    (invariant.to_csc(), antiinvariant.to_csc())
234}
235
236///Tests
237#[cfg(test)]
238mod tests {
239    use super::*;
240    use crate::flags::*;
241    use canonical_form::Canonize;
242
243    /// Returns the number of induced subflags of `g` isomorphic to `f`,
244    /// considered with type of size `type_size`.
245    fn count_subflags<F: Flag>(type_size: usize, f: &F, g: &F) -> u32 {
246        // f must be in normal form
247        assert_eq!(*f, f.canonical_typed(type_size));
248        let k = f.size();
249        let n = g.size();
250        assert!(type_size <= k && k <= n);
251        let mut count = 0;
252        let mut iter = Choose::with_fixed_part(n, k, type_size);
253        while let Some(subset) = iter.next() {
254            if induce_and_reduce(type_size, g, subset) == *f {
255                count += 1;
256            }
257        }
258        count
259    }
260
261    /// Returns the number of split partitions of `g`
262    /// with intersection `[type_size]`
263    /// inducing `f1` and `f2`.
264    fn count_split<F: Flag>(type_size: usize, f1: &F, f2: &F, g: &F) -> u32 {
265        assert_eq!(*f1, f1.canonical_typed(type_size));
266        assert_eq!(*f2, f2.canonical_typed(type_size));
267        let k1 = f1.size();
268        let k2 = f2.size();
269        let n = g.size();
270        assert!(type_size <= k1 && k1 <= n);
271        assert_eq!(n, k1 + k2 - type_size);
272        let mut count = 0;
273        let mut iter = Split::with_fixed_part(n, k1, type_size);
274        while let Some((subset1, subset2)) = iter.next() {
275            if *f1 == induce_and_reduce(type_size, g, subset1)
276                && *f2 == induce_and_reduce(type_size, g, subset2)
277            {
278                count += 1;
279            }
280        }
281        count
282    }
283
284    fn count_subflags_ext<F: Flag>(sigma: usize, h: &F, g: &F) -> u32 {
285        count_subflags(sigma, &h.canonical_typed(sigma), g)
286    }
287
288    #[test]
289    fn unit_count_subflags() {
290        let cherry = Graph::new(3, &[(0, 1), (1, 2)]);
291        let c4 = Graph::cycle(4);
292        assert_eq!(4, count_subflags_ext(0, &cherry, &c4));
293        assert_eq!(2, count_subflags_ext(1, &cherry, &c4));
294
295        let c5 = Graph::cycle(5);
296        assert_eq!(
297            12,
298            count_subflags(0, &c5.canonical(), &Graph::petersen().canonical())
299        );
300    }
301
302    #[test]
303    fn count_subflags_typed() {
304        let p3 = Graph::new(3, &[(0, 1), (1, 2)]);
305        let p4 = Graph::new(4, &[(0, 1), (1, 2), (2, 3)]);
306        let g = Graph::new(6, &[(2, 0), (0, 5), (2, 5), (0, 4), (4, 3)]);
307        let g2 = Graph::new(
308            7,
309            &[
310                (0, 1),
311                (0, 5),
312                (1, 2),
313                (1, 4),
314                (2, 3),
315                (2, 4),
316                (5, 4),
317                (2, 5),
318            ],
319        );
320        let g3 = Graph::new(5, &[(0, 1), (1, 2), (1, 4), (2, 4), (2, 3)]);
321
322        assert_eq!(1, count_subflags_ext(1, &p3, &g));
323        assert_eq!(1, count_subflags_ext(3, &p4, &g2));
324        assert_eq!(1, count_subflags_ext(2, &g3, &g2));
325    }
326
327    #[test]
328    fn unit_count_split() {
329        let cherry = Graph::new(3, &[(0, 1), (1, 2)]).canonical();
330        let edge = Graph::new(2, &[(0, 1)]).canonical();
331        let g = Graph::new(5, &[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0), (0, 2)]).canonical();
332        assert_eq!(4, count_split(0, &cherry, &edge, &g));
333    }
334
335    fn csm_get<N: num::Zero + Copy>(mat: &CsMat<N>, i: usize, j: usize) -> N {
336        assert!(i < mat.rows() && j < mat.cols());
337        *mat.get(i, j).unwrap_or(&N::zero())
338    }
339
340    fn count_subflag_consistency<F: Flag>(k: usize, n: usize) {
341        let a = F::generate(k);
342        let b = F::generate(n);
343        let tab = count_subflag_tabulate(0, &a, &b);
344        for (i, ai) in a.iter().enumerate() {
345            for (j, bj) in b.iter().enumerate() {
346                assert_eq!(count_subflags(0, ai, bj), csm_get(&tab, j, i))
347            }
348        }
349    }
350
351    fn count_split_consistency<F: Flag>(k: usize, n: usize) {
352        let left = F::generate(k);
353        let right = F::generate(n - k);
354        let prod = F::generate(n);
355        let tab = count_split_tabulate(0, &left, &right, &prod);
356        for (i, left_flag) in left.iter().enumerate() {
357            for (j, right_flag) in right.iter().enumerate() {
358                for (p, prod_flag) in prod.iter().enumerate() {
359                    assert_eq!(
360                        count_split(0, left_flag, right_flag, prod_flag),
361                        csm_get(&tab[p], i, j)
362                    )
363                }
364            }
365        }
366    }
367
368    #[test]
369    fn unit_count_subflag_tabulate() {
370        count_subflag_consistency::<Graph>(3, 5);
371    }
372
373    #[test]
374    fn unit_count_split_tabulate() {
375        count_split_consistency::<Graph>(2, 5);
376    }
377}