qmc/sse/qmc_traits/
op_container.rs

1#[cfg(feature = "serialize")]
2use serde::{Deserialize, Serialize};
3use std::fmt::Debug;
4use std::iter::FromIterator;
5
6/// Ops for holding SSE graph state.
7pub trait Op: Clone + Debug {
8    /// The list of op variables.
9    type Vars: FromIterator<usize> + AsRef<[usize]> + AsMut<[usize]> + Debug;
10    /// The list of op input and output states.
11    type SubState: FromIterator<bool> + AsRef<[bool]> + AsMut<[bool]> + Debug;
12
13    /// Make a diagonal op.
14    fn diagonal<A, B>(vars: A, bond: usize, state: B, constant: bool) -> Self
15    where
16        A: Into<Self::Vars>,
17        B: Into<Self::SubState>;
18
19    /// Make an offdiagonal op.
20    fn offdiagonal<A, B, C>(vars: A, bond: usize, inputs: B, outputs: C, constant: bool) -> Self
21    where
22        A: Into<Self::Vars>,
23        B: Into<Self::SubState>,
24        C: Into<Self::SubState>;
25
26    /// Make vars (this is here mostly due to rust bug 38078)
27    fn make_vars<V>(vars: V) -> Self::Vars
28    where
29        V: IntoIterator<Item = usize>,
30    {
31        vars.into_iter().collect()
32    }
33
34    /// Make substate (this is here mostly due to rust bug 38078)
35    fn make_substate<S>(state: S) -> Self::SubState
36    where
37        S: IntoIterator<Item = bool>,
38    {
39        state.into_iter().collect()
40    }
41
42    /// Get the relative index of a variable.
43    fn index_of_var(&self, var: usize) -> Option<usize>;
44
45    /// Check if the op is diagonal (makes no state changes).
46    fn is_diagonal(&self) -> bool {
47        self.get_inputs() == self.get_outputs()
48    }
49
50    /// Get the set of variables used for this op.
51    fn get_vars(&self) -> &[usize];
52
53    /// Get the associated bond number for the op.
54    fn get_bond(&self) -> usize;
55
56    /// Get the input state for the op.
57    fn get_inputs(&self) -> &[bool];
58
59    /// Get the output state for the op.
60    fn get_outputs(&self) -> &[bool];
61
62    /// Clone the op and edit inputs and outputs.
63    fn clone_and_edit_in_out<F>(&self, f: F) -> Self
64    where
65        F: Fn(&mut [bool], &mut [bool]);
66
67    /// Clone the op and edit inputs and outputs. Must edit states symmetrically (diagonal stays
68    /// diagonal, offdiagonal stays offdiagonal).
69    fn clone_and_edit_in_out_symmetric<F>(&self, f: F) -> Self
70    where
71        F: Fn(&mut [bool]);
72
73    /// Edit inputs and outputs.
74    fn edit_in_out<F>(&mut self, f: F)
75    where
76        F: Fn(&mut [bool], &mut [bool]);
77
78    /// Edit inputs and outputs. Must edit states symmetrically (diagonal stays
79    /// diagonal, offdiagonal stays offdiagonal).
80    fn edit_in_out_symmetric<F>(&mut self, f: F)
81    where
82        F: Fn(&mut [bool]);
83
84    /// Get the input state for the op.
85    fn clone_inputs(&self) -> Self::SubState;
86
87    /// Get the output state for the op.
88    fn clone_outputs(&self) -> Self::SubState;
89
90    /// If the op is always a constant under any bit flip in input or output, then it can be used
91    /// to mark the edges of clusters.
92    fn is_constant(&self) -> bool;
93}
94
95/// A node for a loop updater to contain ops.
96pub trait OpNode<O: Op> {
97    /// Get the contained up
98    fn get_op(&self) -> O;
99    /// Get a reference to the contained op
100    fn get_op_ref(&self) -> &O;
101    /// Get a mutable reference to the contained op.
102    fn get_op_mut(&mut self) -> &mut O;
103}
104
105/// The ability to construct a new OpContainer
106pub trait OpContainerConstructor {
107    /// Make a new container for nvars.
108    fn new(nvars: usize) -> Self;
109    /// Make a new container for nvars giving a hint as to the number of bonds.
110    fn new_with_bonds(nvars: usize, nbonds: usize) -> Self;
111}
112
113/// Contain and manage ops.
114pub trait OpContainer {
115    /// The op object to manage.
116    type Op: Op;
117
118    /// Get the cutoff for this container.
119    fn get_cutoff(&self) -> usize;
120    /// Set the cutoff for this container.
121    fn set_cutoff(&mut self, cutoff: usize);
122    /// Get the number of non-identity ops.
123    fn get_n(&self) -> usize;
124    /// Get the number of managed variables.
125    fn get_nvars(&self) -> usize;
126    /// Get the pth op, None is identity.
127    fn get_pth(&self, p: usize) -> Option<&Self::Op>;
128    /// Gets the count of `bond` ops in the graph.
129    fn get_count(&self, bond: usize) -> usize;
130
131    /// Iterate through the imaginary time states of the opcontainer.
132    fn itime_fold<F, T>(&self, state: &mut [bool], fold_fn: F, init: T) -> T
133    where
134        F: Fn(T, &[bool]) -> T;
135
136    /// Verify the integrity of the OpContainer.
137    fn verify(&self, state: &[bool]) -> bool {
138        let mut rolling_state = state.to_vec();
139        for p in 0..self.get_cutoff() {
140            let op = self.get_pth(p);
141            if let Some(op) = op {
142                for (v, inp) in op.get_vars().iter().zip(op.get_inputs().iter()) {
143                    if rolling_state[*v] != *inp {
144                        return false;
145                    }
146                }
147                op.get_vars()
148                    .iter()
149                    .zip(op.get_outputs().iter())
150                    .for_each(|(v, out)| {
151                        rolling_state[*v] = *out;
152                    })
153            }
154        }
155        rolling_state
156            .into_iter()
157            .zip(state.iter().cloned())
158            .all(|(a, b)| a == b)
159    }
160}
161
162/// Holds op inputs and outputs as diagonal or offdiagonal.
163#[derive(Clone, Debug, PartialEq, Eq)]
164#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
165pub enum OpType<SubState>
166where
167    SubState: Clone + Debug + FromIterator<bool> + AsRef<[bool]> + AsMut<[bool]>,
168{
169    /// A diagonal op.
170    Diagonal(SubState),
171    /// An offdiagonal op.
172    Offdiagonal(SubState, SubState),
173}
174
175impl<SubState> OpType<SubState>
176where
177    SubState: Clone + Debug + FromIterator<bool> + AsRef<[bool]> + AsMut<[bool]>,
178{
179    fn edit_states<F>(&mut self, f: F)
180    where
181        F: Fn(&mut [bool], &mut [bool]),
182    {
183        let (inputs, outputs) = match self {
184            OpType::Diagonal(state) => {
185                let mut inputs = state.clone();
186                let mut outputs = state.clone();
187                f(inputs.as_mut(), outputs.as_mut());
188                (inputs, outputs)
189            }
190            OpType::Offdiagonal(inputs, outputs) => {
191                let mut inputs = inputs.clone();
192                let mut outputs = outputs.clone();
193                f(inputs.as_mut(), outputs.as_mut());
194                (inputs, outputs)
195            }
196        };
197
198        *self = if inputs.as_ref() == outputs.as_ref() {
199            Self::Diagonal(inputs)
200        } else {
201            Self::Offdiagonal(inputs, outputs)
202        };
203    }
204
205    fn edit_states_symmetric<F>(&mut self, f: F)
206    where
207        F: Fn(&mut [bool]),
208    {
209        match self {
210            OpType::Diagonal(state) => {
211                f(state.as_mut());
212            }
213            OpType::Offdiagonal(inputs, outputs) => {
214                f(inputs.as_mut());
215                f(outputs.as_mut());
216            }
217        };
218    }
219}
220
221/// An standard op which covers a number of variables and changes the state from input to output.
222#[derive(Clone, Debug, PartialEq, Eq)]
223#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
224pub struct BasicOp<Vars, SubState>
225where
226    Vars: FromIterator<usize> + AsRef<[usize]> + AsMut<[usize]> + Clone + Debug,
227    SubState: FromIterator<bool> + AsRef<[bool]> + AsMut<[bool]> + Clone + Debug,
228{
229    /// Variables involved in op
230    vars: Vars,
231    /// Bond number (index of op)
232    bond: usize,
233    /// Input and Output state.
234    in_out: OpType<SubState>,
235    /// Is this op constant under bit flips?
236    constant: bool,
237}
238
239impl<Vars, SubState> Op for BasicOp<Vars, SubState>
240where
241    Vars: FromIterator<usize> + AsRef<[usize]> + AsMut<[usize]> + Clone + Debug,
242    SubState: FromIterator<bool> + AsRef<[bool]> + AsMut<[bool]> + Clone + Debug,
243{
244    type Vars = Vars;
245    type SubState = SubState;
246
247    fn diagonal<A, B>(vars: A, bond: usize, state: B, constant: bool) -> Self
248    where
249        A: Into<Self::Vars>,
250        B: Into<Self::SubState>,
251    {
252        Self {
253            vars: vars.into(),
254            bond,
255            in_out: OpType::Diagonal(state.into()),
256            constant,
257        }
258    }
259
260    fn offdiagonal<A, B, C>(vars: A, bond: usize, inputs: B, outputs: C, constant: bool) -> Self
261    where
262        A: Into<Self::Vars>,
263        B: Into<Self::SubState>,
264        C: Into<Self::SubState>,
265    {
266        Self {
267            vars: vars.into(),
268            bond,
269            in_out: OpType::Offdiagonal(inputs.into(), outputs.into()),
270            constant,
271        }
272    }
273
274    fn is_diagonal(&self) -> bool {
275        matches!(&self.in_out, OpType::Diagonal(_))
276    }
277
278    fn index_of_var(&self, var: usize) -> Option<usize> {
279        let res = self
280            .vars
281            .as_ref()
282            .iter()
283            .enumerate()
284            .try_for_each(|(indx, v)| if *v == var { Err(indx) } else { Ok(()) });
285        match res {
286            Ok(_) => None,
287            Err(v) => Some(v),
288        }
289    }
290
291    fn get_vars(&self) -> &[usize] {
292        self.vars.as_ref()
293    }
294
295    fn get_bond(&self) -> usize {
296        self.bond
297    }
298
299    fn get_inputs(&self) -> &[bool] {
300        match &self.in_out {
301            OpType::Diagonal(state) => state.as_ref(),
302            OpType::Offdiagonal(state, _) => state.as_ref(),
303        }
304    }
305
306    fn get_outputs(&self) -> &[bool] {
307        match &self.in_out {
308            OpType::Diagonal(state) => state.as_ref(),
309            OpType::Offdiagonal(_, state) => state.as_ref(),
310        }
311    }
312
313    fn clone_and_edit_in_out<F>(&self, f: F) -> Self
314    where
315        F: Fn(&mut [bool], &mut [bool]),
316    {
317        let (mut inputs, mut outputs) = match &self.in_out {
318            OpType::Diagonal(state) => {
319                let inputs = state.clone();
320                let outputs = state.clone();
321                (inputs, outputs)
322            }
323            OpType::Offdiagonal(inputs, outputs) => {
324                let inputs = inputs.clone();
325                let outputs = outputs.clone();
326                (inputs, outputs)
327            }
328        };
329        f(inputs.as_mut(), outputs.as_mut());
330        let all_eq = inputs.as_ref() == outputs.as_ref();
331        let in_out = if all_eq {
332            OpType::Diagonal(inputs)
333        } else {
334            OpType::Offdiagonal(inputs, outputs)
335        };
336        Self {
337            vars: self.vars.clone(),
338            bond: self.bond,
339            in_out,
340            constant: self.constant,
341        }
342    }
343
344    fn clone_and_edit_in_out_symmetric<F>(&self, f: F) -> Self
345    where
346        F: Fn(&mut [bool]),
347    {
348        let in_out = match &self.in_out {
349            OpType::Diagonal(state) => {
350                let mut inputs = state.clone();
351                f(inputs.as_mut());
352                OpType::Diagonal(inputs)
353            }
354            OpType::Offdiagonal(inputs, outputs) => {
355                let mut inputs = inputs.clone();
356                let mut outputs = outputs.clone();
357                f(inputs.as_mut());
358                f(outputs.as_mut());
359                OpType::Offdiagonal(inputs, outputs)
360            }
361        };
362        Self {
363            vars: self.vars.clone(),
364            bond: self.bond,
365            in_out,
366            constant: self.constant,
367        }
368    }
369
370    fn clone_inputs(&self) -> Self::SubState {
371        match &self.in_out {
372            OpType::Diagonal(state) => state.clone(),
373            OpType::Offdiagonal(state, _) => state.clone(),
374        }
375    }
376
377    fn clone_outputs(&self) -> Self::SubState {
378        match &self.in_out {
379            OpType::Diagonal(state) => state.clone(),
380            OpType::Offdiagonal(_, state) => state.clone(),
381        }
382    }
383
384    fn is_constant(&self) -> bool {
385        self.constant
386    }
387
388    fn edit_in_out<F>(&mut self, f: F)
389    where
390        F: Fn(&mut [bool], &mut [bool]),
391    {
392        self.in_out.edit_states(f)
393    }
394
395    fn edit_in_out_symmetric<F>(&mut self, f: F)
396    where
397        F: Fn(&mut [bool]),
398    {
399        self.in_out.edit_states_symmetric(f)
400    }
401}