diff --git a/rust/adagio/Cargo.toml b/rust/adagio/Cargo.toml --- a/rust/adagio/Cargo.toml +++ b/rust/adagio/Cargo.toml @@ -6,3 +6,4 @@ [dependencies] num = "0.1.37" num-rational = "0.1.36" +void = "1.0.2" diff --git a/rust/adagio/src/builder.rs b/rust/adagio/src/builder.rs --- a/rust/adagio/src/builder.rs +++ b/rust/adagio/src/builder.rs @@ -1,21 +1,21 @@ -use num_rational::Rational; +use num_rational::Ratio; -use {Environment, Node}; +use {Environment, LiteralType, Node}; use node_defs::{Container, NodeData, Sum}; +use node_ops::NodeExpr; -impl Environment { - fn swlu>(&mut self, input: Container) -> Container> where T::Constant: From { - let leakiness = self.number(Rational::new(1, 10)); - let one_half = self.number(Rational::new(1, 2)); - let one = self.number(Rational::new(1, 1)); - let minus_one = self.number(Rational::new(-1, 1)); - let tanh = self.tanh(input.clone()); - let minus_tanh = self.multiply(minus_one, tanh.clone()); - let one_plus = self.add(one.clone(), tanh); - let one_minus = self.add(one, minus_tanh); - let scaled = self.multiply(leakiness, one_minus); - let sum = self.add(one_plus, scaled); - let half_input = self.multiply(one_half, input); - self.multiply(half_input, sum) +impl NodeExpr { + pub fn swlu(self) -> NodeExpr + where T::Constant: From> + { + self.clone() * constant((1, 2)) * + ((self.clone().tanh() + constant(1)) + + (constant(1) - self.clone().tanh()) * constant((1, 10))) } -} \ No newline at end of file +} + +fn constant>>(constant: C) -> NodeExpr + where T::Constant: From> +{ + NodeExpr::constant(constant.into()) +} diff --git a/rust/adagio/src/eval.rs b/rust/adagio/src/eval.rs --- a/rust/adagio/src/eval.rs +++ b/rust/adagio/src/eval.rs @@ -66,11 +66,11 @@ fn eval_primitive(&mut self, state: &State, primitive: Container>) -> f64 { if !self.primitives.contains_key(&primitive.clone()) { - let result = match primitive.as_ref() { - &Primitive::Input(index) => state.inputs[index], - &Primitive::Real(_) => unimplemented!(), - &Primitive::Parameter(index) => state.parameters[index], - &Primitive::Sigmoid(minus, ref sum) => { + let result = match *primitive.as_ref() { + Primitive::Input(index) => state.inputs[index], + Primitive::Real(_) => unimplemented!(), + Primitive::Parameter(index) => state.parameters[index], + Primitive::Sigmoid(minus, ref sum) => { let tanh = self.eval_sum(state, sum.clone()).tanh(); if minus { -tanh } else { tanh } } diff --git a/rust/adagio/src/lib.rs b/rust/adagio/src/lib.rs --- a/rust/adagio/src/lib.rs +++ b/rust/adagio/src/lib.rs @@ -1,5 +1,6 @@ extern crate num; extern crate num_rational; +extern crate void; use std::cmp::Ordering; use std::collections::{HashMap, HashSet}; @@ -9,10 +10,15 @@ mod eval; mod node_conversions; mod node_defs; +mod node_ops; mod node_ordering; use node_conversions::Summable; use node_defs::{Container, container, FancyClone, NodeData, Sum, Product, Power, Primitive}; +use node_ops::{AsExpr, NodeExpr}; + +///Writing code that's generic over scalars is suffering. +type LiteralType = i32; /// Negates a Sum node. impl Sum { @@ -77,6 +83,19 @@ } impl Environment { + pub fn new() -> Environment { + Environment { + sum: HashSet::new(), + product: HashSet::new(), + power: HashSet::new(), + primitive: HashSet::new(), + sum_derivative: HashMap::new(), + product_derivative: HashMap::new(), + power_derivative: HashMap::new(), + primitive_derivative: HashMap::new(), + } + } + /// Makes a canonicalized Sum from the given components. pub fn make_sum>(&mut self, minus: bool, @@ -298,9 +317,7 @@ } } - last = self.add(last, inner); - last = self.add(last, outer); - self.add(last, first) + self.eval(first.expr() + outer.expr() + inner.expr() + last.expr()) } fn _combine_terms(&mut self, @@ -314,9 +331,18 @@ let mut left_index = 0; let mut right_index = 0; - while left_index < left_length && right_index < right_length { + println!("Vectors equal: {}", left == right); + //println!("Left: {:?} right: {:?}", left, right); + + while (left_index < left_length) && (right_index < right_length) { + assert!(left_index < left.len(), "Left OOB"); + assert!(right_index < right.len(), "Right OOB"); + println!("Right len: {} index: {}", right.len(), right_index); + let ref right_power = right[right_length]; + println!("Right indexing succeeded"); + println!("Left len: {} index: {} right len: {} index: {}", left.len(), left_index, right.len(), right_index); let ref left_power = left[left_index]; - let ref right_power = right[right_length]; + println!("Left indexing succeeded"); match left_power.fuzzy_cmp(right_power.as_ref()) { Ordering::Less => { powers.push(left_power.clone()); @@ -351,9 +377,38 @@ let sum = sum.as_sum(self); self.make_sigmoid(false, sum) } + + pub fn eval(&mut self, expr: NodeExpr) -> Container> { + match expr { + NodeExpr::Sum(a) => a.insert(self), + NodeExpr::Product(a) => a.as_sum(self), + NodeExpr::Power(a) => a.as_sum(self), + NodeExpr::Primitive(a) => a.as_sum(self), + NodeExpr::Constant(a) => self.number(a), + NodeExpr::Add(a, b) => { + let left = self.eval(*a); + let right = self.eval(*b); + self.add(left, right) + } + NodeExpr::Subtract(a, b) => { + let left = self.eval(*a); + let right = self.eval(*b).negate(self); + self.add(left, right) + } + NodeExpr::Multiply(a, b) => { + let left = self.eval(*a); + let right = self.eval(*b); + self.multiply(left, right) + } + NodeExpr::Tanh(a) => { + let sum = self.eval(*a); + self.tanh(sum).as_sum(self) + } + } + } } -pub trait Node: Summable + FancyClone { +pub trait Node: Summable + FancyClone + AsExpr { fn partial_derivative(&self, variable: &T::Parameter, env: &mut Environment) @@ -497,8 +552,8 @@ variable: &T::Parameter, env: &mut Environment) -> Container> { - match self { - &Primitive::Sigmoid(minus, ref sum) => { + match *self { + Primitive::Sigmoid(minus, ref sum) => { let derivative = sum.partial_derivative(variable, env); let self_in_env = self.insert(env); let self_squared = env.secret_make_power(self_in_env, 2); @@ -506,7 +561,7 @@ let sum = env.make_sum(minus, 1, vec![minus_self_squared]); env.multiply(derivative, sum) } - &Primitive::Parameter(ref p) => env.number(if p == variable { 1 } else { 0 }), + Primitive::Parameter(ref p) => env.number(if p == variable { 1 } else { 0 }), _ => env.number(0), } } @@ -527,6 +582,50 @@ #[cfg(test)] mod tests { + use num_rational; + use void; + + use {Environment, LiteralType, NodeData}; + use node_ops::AsExpr; #[test] - fn it_works() {} + fn construct_xor() { + struct MyNodeData(void::Void); + + impl NodeData for MyNodeData { + type Constant = num_rational::Ratio; + type Coefficient = num_rational::Ratio; + type Exponent = LiteralType; + type Input = usize; + type Real = bool; // Pls no use. + type Parameter = usize; + } + + let mut env = Environment::::new(); + let mut param_count = 0..; + + let left_input = env.make_input(0); + let right_input = env.make_input(1); + + let left_hidden = + (left_input.expr() * env.make_parameter(param_count.next().unwrap()).expr() + + right_input.expr() * env.make_parameter(param_count.next().unwrap()).expr() + + env.make_parameter(param_count.next().unwrap()).expr()) + .swlu(); + let right_hidden = + (left_input.expr() * env.make_parameter(param_count.next().unwrap()).expr() + + right_input.expr() * env.make_parameter(param_count.next().unwrap()).expr() + + env.make_parameter(param_count.next().unwrap()).expr()) + .swlu(); + + let result = left_hidden * env.make_parameter(param_count.next().unwrap()).expr() + + right_hidden * env.make_parameter(param_count.next().unwrap()).expr() + + env.make_parameter(param_count.next().unwrap()).expr(); + + let expected = env.make_input(2); + let difference = result.clone() - expected.expr(); + let error = difference.clone() * difference; + + let result_container = env.eval(result); + let error_container = env.eval(error); + } } diff --git a/rust/adagio/src/node_conversions.rs b/rust/adagio/src/node_conversions.rs --- a/rust/adagio/src/node_conversions.rs +++ b/rust/adagio/src/node_conversions.rs @@ -44,8 +44,8 @@ impl Productable for Primitive { fn as_product(&self, env: &mut Environment) -> Container> { - match self { - &Primitive::Sigmoid(true, ref sum) => { + match *self { + Primitive::Sigmoid(true, ref sum) => { let sigmoid = env.make_sigmoid(false, sum.clone()); let power = sigmoid.as_power(env); env.secret_make_product(-1, vec![power]) diff --git a/rust/adagio/src/node_defs.rs b/rust/adagio/src/node_defs.rs --- a/rust/adagio/src/node_defs.rs +++ b/rust/adagio/src/node_defs.rs @@ -5,7 +5,7 @@ use num::{Integer, Signed}; -use {Environment, Node}; +use {Environment, LiteralType, Node}; pub type Container = Arc; @@ -14,9 +14,9 @@ } pub trait NodeData { - type Constant: Hash + Ord + Clone + Signed + Into + From; - type Coefficient: Hash + Ord + Clone + Signed + Into + From; - type Exponent: Hash + Clone + Integer + Into + From; + type Constant: Hash + Ord + Clone + Signed + Into + From; + type Coefficient: Hash + Ord + Clone + Signed + Into + From; + type Exponent: Hash + Clone + Integer + Into + From; type Input: Hash + Ord + Clone; type Real: Hash + Ord + Clone; type Parameter: Hash + Ord + Clone; @@ -130,20 +130,20 @@ impl Hash for Primitive { fn hash(&self, state: &mut H) { - match self { - &Primitive::Input(ref a) => { + match *self { + Primitive::Input(ref a) => { "Input".hash(state); a.hash(state) } - &Primitive::Real(ref a) => { + Primitive::Real(ref a) => { "Real".hash(state); a.hash(state) } - &Primitive::Parameter(ref a) => { + Primitive::Parameter(ref a) => { "Parameter".hash(state); a.hash(state) } - &Primitive::Sigmoid(minus, ref a) => { + Primitive::Sigmoid(minus, ref a) => { "Sigmoid".hash(state); minus.hash(state); a.hash(state) @@ -184,8 +184,8 @@ impl FancyClone for Primitive { fn fancy_clone(&self, env: &mut Environment) -> Primitive { - match self { - &Primitive::Sigmoid(minus, ref a) => Primitive::Sigmoid(minus, a.insert(env)), + match *self { + Primitive::Sigmoid(minus, ref a) => Primitive::Sigmoid(minus, a.insert(env)), _ => self.clone(), } } @@ -223,11 +223,11 @@ impl Clone for Primitive { fn clone(&self) -> Primitive { - match self { - &Primitive::Input(ref a) => Primitive::Input(a.clone()), - &Primitive::Real(ref a) => Primitive::Real(a.clone()), - &Primitive::Parameter(ref a) => Primitive::Parameter(a.clone()), - &Primitive::Sigmoid(minus, ref a) => Primitive::Sigmoid(minus, a.clone()), + match *self { + Primitive::Input(ref a) => Primitive::Input(a.clone()), + Primitive::Real(ref a) => Primitive::Real(a.clone()), + Primitive::Parameter(ref a) => Primitive::Parameter(a.clone()), + Primitive::Sigmoid(minus, ref a) => Primitive::Sigmoid(minus, a.clone()), } } } diff --git a/rust/adagio/src/node_ops.rs b/rust/adagio/src/node_ops.rs new file mode 100644 --- /dev/null +++ b/rust/adagio/src/node_ops.rs @@ -0,0 +1,94 @@ +use std::ops::{Add, Mul, Sub}; + +use Node; +use node_defs::{NodeData, Sum, Product, Power, Primitive}; + +pub enum NodeExpr { + Sum(Sum), + Product(Product), + Power(Power), + Primitive(Primitive), + Constant(T::Constant), + Add(Box>, Box>), + Subtract(Box>, Box>), + Multiply(Box>, Box>), + Tanh(Box>), +} + +impl Clone for NodeExpr { + fn clone(&self) -> NodeExpr { + match *self { + NodeExpr::Sum(ref a) => NodeExpr::Sum(a.clone()), + NodeExpr::Product(ref a) => NodeExpr::Product(a.clone()), + NodeExpr::Power(ref a) => NodeExpr::Power(a.clone()), + NodeExpr::Primitive(ref a) => NodeExpr::Primitive(a.clone()), + NodeExpr::Constant(ref a) => NodeExpr::Constant(a.clone()), + NodeExpr::Add(ref a, ref b) => NodeExpr::Add(a.clone(), b.clone()), + NodeExpr::Subtract(ref a, ref b) => NodeExpr::Subtract(a.clone(), b.clone()), + NodeExpr::Multiply(ref a, ref b) => NodeExpr::Multiply(a.clone(), b.clone()), + NodeExpr::Tanh(ref a) => NodeExpr::Tanh(a.clone()), + } + } +} + +impl Add for NodeExpr { + type Output = NodeExpr; + + fn add(self, rhs: NodeExpr) -> NodeExpr { + NodeExpr::Add(Box::new(self), Box::new(rhs)) + } +} + +impl Sub for NodeExpr { + type Output = NodeExpr; + + fn sub(self, rhs: NodeExpr) -> NodeExpr { + NodeExpr::Subtract(Box::new(self), Box::new(rhs)) + } +} + +impl Mul for NodeExpr { + type Output = NodeExpr; + + fn mul(self, rhs: NodeExpr) -> NodeExpr { + NodeExpr::Multiply(Box::new(self), Box::new(rhs)) + } +} + +pub trait AsExpr { + fn expr(&self) -> NodeExpr; +} + +impl AsExpr for Sum { + fn expr(&self) -> NodeExpr { + NodeExpr::Sum(self.clone()) + } +} + +impl AsExpr for Product { + fn expr(&self) -> NodeExpr { + NodeExpr::Product(self.clone()) + } +} + +impl AsExpr for Power { + fn expr(&self) -> NodeExpr { + NodeExpr::Power(self.clone()) + } +} + +impl AsExpr for Primitive { + fn expr(&self) -> NodeExpr { + NodeExpr::Primitive(self.clone()) + } +} + +impl NodeExpr { + pub fn tanh(self) -> NodeExpr { + NodeExpr::Tanh(Box::new(self)) + } + + pub fn constant>(constant: C) -> NodeExpr { + NodeExpr::Constant(constant.into()) + } +} diff --git a/rust/adagio/src/node_ordering.rs b/rust/adagio/src/node_ordering.rs --- a/rust/adagio/src/node_ordering.rs +++ b/rust/adagio/src/node_ordering.rs @@ -12,11 +12,11 @@ impl Primitive { fn simplify(&self) -> PrimitiveType { - match self { - &Primitive::Input(_) => PrimitiveType::Input, - &Primitive::Real(_) => PrimitiveType::Real, - &Primitive::Parameter(_) => PrimitiveType::Parameter, - &Primitive::Sigmoid(_, _) => PrimitiveType::Sigmoid, + match *self { + Primitive::Input(_) => PrimitiveType::Input, + Primitive::Real(_) => PrimitiveType::Real, + Primitive::Parameter(_) => PrimitiveType::Parameter, + Primitive::Sigmoid(_, _) => PrimitiveType::Sigmoid, } } }