libreda_logic/networks/
aig.rs

1// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5//! And-inverter (AIG) graph data structures.
6
7use crate::{
8    helpers::sort2,
9    network::*,
10    networks::generic_network::*,
11    truth_table::small_lut::{truth_table_library, SmallTruthTable},
12};
13
14use super::SimplifyResult;
15
16/// And-inverter graph.
17///
18/// A logic network consisting of three-input majority nodes.
19///
20/// # Examples
21/// ```
22/// use libreda_logic::traits::*;
23/// use libreda_logic::network::*;
24/// use libreda_logic::networks::aig::*;
25///
26/// // Create an empty network.
27/// let mut aig = Aig::new();
28///
29/// // Create two inputs.
30/// let a = aig.create_primary_input();
31/// let b = aig.create_primary_input();
32///
33/// // Create the boolean AND of the two inputs.
34/// let a_and_b = aig.create_and(a, b);
35/// ```
36pub type Aig = LogicNetwork<AigNode>;
37
38/// Identifier for nodes in the and-inverter graph.
39pub type AigNodeId = NodeId;
40
41/// Node in the and-inverter graph.
42#[derive(Clone, Copy, Debug, Hash, PartialEq, PartialOrd, Eq, Ord)]
43pub struct AigNode {
44    pub(super) a: AigNodeId,
45    pub(super) b: AigNodeId,
46    /// Number of nodes which reference to this node.
47    num_references: usize,
48}
49
50impl AigNode {
51    /// Create a new 2-input AND node.
52    fn new([a, b]: [AigNodeId; 2]) -> Self {
53        Self {
54            a,
55            b,
56            num_references: 0,
57        }
58    }
59
60    /// Get the child node IDs as an array.
61    fn to_array(self) -> [AigNodeId; 2] {
62        [self.a, self.b]
63    }
64
65    /// Get an array of mutable references to the node inputs.
66    fn to_array_mut(&mut self) -> [&mut AigNodeId; 2] {
67        [&mut self.a, &mut self.b]
68    }
69}
70
71impl NetworkNode for AigNode {
72    type NodeId = AigNodeId;
73
74    fn num_inputs(&self) -> usize {
75        2
76    }
77
78    fn get_input(&self, i: usize) -> Self::NodeId {
79        match i {
80            0 => self.a,
81            1 => self.b,
82            _ => panic!("index out of bounds"),
83        }
84    }
85
86    fn function(&self) -> SmallTruthTable {
87        truth_table_library::and2()
88    }
89
90    fn normalized(self) -> SimplifyResult<Self, Self::NodeId> {
91        Aig::simplify_node(self)
92    }
93}
94
95impl MutNetworkNode for AigNode {
96    fn set_input(&mut self, i: usize, signal: Self::NodeId) {
97        let input = match i {
98            0 => &mut self.a,
99            1 => &mut self.b,
100            _ => panic!("index out of bounds"),
101        };
102
103        *input = signal;
104    }
105}
106
107impl NetworkNodeWithReferenceCount for AigNode {
108    fn num_references(&self) -> usize {
109        self.num_references
110    }
111}
112
113impl MutNetworkNodeWithReferenceCount for AigNode {
114    fn reference(&mut self) {
115        self.num_references += 1;
116    }
117
118    fn dereference(&mut self) {
119        self.num_references -= 1;
120    }
121}
122
123impl IntoIterator for AigNode {
124    type Item = AigNodeId;
125
126    type IntoIter = std::array::IntoIter<AigNodeId, 2>;
127
128    fn into_iter(self) -> Self::IntoIter {
129        // Sanity check: inputs must be sorted.
130        debug_assert_eq!(
131            self.to_array(),
132            sort2(self.to_array()),
133            "inputs must be sorted"
134        );
135
136        self.to_array().into_iter()
137    }
138}
139
140impl Aig {
141    /// Simplify the node without making changes to the graph.
142    ///
143    /// Either returns the ID of an existing node which is equivalent to the given node,
144    /// or returns a node with simplified input, or returns the unmodified node.
145    fn simplify_node(node: AigNode) -> SimplifyResult<AigNode, NodeId> {
146        SimplifyResult::new_node(node)
147            // Use majority rule
148            .and_then(Self::simplify_node_by_majority)
149            .and_then(Self::simplify_constants)
150            .map_unsimplified(Self::normalize_node_by_commutativity) // TODO: Is this necessary? Might be done in simplify_node_with_hashtable
151    }
152
153    /// Sort the inputs of the node.
154    /// Use the rule `AND(x, y) == AND(y, x)``.
155    fn normalize_node_by_commutativity(node: AigNode) -> AigNode {
156        let [a, b] = sort2(node.to_array());
157        AigNode { a, b, ..node }
158    }
159
160    /// Simplify if both operands are equal.
161    fn simplify_node_by_majority(node: AigNode) -> SimplifyResult<AigNode, NodeId> {
162        let [a, b] = [node.a, node.b];
163        match [a, b] {
164            // And(x, x) => x
165            [x, y] if x == y => SimplifyResult::new_id(x),
166            // And(x, x') => false
167            [x, y] if x == y.invert() => SimplifyResult::new_id(AigNodeId::zero()),
168            _ => SimplifyResult::new_node(node),
169        }
170    }
171
172    /// Simplify `And(x, 0) = 0` and `And(x, 1) = x`.
173    fn simplify_constants(node: AigNode) -> SimplifyResult<AigNode, NodeId> {
174        let [a, b] = node.to_array();
175        match [a, b] {
176            [x, _] | [_, x] if x.is_zero() => SimplifyResult::new_id(AigNodeId::zero()),
177            [x, y] | [y, x] if x.is_one() => SimplifyResult::new_id(y),
178            _ => SimplifyResult::new_node(node),
179        }
180    }
181}
182
183impl HomogeneousNetwork for Aig {
184    const NUM_NODE_INPUTS: usize = 2;
185
186    fn function(&self) -> Self::NodeFunction {
187        crate::truth_table::small_lut::truth_table_library::and2()
188    }
189}
190
191impl SubstituteInNode for Aig {
192    fn substitute_in_node(
193        &mut self,
194        node: Self::NodeId,
195        old_signal: Self::Signal,
196        new_signal: Self::Signal,
197    ) {
198        if let Some(n) = self.get_logic_node_mut(node) {
199            // Replace the each occurrence of the old signal with the new signal.
200            n.to_array_mut()
201                .into_iter()
202                .filter(|input| **input == old_signal)
203                .for_each(|input| *input = new_signal);
204        }
205    }
206}
207
208impl UnaryOp for Aig {
209    fn create_not(&mut self, signal: Self::Signal) -> Self::Signal {
210        signal.invert()
211    }
212}
213
214impl BinaryOp for Aig {
215    fn create_and(&mut self, a: Self::Signal, b: Self::Signal) -> Self::Signal {
216        let node = AigNode::new([a, b]);
217        // TODO: doing double the work. Normalization is already called by `create_node`
218        match Self::simplify_node(node) {
219            SimplifyResult::Node(n, invert) => self.create_node(n).invert_if(invert),
220            SimplifyResult::Simplified(s, invert) => s.invert_if(invert),
221        }
222    }
223
224    fn create_or(&mut self, a: Self::Signal, b: Self::Signal) -> Self::Signal {
225        self.create_and(a.invert(), b.invert()).invert()
226    }
227
228    fn create_nand(&mut self, a: Self::Signal, b: Self::Signal) -> Self::Signal {
229        self.create_and(a, b).invert()
230    }
231
232    fn create_nor(&mut self, a: Self::Signal, b: Self::Signal) -> Self::Signal {
233        self.create_or(a, b).invert()
234    }
235
236    fn create_xor(&mut self, a: Self::Signal, b: Self::Signal) -> Self::Signal {
237        let x = self.create_and(a, b.invert());
238        let y = self.create_and(a.invert(), b);
239        self.create_or(x, y)
240    }
241}
242
243impl TernaryOp for Aig {}
244
245#[test]
246fn test_aig_create_constants() {
247    let aig = Aig::new();
248
249    let zero = aig.get_constant(false);
250    let one = aig.get_constant(true);
251
252    assert!(zero.is_constant());
253    assert!(one.is_constant());
254
255    assert!(zero.is_zero());
256
257    assert_ne!(zero, one);
258    assert_eq!(zero.invert(), one);
259}
260
261#[test]
262fn test_aig_create_primary_inputs() {
263    let mut aig = Aig::new();
264    let a = aig.create_primary_input();
265    let b = aig.create_primary_input();
266    let c = aig.create_primary_input();
267
268    assert_ne!(a, b);
269    assert_ne!(b, c);
270
271    assert!(aig.is_input(a));
272    assert!(aig.is_input(b));
273    assert!(aig.is_input(c));
274
275    assert!(!aig.is_constant(a));
276}
277
278#[test]
279fn test_aig_node_deduplication() {
280    let mut aig = Aig::new();
281
282    let a = aig.create_primary_input();
283    let b = aig.create_primary_input();
284
285    let a_and_b_1 = aig.create_and(a, b);
286    let a_and_b_2 = aig.create_and(a, b);
287
288    assert_eq!(a_and_b_1, a_and_b_2);
289}
290
291#[test]
292fn test_aig_node_simplification_by_commutativity() {
293    let mut aig = Aig::new();
294
295    let a = aig.create_primary_input();
296    let b = aig.create_primary_input();
297
298    let a_and_b_1 = aig.create_and(a, b);
299    let a_and_b_2 = aig.create_and(b, a);
300
301    assert_eq!(a_and_b_1, a_and_b_2);
302}
303
304#[test]
305fn test_aig_node_simplification_by_majority() {
306    let mut aig = Aig::new();
307
308    let a = aig.create_primary_input();
309
310    let a_and_a = aig.create_and(a, a);
311    assert_eq!(a_and_a, a);
312}
313
314#[test]
315fn test_aig_node_simplification_with_constants() {
316    let mut aig = Aig::new();
317
318    let a = aig.create_primary_input();
319
320    let zero = aig.get_constant(false);
321
322    let a_and_zero = aig.create_and(a, zero);
323
324    assert_eq!(a_and_zero, zero);
325}
326
327#[test]
328fn test_aig_simulation() {
329    use crate::native_boolean_functions::NativeBooleanFunction;
330    use crate::traits::BooleanSystem;
331
332    // Construct a one-bit full adder.
333    let mut aig = Aig::new();
334    let [in1, in2, carry_in] = aig.create_primary_inputs();
335
336    let sum = aig.create_xor3(in1, in2, carry_in);
337    let carry = aig.create_maj3(in1, in2, carry_in);
338
339    let output_sum = aig.create_primary_output(sum);
340    let output_carry = aig.create_primary_output(carry);
341
342    let simulator = crate::network_simulator::RecursiveSim::new(&aig);
343
344    // Reference model of the full adder.
345    fn full_adder([a, b, c]: [bool; 3]) -> [bool; 2] {
346        let sum = (a as usize) + (b as usize) + (c as usize);
347        [
348            sum & 0b1 == 1,
349            sum & 0b10 == 0b10, // carry
350        ]
351    }
352
353    let reference = NativeBooleanFunction::new(full_adder);
354
355    for i in 0..(1 << 3) {
356        let inputs = [0, 1, 2].map(|idx| (i >> idx) & 1 == 1);
357
358        let exptected_output = [0, 1].map(|out| reference.evaluate_term(&out, &inputs));
359        let actual_output: Vec<_> = simulator
360            .simulate(&[output_sum, output_carry], &inputs)
361            .collect();
362
363        dbg!(inputs);
364
365        assert_eq!(exptected_output.as_slice(), actual_output.as_slice());
366    }
367}