1use std::collections::HashMap;
4
5use crate::circuit::{Circuit, Comparison, Macro, OpKind};
6use crate::wire::{Lin, Term, Wire};
7
8impl Circuit {
9 pub fn inline_circuit(
16 &mut self,
17 source: &Circuit,
18 source_wires: &[Wire],
19 allow_existing_readers: bool,
20 ) -> Vec<Wire> {
21 assert_eq!(source_wires.len(), source.source_size(), "inlined source mapping has the wrong length");
22 let mut map: Vec<Wire> = source_wires.to_vec();
23 let declare_range = |circuit: &mut Circuit, wire: Wire, min: f64, max: f64| {
24 if allow_existing_readers {
25 circuit.assume_range_allowing_readers(wire, min, max);
26 } else {
27 circuit.assume_range(wire, min, max);
28 }
29 };
30 let declare_grid = |circuit: &mut Circuit, wire: Wire, step: f64| {
31 if allow_existing_readers {
32 circuit.assume_grid_allowing_readers(wire, step);
33 } else {
34 circuit.assume_grid(wire, step);
35 }
36 };
37 for (cell, &to) in source_wires.iter().enumerate() {
38 let from = Wire::from_index(cell);
39 let range = source.range(from);
40 declare_range(self, to, range.min, range.max);
41 if let Some(grid) = source.grid(from) {
42 declare_grid(self, to, grid);
43 }
44 if source.is_exact_bit(from) {
45 declare_range(self, to, 0.0, 1.0);
46 self.mark_exact_bit(to);
47 }
48 }
49 let mut mapped_tables: HashMap<usize, Vec<Wire>> = HashMap::new();
51 for (operation_index, operation) in source.operations().iter().enumerate() {
52 let map_terms = |terms: &[Term]| -> Vec<Term> {
53 terms.iter().map(|&(wire, coefficient)| (map[wire.index()], coefficient)).collect()
54 };
55 let wire = match (operation.macro_op, operation.kind) {
56 (Some(Macro::Multiply { left, right }), _) => self.multiply(map[left.index()], map[right.index()]),
57 (Some(Macro::Select { gate, off, on }), _) => {
58 self.select(map[gate.index()], map[off.index()], map[on.index()])
59 }
60 (None, OpKind::Reglu) => self.append(
61 OpKind::Reglu,
62 Lin::new(map_terms(&operation.left_terms), operation.left_bias),
63 Lin::new(map_terms(&operation.gate_terms), operation.gate_bias),
64 operation.preserve_rounding,
65 None,
66 ),
67 (None, OpKind::Index) => {
68 let read = operation.index_read.as_ref().unwrap();
69 let candidates = mapped_tables
70 .entry(read.table)
71 .or_insert_with(|| {
72 source.tables[read.table].candidates.iter().map(|c| map[c.index()]).collect()
73 })
74 .clone();
75 self.index_read_affine(
77 Lin::new(map_terms(&operation.gate_terms), operation.gate_bias),
78 &candidates,
79 read.below,
80 read.above,
81 Some(read.index_range),
82 )
83 }
84 (None, OpKind::Boolean) if operation.preserve_rounding => self.append(
86 OpKind::Boolean,
87 Lin::new(map_terms(&operation.left_terms), operation.left_bias),
88 Lin::default(),
89 true,
90 None,
91 ),
92 (None, kind) => {
93 let form = Lin::new(map_terms(&operation.left_terms), operation.left_bias);
94 let value = if operation.preserve_rounding { self.round_affine(form) } else { self.affine(form) };
95 if kind == OpKind::Boolean { self.boolean(value) } else { value }
96 }
97 };
98 let from = Wire::from_index(source.source_size() + operation_index);
99 let range = source.range(from);
100 declare_range(self, wire, range.min, range.max);
101 if let Some(grid) = source.grid(from) {
102 declare_grid(self, wire, grid);
103 }
104 if let Some(index) = wire.index().checked_sub(self.source_size()) {
106 if let Some(kind) = operation.rounding_point {
107 self.record_rounding_point(wire, kind);
108 }
109 if operation.comparison_bit {
111 self.operations[index].comparison_bit = true;
112 }
113 if let Some(comparison) = &operation.comparison {
114 self.operations[index].comparison = Some(Comparison {
115 terms: comparison
116 .terms
117 .iter()
118 .map(|&(wire, coefficient)| (map[wire.index()], coefficient))
119 .collect(),
120 bias: comparison.bias,
121 equal: comparison.equal,
122 });
123 }
124 }
125 map.push(wire);
126 }
127 for group in source.one_hot_groups() {
129 let members: Vec<Wire> = group.members.iter().map(|member| map[member.index()]).collect();
130 if allow_existing_readers {
131 self.assume_one_hot_allowing_readers(&members, group.allow_all_zero);
132 } else {
133 self.assume_one_hot(&members, group.allow_all_zero);
134 }
135 }
136 map
137 }
138}