use std::collections::HashMap;
use crate::circuit::{Circuit, Macro, OpKind};
use crate::wire::{Lin, Term, Wire};
impl Circuit {
pub fn inline_circuit(
&mut self,
source: &Circuit,
source_wires: &[Wire],
allow_existing_readers: bool,
) -> Vec<Wire> {
assert_eq!(source_wires.len(), source.source_size(), "inlined source mapping has the wrong length");
let mut map: Vec<Wire> = source_wires.to_vec();
let declare_range = |circuit: &mut Circuit, wire: Wire, min: f64, max: f64| {
if allow_existing_readers {
circuit.assume_range_allowing_readers(wire, min, max);
} else {
circuit.assume_range(wire, min, max);
}
};
let declare_grid = |circuit: &mut Circuit, wire: Wire, step: f64| {
if allow_existing_readers {
circuit.assume_grid_allowing_readers(wire, step);
} else {
circuit.assume_grid(wire, step);
}
};
for (cell, &to) in source_wires.iter().enumerate() {
let from = Wire::from_index(cell);
let range = source.range(from);
declare_range(self, to, range.min, range.max);
if let Some(grid) = source.grid(from) {
declare_grid(self, to, grid);
}
if source.is_exact_bit(from) {
declare_range(self, to, 0.0, 1.0);
self.mark_exact_bit(to);
}
}
let mut mapped_tables: HashMap<usize, Vec<Wire>> = HashMap::new();
for (operation_index, operation) in source.operations().iter().enumerate() {
let map_terms = |terms: &[Term]| -> Vec<Term> {
terms.iter().map(|&(wire, coefficient)| (map[wire.index()], coefficient)).collect()
};
let wire = match (operation.macro_op, operation.kind) {
(Some(Macro::Multiply { left, right }), _) => self.multiply(map[left.index()], map[right.index()]),
(Some(Macro::Select { gate, off, on }), _) => {
self.select(map[gate.index()], map[off.index()], map[on.index()])
}
(None, OpKind::Reglu) => self.append(
OpKind::Reglu,
Lin::new(map_terms(&operation.left_terms), operation.left_bias),
Lin::new(map_terms(&operation.gate_terms), operation.gate_bias),
operation.preserve_rounding,
None,
),
(None, OpKind::Index) => {
let read = operation.index_read.as_ref().unwrap();
let candidates = mapped_tables
.entry(read.table)
.or_insert_with(|| {
source.tables[read.table].candidates.iter().map(|c| map[c.index()]).collect()
})
.clone();
self.index_read_affine(
Lin::new(map_terms(&operation.gate_terms), operation.gate_bias),
&candidates,
read.below,
read.above,
Some(read.index_range),
)
}
(None, OpKind::Boolean) if operation.preserve_rounding => self.append(
OpKind::Boolean,
Lin::new(map_terms(&operation.left_terms), operation.left_bias),
Lin::default(),
true,
None,
),
(None, kind) => {
let form = Lin::new(map_terms(&operation.left_terms), operation.left_bias);
let value = if operation.preserve_rounding { self.round_affine(form) } else { self.affine(form) };
if kind == OpKind::Boolean { self.boolean(value) } else { value }
}
};
let from = Wire::from_index(source.source_size() + operation_index);
let range = source.range(from);
declare_range(self, wire, range.min, range.max);
if let Some(grid) = source.grid(from) {
declare_grid(self, wire, grid);
}
if let Some(index) = wire.index().checked_sub(self.source_size()) {
if let Some(kind) = operation.rounding_point {
self.record_rounding_point(wire, kind);
}
if operation.comparison_bit {
self.operations[index].comparison_bit = true;
}
}
map.push(wire);
}
for group in source.one_hot_groups() {
let members: Vec<Wire> = group.members.iter().map(|member| map[member.index()]).collect();
if allow_existing_readers {
self.assume_one_hot_allowing_readers(&members, group.allow_all_zero);
} else {
self.assume_one_hot(&members, group.allow_all_zero);
}
}
map
}
}