Skip to main content

open_hypergraphs/strict/
relation.rs

1//! Indexed coproducts as relations
2use crate::array::{Array, ArrayKind, NaturalArray, OrdArray};
3use crate::finite_function::FiniteFunction;
4use crate::indexed_coproduct::{HasLen, IndexedCoproduct};
5use num_traits::{One, Zero};
6
7/// Compute the *converse* of an [`IndexedCoproduct`] thought of as a "multirelation".
8///
9/// An [`IndexedCoproduct`] `c : Σ_{x ∈ X} s(x) → Q` can equivalently be thought of as `c : X →
10/// Q*`, i.e. a mapping from X to finite lists of elements in Q.
11///
12/// Such a list defines a (multi-)relation as the multiset of pairs
13///
14/// `R = { ( x, f(x)_i ) | x ∈ X, i ∈ len(f(x)) }`
15///
16/// This function computes the *converse* of that relation as an indexed coproduct
17/// `converse(c) : Q → X*`, or more precisely
18/// `converse(c) : Σ_{q ∈ Q} s(q) → X`.
19///
20/// NOTE: An indexed coproduct does not uniquely represent a 'multirelation', since *order* of the
21/// elements matters.
22/// The result of this function is only unique up to permutation of the sublists.
23pub fn converse<K: ArrayKind>(
24    r: &IndexedCoproduct<K, FiniteFunction<K>>,
25) -> IndexedCoproduct<K, FiniteFunction<K>>
26where
27    K::Type<K::I>: NaturalArray<K>,
28{
29    let values_table = {
30        let arange = K::Index::arange(&K::I::zero(), &r.sources.len());
31        let unsorted_values = r.sources.table.repeat(arange.get_range(..));
32        unsorted_values.sort_by(&r.values.table)
33    };
34
35    let sources_table =
36        (r.values.table.as_ref() as &K::Type<K::I>).bincount(r.values.target.clone());
37
38    let sources = FiniteFunction::new(sources_table, r.values.table.len() + K::I::one()).unwrap();
39    let values = FiniteFunction::new(values_table, r.len()).unwrap();
40
41    IndexedCoproduct::new(sources, values).unwrap()
42}