1use std::ops::{Add, Mul, Neg, Sub};
11
12#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
14pub struct Wire(pub(crate) u32);
15
16impl Wire {
17 pub fn from_index(index: usize) -> Wire {
19 Wire(u32::try_from(index).expect("wire position must fit in u32"))
20 }
21
22 pub fn index(self) -> usize {
23 self.0 as usize
24 }
25}
26
27pub type Term = (Wire, f64);
30
31#[derive(Clone, Debug, Default, PartialEq)]
36pub struct Lin {
37 pub terms: Vec<Term>,
38 pub bias: f64,
39}
40
41impl Lin {
42 pub fn new(terms: Vec<Term>, bias: f64) -> Lin {
43 Lin { terms, bias }
44 }
45
46 pub fn constant(bias: f64) -> Lin {
47 Lin { terms: Vec::new(), bias }
48 }
49
50 fn scaled(mut self, factor: f64) -> Lin {
51 for term in &mut self.terms {
52 term.1 *= factor;
53 }
54 self.bias *= factor;
55 self
56 }
57
58 fn plus(mut self, other: Lin) -> Lin {
59 self.terms.extend(other.terms);
60 self.bias += other.bias;
61 self
62 }
63}
64
65impl From<Wire> for Lin {
66 fn from(wire: Wire) -> Lin {
67 Lin { terms: vec![(wire, 1.0)], bias: 0.0 }
68 }
69}
70
71impl From<f64> for Lin {
72 fn from(bias: f64) -> Lin {
73 Lin::constant(bias)
74 }
75}
76
77impl Neg for Lin {
78 type Output = Lin;
79 fn neg(self) -> Lin {
80 self.scaled(-1.0)
81 }
82}
83
84impl Neg for Wire {
85 type Output = Lin;
86 fn neg(self) -> Lin {
87 Lin::from(self).scaled(-1.0)
88 }
89}
90
91impl Mul<f64> for Lin {
92 type Output = Lin;
93 fn mul(self, factor: f64) -> Lin {
94 self.scaled(factor)
95 }
96}
97
98impl Mul<f64> for Wire {
99 type Output = Lin;
100 fn mul(self, factor: f64) -> Lin {
101 Lin::from(self).scaled(factor)
102 }
103}
104
105impl Mul<Wire> for f64 {
106 type Output = Lin;
107 fn mul(self, wire: Wire) -> Lin {
108 Lin::from(wire).scaled(self)
109 }
110}
111
112impl Mul<Lin> for f64 {
113 type Output = Lin;
114 fn mul(self, form: Lin) -> Lin {
115 form.scaled(self)
116 }
117}
118
119macro_rules! affine_arithmetic {
121 ($left:ty, $right:ty) => {
122 impl Add<$right> for $left {
123 type Output = Lin;
124 fn add(self, right: $right) -> Lin {
125 Lin::from(self).plus(Lin::from(right))
126 }
127 }
128 impl Sub<$right> for $left {
129 type Output = Lin;
130 fn sub(self, right: $right) -> Lin {
131 Lin::from(self).plus(Lin::from(right).scaled(-1.0))
132 }
133 }
134 };
135}
136
137affine_arithmetic!(Wire, Wire);
138affine_arithmetic!(Wire, Lin);
139affine_arithmetic!(Wire, f64);
140affine_arithmetic!(Lin, Wire);
141affine_arithmetic!(Lin, Lin);
142affine_arithmetic!(Lin, f64);
143affine_arithmetic!(f64, Wire);
144affine_arithmetic!(f64, Lin);
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn operators_build_forms_in_written_order() {
152 let a = Wire(3);
153 let b = Wire(1);
154 let form = a * 0.5 - b + 2.0;
155 assert_eq!(form, Lin::new(vec![(a, 0.5), (b, -1.0)], 2.0));
156 assert_eq!(1.0 - a, Lin::new(vec![(a, -1.0)], 1.0));
157 }
158}