th_rust/circuit/
circuit.rs

1use na::{DMatrix};
2use crate::gate::Gate;
3
4pub struct Circuit {
5	pub gates: Vec<Box<dyn Gate>>,
6	pub weight: DMatrix<i32>,
7	pub bias: Vec<i32>,
8}
9
10impl Circuit {
11	pub fn new() -> Circuit {
12		Circuit { gates: Vec::new(), weight: DMatrix::<i32>::from_vec(1, 1, vec![0]), bias: Vec::new() }
13	}
14
15	pub fn from_vector(gates: Vec<Box<dyn Gate>>) -> Circuit {
16		Circuit { gates, weight: DMatrix::<i32>::from_vec(1, 1, vec![0]), bias: Vec::new() }
17	}
18
19	pub fn append(&mut self, gate: Box<dyn Gate>) {
20		self.gates.push(gate);
21	}
22
23	pub fn compile(&mut self) {
24		// Compose circuit, nothing to do if size is 1
25		if self.gates.len() == 1 {
26			self.weight = self.gates[0].weight().clone();
27			self.bias = self.gates[0].bias().clone();
28			return;
29		}
30
31		// Compute the grid space
32		let (max_row, max_col) = self.grid_space();
33		let to_linear = |row: usize, col: usize| -> usize {
34			row * (max_col + 1) + col
35		};
36
37		// Compilation parameters
38		let mut visited = vec![false; (max_row + 1) * (max_col + 1)];
39		let mut weight: DMatrix<i32> = self.gates[0].weight().clone();
40		let mut bias: Vec<i32> = self.gates[0].bias().clone();
41
42
43		// Visit each gate
44		for gate in self.gates.iter() {
45			for pbit in gate.pbits() {
46				let index = to_linear(pbit.coordinates().0, pbit.coordinates().1);
47				let other_weight = gate.weight();
48				let other_bias = gate.bias();
49
50				if visited[index] {
51					(weight, bias) = self.compose_weights_and_bias(index, &weight, &bias, other_weight, other_bias);
52				}
53
54				visited[index] = true;
55			}
56		}
57
58		self.weight = weight;
59		self.bias = bias;
60	}
61
62	fn compose_weights_and_bias(
63		&self, 
64		linear_index: usize, 
65		weight: &DMatrix<i32>, 
66		bias: &Vec<i32>,
67		other_weight: &DMatrix<i32>,
68		other_bias: &Vec<i32>
69	) -> (DMatrix<i32>, Vec<i32>) {
70		// Figure out new size of weight matrix, these are square matrices
71		let (other_row, _) = other_weight.shape();
72
73		let additional_columns = other_row - 1;
74
75		let mut new_weight = weight.clone();
76		let mut new_other_weight = other_weight.clone();
77
78		for i in 0..additional_columns {
79			 new_weight = new_weight.insert_column(linear_index + i + 1, 0);
80			 new_weight = new_weight.insert_row(linear_index + i + 1, 0);
81			 new_other_weight = new_other_weight.insert_column(i, 0);
82			 new_other_weight = new_other_weight.insert_row(i, 0);
83		}
84
85		// Compose weights
86		new_weight += new_other_weight;
87
88		let mut new_bias = bias.clone();
89		let new_other_bias = other_bias.clone();
90
91		new_bias[linear_index] += new_other_bias[0];
92		for (i, bias_value) in new_other_bias.iter().enumerate() {
93			if i == 0 {
94				continue;
95			}
96
97			new_bias.push(*bias_value);
98		}
99
100		(new_weight, new_bias)
101	}
102
103	fn grid_space(&self) -> (usize, usize) {
104		let mut x = 0;
105		let mut y = 0;
106		for gate in self.gates.iter() {
107			for pbit in gate.pbits().iter() {
108				if pbit.coordinates().0 > x {
109					x = pbit.coordinates().0;
110				}
111				if pbit.coordinates().1 > y {
112					y = pbit.coordinates().1;
113				}
114			}
115		}
116
117		(x, y)
118	}
119}