open_hypergraphs/lax/
optic.rs

1//! # Optics for lax open hypergraphs
2//!
3//! This module provides an interface for defining optics on [`crate::lax::OpenHypergraph`]
4//! via the [`Optic`] trait.
5//!
6//! By defining the fwd and reverse mappings on objects and operations, the [`Optic`] trait will
7//! give you `map_arrow` and `map_adapted` methods for free.
8use std::fmt::Debug;
9
10use crate::lax::functor::{to_dyn_functor, DynFunctor, Functor};
11use crate::operations::Operations;
12use crate::strict::vec::VecArray;
13use crate::strict::vec::VecKind;
14use crate::strict::IndexedCoproduct;
15use crate::strict::SemifiniteFunction;
16use crate::{lax, lax::OpenHypergraph, strict::functor::optic::Optic as StrictOptic};
17
18/// #
19///
20/// foo
21pub trait Optic<
22    O1: Clone + PartialEq,
23    A1: Clone,
24    O2: Clone + PartialEq + std::fmt::Debug,
25    A2: Clone,
26>: Clone + 'static
27{
28    fn fwd_object(&self, o: &O1) -> Vec<O2>;
29    fn fwd_operation(&self, a: &A1, source: &[O1], target: &[O1]) -> OpenHypergraph<O2, A2>;
30    fn rev_object(&self, o: &O1) -> Vec<O2>;
31    fn rev_operation(&self, a: &A1, source: &[O1], target: &[O1]) -> OpenHypergraph<O2, A2>;
32    fn residual(&self, a: &A1) -> Vec<O2>;
33
34    fn map_arrow(&self, term: OpenHypergraph<O1, A1>) -> OpenHypergraph<O2, A2> {
35        let optic = to_strict_optic(self);
36        let strict = term.to_strict();
37        lax::OpenHypergraph::from_strict({
38            // Get the right trait in scope.
39            use crate::strict::functor::Functor;
40            optic.map_arrow(&strict)
41        })
42    }
43
44    fn map_adapted(&self, term: OpenHypergraph<O1, A1>) -> OpenHypergraph<O2, A2> {
45        let optic = to_strict_optic(self);
46        let strict = term.to_strict();
47        lax::OpenHypergraph::from_strict({
48            use crate::strict::functor::Functor;
49            let optic_term = optic.map_arrow(&strict);
50            // Adapt the produced term so it's monogamous again (as long as the input was).
51            optic.adapt(&optic_term, &strict.source(), &strict.target())
52        })
53    }
54}
55
56fn to_strict_optic<
57    T: Optic<O1, A1, O2, A2> + 'static,
58    O1: Clone + PartialEq,
59    A1: Clone,
60    O2: Clone + PartialEq + Debug,
61    A2: Clone,
62>(
63    this: &T,
64) -> StrictOptic<
65    DynFunctor<Fwd<T, O1, A1, O2, A2>, O1, A1, O2, A2>,
66    DynFunctor<Rev<T, O1, A1, O2, A2>, O1, A1, O2, A2>,
67    VecKind,
68    O1,
69    A1,
70    O2,
71    A2,
72> {
73    let fwd = to_dyn_functor(Fwd::new(this.clone()));
74    let rev = to_dyn_functor(Rev::new(this.clone()));
75
76    // Clone self to avoid lifetime issues in the closure
77    let self_clone = this.clone();
78
79    StrictOptic::new(
80        fwd,
81        rev,
82        Box::new(move |ops: &Operations<VecKind, O1, A1>| {
83            let mut sources_vec = Vec::new();
84            let mut residuals = Vec::new();
85
86            for (op, _, _) in ops.iter() {
87                let m = self_clone.residual(op);
88                sources_vec.push(m.len());
89                residuals.extend(m);
90            }
91
92            let sources = SemifiniteFunction::<VecKind, usize>(VecArray(sources_vec));
93            let values = SemifiniteFunction(VecArray(residuals));
94            IndexedCoproduct::from_semifinite(sources, values).unwrap()
95        }),
96    )
97}
98
99////////////////////////////////////////////////////////////////////////////////
100// Fwd and Rev lax functor helpers, needed for Optic
101// **IMPORTANT NOTE**: never expose these in the public API.
102// They rely on never having their `map_arrow` methods called, and panic! in that case.
103
104#[derive(Clone, PartialEq)]
105struct Fwd<T, O1, A1, O2, A2> {
106    _phantom: std::marker::PhantomData<(O1, A1, O2, A2)>,
107    optic: Box<T>,
108}
109
110impl<
111        T: Optic<O1, A1, O2, A2>,
112        O1: Clone + PartialEq,
113        A1: Clone,
114        O2: Clone + PartialEq + Debug,
115        A2: Clone,
116    > Functor<O1, A1, O2, A2> for Fwd<T, O1, A1, O2, A2>
117{
118    fn map_object(&self, o: &O1) -> impl ExactSizeIterator<Item = O2> {
119        self.optic.fwd_object(o).into_iter()
120    }
121
122    fn map_operation(&self, a: &A1, source: &[O1], target: &[O1]) -> OpenHypergraph<O2, A2> {
123        self.optic.fwd_operation(a, source, target)
124    }
125
126    // NOTE: this method is never called; and the struct *must* remain private.
127    fn map_arrow(&self, _f: &OpenHypergraph<O1, A1>) -> OpenHypergraph<O2, A2> {
128        panic!("Fwd is not a functor!");
129    }
130}
131
132impl<
133        T: Optic<O1, A1, O2, A2>,
134        O1: Clone + PartialEq,
135        A1: Clone,
136        O2: Clone + PartialEq + Debug,
137        A2: Clone,
138    > Fwd<T, O1, A1, O2, A2>
139{
140    fn new(t: T) -> Self {
141        Self {
142            _phantom: std::marker::PhantomData,
143            optic: Box::new(t),
144        }
145    }
146}
147
148#[derive(Clone, PartialEq)]
149struct Rev<T, O1, A1, O2, A2> {
150    _phantom: std::marker::PhantomData<(O1, A1, O2, A2)>,
151    optic: Box<T>,
152}
153
154impl<
155        T: Optic<O1, A1, O2, A2>,
156        O1: Clone + PartialEq,
157        A1: Clone,
158        O2: Clone + PartialEq + Debug,
159        A2: Clone,
160    > Functor<O1, A1, O2, A2> for Rev<T, O1, A1, O2, A2>
161{
162    fn map_object(&self, o: &O1) -> impl ExactSizeIterator<Item = O2> {
163        self.optic.rev_object(o).into_iter()
164    }
165
166    fn map_operation(&self, a: &A1, source: &[O1], target: &[O1]) -> OpenHypergraph<O2, A2> {
167        self.optic.rev_operation(a, source, target)
168    }
169
170    // NOTE: this method is never called; and the struct *must* remain private.
171    fn map_arrow(&self, _f: &OpenHypergraph<O1, A1>) -> OpenHypergraph<O2, A2> {
172        panic!("Rev is not a functor!");
173    }
174}
175
176impl<
177        T: Optic<O1, A1, O2, A2>,
178        O1: Clone + PartialEq,
179        A1: Clone,
180        O2: Clone + PartialEq + Debug,
181        A2: Clone,
182    > Rev<T, O1, A1, O2, A2>
183{
184    fn new(t: T) -> Self {
185        Self {
186            _phantom: std::marker::PhantomData,
187            optic: Box::new(t),
188        }
189    }
190}