use std::ops::{Add, Mul, Neg, Sub};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Wire(pub(crate) u32);
impl Wire {
pub fn from_index(index: usize) -> Wire {
Wire(u32::try_from(index).expect("wire position must fit in u32"))
}
pub fn index(self) -> usize {
self.0 as usize
}
}
pub type Term = (Wire, f64);
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Lin {
pub terms: Vec<Term>,
pub bias: f64,
}
impl Lin {
pub fn new(terms: Vec<Term>, bias: f64) -> Lin {
Lin { terms, bias }
}
pub fn constant(bias: f64) -> Lin {
Lin { terms: Vec::new(), bias }
}
fn scaled(mut self, factor: f64) -> Lin {
for term in &mut self.terms {
term.1 *= factor;
}
self.bias *= factor;
self
}
fn plus(mut self, other: Lin) -> Lin {
self.terms.extend(other.terms);
self.bias += other.bias;
self
}
}
impl From<Wire> for Lin {
fn from(wire: Wire) -> Lin {
Lin { terms: vec![(wire, 1.0)], bias: 0.0 }
}
}
impl From<f64> for Lin {
fn from(bias: f64) -> Lin {
Lin::constant(bias)
}
}
impl Neg for Lin {
type Output = Lin;
fn neg(self) -> Lin {
self.scaled(-1.0)
}
}
impl Neg for Wire {
type Output = Lin;
fn neg(self) -> Lin {
Lin::from(self).scaled(-1.0)
}
}
impl Mul<f64> for Lin {
type Output = Lin;
fn mul(self, factor: f64) -> Lin {
self.scaled(factor)
}
}
impl Mul<f64> for Wire {
type Output = Lin;
fn mul(self, factor: f64) -> Lin {
Lin::from(self).scaled(factor)
}
}
impl Mul<Wire> for f64 {
type Output = Lin;
fn mul(self, wire: Wire) -> Lin {
Lin::from(wire).scaled(self)
}
}
impl Mul<Lin> for f64 {
type Output = Lin;
fn mul(self, form: Lin) -> Lin {
form.scaled(self)
}
}
macro_rules! affine_arithmetic {
($left:ty, $right:ty) => {
impl Add<$right> for $left {
type Output = Lin;
fn add(self, right: $right) -> Lin {
Lin::from(self).plus(Lin::from(right))
}
}
impl Sub<$right> for $left {
type Output = Lin;
fn sub(self, right: $right) -> Lin {
Lin::from(self).plus(Lin::from(right).scaled(-1.0))
}
}
};
}
affine_arithmetic!(Wire, Wire);
affine_arithmetic!(Wire, Lin);
affine_arithmetic!(Wire, f64);
affine_arithmetic!(Lin, Wire);
affine_arithmetic!(Lin, Lin);
affine_arithmetic!(Lin, f64);
affine_arithmetic!(f64, Wire);
affine_arithmetic!(f64, Lin);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn operators_build_forms_in_written_order() {
let a = Wire(3);
let b = Wire(1);
let form = a * 0.5 - b + 2.0;
assert_eq!(form, Lin::new(vec![(a, 0.5), (b, -1.0)], 2.0));
assert_eq!(1.0 - a, Lin::new(vec![(a, -1.0)], 1.0));
}
}