mwchase icon

LC:NN upload 4 - diff

mwchase | PRO | 03/26/17 07:02:41 PM UTC | 0 ⭐ | 942 👁️ | Never ⏰ | []
Diff |

37.03 KB

|

None

|

0 👍

/

0 👎

diff --git a/rust/adagio/src/eval.rs b/rust/adagio/src/eval.rs
new file mode 100644
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
@@ -2,30 +2,37 @@
 
 use std::cmp::Ordering;
 use std::collections::HashMap;
-use std::hash::{Hash, Hasher};
+use std::hash::Hash;
 
-use num::{One, Zero};
-
+mod node_conversions;
 mod node_defs;
 mod node_ordering;
 
-use node_defs::{Container, container, NodeData, Sum, Product, Power, Primitive};
+use node_conversions::Summable;
+use node_defs::{Container, container, FancyClone, NodeData, Sum, Product, Power, Primitive};
 
+/// Negates a Sum node.
 impl<T: NodeData> Sum<T> {
     fn negate(&self, env: &mut Environment<T>) -> Container<Sum<T>> {
-        env.make_sum(!self.minus, &self.constant, &self.terms)
+        env.make_sum(!self.minus,
+                     self.constant.clone(),
+                     self.terms.iter().cloned())
     }
 }
 
+/// Negates a Product node.
 impl<T: NodeData> Product<T> {
     fn negate(&self, env: &mut Environment<T>) -> Container<Product<T>> {
-        env.make_product(&-self.coefficient.clone(), &self.powers).unwrap()
+        env.secret_make_product(-self.coefficient.clone(), self.powers.clone())
     }
 }
 
+/// Convenience type, maps an instance of a type to an Arc containing a clone of the instance.
 type SelfMap<T> = HashMap<T, Container<T>>;
 
-struct Environment<T: NodeData> {
+/// A struct that contains canonicalization maps for every node type, as well as maps to
+/// memoize partial derivatives.
+pub struct Environment<T: NodeData> {
     sum: SelfMap<Sum<T>>,
     product: SelfMap<Product<T>>,
     power: SelfMap<Power<T>>,
@@ -37,6 +44,7 @@
 }
 
 impl<T: NodeData> Sum<T> {
+    /// Returns the value of the sum, if every term were 0.
     fn at_zero(&self) -> T::Constant {
         if self.minus {
             -self.constant.clone()
@@ -45,108 +53,143 @@
         }
     }
 
+    /// Returns a Sum that has the same shape as the sum, but equal to constant at zero.
     fn adjust_constant(&self,
-                       constant: &T::Constant,
+                       constant: T::Constant,
                        env: &mut Environment<T>)
                        -> Container<Sum<T>> {
         env.make_sum(self.minus,
-                     &(if self.minus {
-                           -constant.clone()
-                       } else {
-                           constant.clone()
-                       }),
-                     &self.terms)
+                     (if self.minus { -constant } else { constant }),
+                     self.terms.iter().cloned())
     }
 
+    /// Returns the terms of the sum, negated if the sum was negated.
     fn conditional_negate(&self, env: &mut Environment<T>) -> Vec<Container<Product<T>>> {
         if self.minus {
-            env.negate_products(&self.terms)
+            env.negate_products(self.terms.iter().cloned())
         } else {
             self.terms
                 .iter()
                 .map(|p| p.insert(env))
-                .collect::<Vec<_>>()
+                .collect()
         }
     }
 }
 
 impl<T: NodeData> Environment<T> {
-    fn make_sum(&mut self,
-                minus: bool,
-                constant: &T::Constant,
-                terms: &Vec<Container<Product<T>>>)
-                -> Container<Sum<T>> {
+    /// Makes a canonicalized Sum from the given components.
+    pub fn make_sum<C, N: Into<T::Constant>>(&mut self,
+                                             minus: bool,
+                                             constant: N,
+                                             terms: C)
+                                             -> Container<Sum<T>>
+        where C: IntoIterator<Item = Container<Product<T>>>
+    {
+        let terms = terms.into_iter().collect::<Vec<_>>();
+        let mut minus = minus;
+        let mut constant = constant.into();
         if minus && terms.len() == 0 {
-            self.make_sum(false, &-constant.clone(), terms)
-        } else {
-            Sum::new(minus, constant.clone(), terms.clone()).insert(self)
+            minus = false;
+            constant = -constant;
         }
+        Sum::new(minus, constant, terms).insert(self)
+    }
+
+    fn secret_make_product<N: Into<T::Coefficient>>(&mut self,
+                                                    coefficient: N,
+                                                    powers: Vec<Container<Power<T>>>)
+                                                    -> Container<Product<T>> {
+        Product::new(coefficient.into(), powers).insert(self)
     }
 
-    fn make_product(&mut self,
-                    coefficient: &T::Constant,
-                    powers: &Vec<Container<Power<T>>>)
-                    -> Option<Container<Product<T>>> {
-        if coefficient.clone() == T::Constant::zero() || powers.len() == 0 {
+    /// Makes a canonicalized Product from the given components. Because some inputs create
+    /// invalid products, this returns an Option.
+    pub fn make_product<C, N: Into<T::Coefficient>>(&mut self,
+                                                    coefficient: N,
+                                                    powers: C)
+                                                    -> Option<Container<Product<T>>>
+        where C: IntoIterator<Item = Container<Power<T>>>
+    {
+        let coefficient = coefficient.into();
+        if coefficient != 0.into() {
+            let powers = powers.into_iter().collect::<Vec<_>>();
+            if powers.len() != 0 {
+                return Some(self.secret_make_product(coefficient, powers));
+            }
+        }
+        None
+    }
+
+    fn secret_make_power<N: Into<T::Exponent>>(&mut self,
+                                               primitive: Container<Primitive<T>>,
+                                               exponent: N)
+                                               -> Container<Power<T>> {
+        Power::new(exponent.into(), primitive).insert(self)
+    }
+
+    /// Makes a canonicalized Power from the given components. Because some inputs create
+    /// invalid powers, this returns an Option.
+    pub fn make_power<N: Into<T::Exponent>>(&mut self,
+                                            primitive: Container<Primitive<T>>,
+                                            exponent: N)
+                                            -> Option<Container<Power<T>>> {
+        let exponent = exponent.into();
+        if exponent <= 0.into() {
             None
         } else {
-            Some(Product::new(coefficient.clone(), powers.clone()).insert(self))
-        }
-    }
-
-    fn make_power(&mut self,
-                  primitive: &Container<Primitive<T>>,
-                  exponent: &T::Exponent)
-                  -> Option<Container<Power<T>>> {
-        if exponent.clone() <= T::Exponent::zero() {
-            None
-        } else {
-            Some(Power::new(exponent.clone(), primitive.clone()).insert(self))
+            Some(self.secret_make_power(primitive, exponent))
         }
     }
 
-    fn make_system_variable(&mut self, index: &T::SystemVariable) -> Container<Primitive<T>> {
-        Primitive::SystemVariable(index.clone()).insert(self)
+    /// Makes a canonicalized SystemVariable from the given components.
+    pub fn make_system_variable(&mut self, index: T::SystemVariable) -> Container<Primitive<T>> {
+        Primitive::SystemVariable(index).insert(self)
     }
 
-    fn make_parameter(&mut self, name: &T::Parameter) -> Container<Primitive<T>> {
-        Primitive::Parameter(name.clone()).insert(self)
+    /// Makes a canonicalized Parameter from the given components.
+    pub fn make_parameter(&mut self, name: T::Parameter) -> Container<Primitive<T>> {
+        Primitive::Parameter(name).insert(self)
     }
 
-    fn make_input(&mut self, index: &T::Input) -> Container<Primitive<T>> {
-        Primitive::Input(index.clone()).insert(self)
+    /// Makes a canonicalized Input from the given components.
+    pub fn make_input(&mut self, index: T::Input) -> Container<Primitive<T>> {
+        Primitive::Input(index).insert(self)
     }
 
-    fn make_sigmoid(&mut self, minus: bool, sum: &Container<Sum<T>>) -> Container<Primitive<T>> {
-        if sum.minus || (sum.terms.len() == 0 && sum.constant < T::Constant::zero()) {
+    /// Makes a canonicalized Sigmoid from the given components.
+    pub fn make_sigmoid(&mut self, minus: bool, sum: Container<Sum<T>>) -> Container<Primitive<T>> {
+        if sum.minus || (sum.terms.len() == 0 && sum.constant < 0.into()) {
             let sum = sum.negate(self);
-            self.make_sigmoid(!minus, &sum)
+            self.make_sigmoid(!minus, sum)
         } else {
-            Primitive::Sigmoid(minus, sum.clone()).insert(self)
+            Primitive::Sigmoid(minus, sum).insert(self)
         }
     }
 
-    fn negate_products(&mut self,
-                       terms: &Vec<Container<Product<T>>>)
-                       -> Vec<Container<Product<T>>> {
-        terms.iter().map(|p| p.negate(self)).collect::<Vec<_>>()
-    }
-    fn number(&mut self, number: &T::Constant) -> Container<Sum<T>> {
-        self.make_sum(false, number, &vec![])
+    fn negate_products<C>(&mut self, terms: C) -> Vec<Container<Product<T>>>
+        where C: IntoIterator<Item = Container<Product<T>>>
+    {
+        terms.into_iter().map(|p| p.negate(self)).collect()
     }
 
-    fn add<A: Node<T>, B: Node<T>>(&mut self,
-                                   left: &Container<A>,
-                                   right: &Container<B>)
-                                   -> Container<Sum<T>> {
+    /// Makes a constant Sum.
+    pub fn number<N: Into<T::Constant>>(&mut self, number: N) -> Container<Sum<T>> {
+        self.make_sum(false, number, vec![])
+    }
+
+    /// Adds two Nodes together, producing a Sum.
+    pub fn add<A: Node<T>, B: Node<T>>(&mut self,
+                                       left: Container<A>,
+                                       right: Container<B>)
+                                       -> Container<Sum<T>> {
         let left_sum = left.as_sum(self);
         let right_sum = right.as_sum(self);
         let constant = left_sum.at_zero() + right_sum.at_zero();
         if left_sum.terms.len() == 0 {
-            return right_sum.adjust_constant(&constant, self);
+            return right_sum.adjust_constant(constant, self);
         }
         if right_sum.terms.len() == 0 {
-            return left_sum.adjust_constant(&constant, self);
+            return left_sum.adjust_constant(constant, self);
         }
 
         // Begin port of weird logic
@@ -170,8 +213,9 @@
                 Ordering::Equal => {
                     let coefficient = left_term.coefficient.clone() +
                                       right_term.coefficient.clone();
-                    if coefficient != T::Constant::zero() {
-                        terms.push(self.make_product(&coefficient, &left_term.powers).unwrap());
+                    match self.make_product(coefficient, left_term.powers.iter().cloned()) {
+                        Some(product) => terms.push(product),
+                        None => (),
                     }
                     left_index += 1;
                     right_index += 1;
@@ -182,40 +226,41 @@
                 }
             }
             if left_index == left_length {
-                terms.extend_from_slice(&right_terms[..].split_at(right_index).1);
+                terms.extend_from_slice(&right_terms[right_index..]);
             }
             if right_index == right_length {
-                terms.extend_from_slice(&left_terms[..].split_at(left_index).1);
+                terms.extend_from_slice(&left_terms[left_index..]);
             }
         }
 
         terms.shrink_to_fit(); // Why not.
 
-        if terms.len() == 0 || terms[0].coefficient >= T::Constant::zero() {
-            self.make_sum(false, &constant, &terms)
+        if terms.len() == 0 || terms[0].coefficient >= 0.into() {
+            self.make_sum(false, constant, terms)
         } else {
-            let terms = self.negate_products(&terms);
-            self.make_sum(true, &constant, &terms)
+            let terms = self.negate_products(terms);
+            self.make_sum(true, constant, terms)
         }
     }
 
-    fn multiply<A: Node<T>, B: Node<T>>(&mut self,
-                                        left: &Container<A>,
-                                        right: &Container<B>)
-                                        -> Container<Sum<T>> {
+    /// Multiplies two Nodes together, producing a Sum.
+    pub fn multiply<A: Node<T>, B: Node<T>>(&mut self,
+                                            left: Container<A>,
+                                            right: Container<B>)
+                                            -> Container<Sum<T>> {
         let left_sum = left.as_sum(self);
         let right_sum = right.as_sum(self);
         let minus = left_sum.minus != right_sum.minus;
 
         let first_constant = left_sum.constant.clone() * right_sum.constant.clone();
-        let first = self.make_sum(minus, &first_constant, &vec![]);
+        let first = self.make_sum(minus, first_constant, vec![]);
 
         let mut outer_terms = Vec::with_capacity(right_sum.terms.len());
-        if left_sum.constant != T::Constant::zero() {
+        if left_sum.constant != 0.into() {
             for term in &right_sum.terms {
-                let outer_coefficient = left_sum.constant.clone() * term.coefficient.clone();
-                let outer_term = self.make_product(&outer_coefficient, &term.powers).unwrap();
-                if left_sum.constant < T::Constant::zero() {
+                let outer_coefficient = left_sum.constant.clone().into() * term.coefficient.clone();
+                let outer_term = self.secret_make_product(outer_coefficient, term.powers.clone());
+                if left_sum.constant < 0.into() {
                     outer_terms.push(outer_term.negate(self));
                 } else {
                     outer_terms.push(outer_term);
@@ -223,17 +268,16 @@
             }
         }
         outer_terms.shrink_to_fit();
-        let outer = self.make_sum((left_sum.constant < T::Constant::zero()) != minus,
-                                  &T::Constant::zero(),
-                                  &outer_terms);
+        let outer = self.make_sum((left_sum.constant < 0.into()) != minus, 0, outer_terms);
 
         // Should be possible to turn these into functions.
         let mut inner_terms = Vec::with_capacity(left_sum.terms.len());
-        if right_sum.constant != T::Constant::zero() {
+        if right_sum.constant != 0.into() {
             for term in &left_sum.terms {
-                let inner_coefficient = right_sum.constant.clone() * term.coefficient.clone();
-                let inner_term = self.make_product(&inner_coefficient, &term.powers).unwrap();
-                if right_sum.constant < T::Constant::zero() {
+                let inner_coefficient = right_sum.constant.clone().into() *
+                                        term.coefficient.clone();
+                let inner_term = self.secret_make_product(inner_coefficient, term.powers.clone());
+                if right_sum.constant < 0.into() {
                     inner_terms.push(inner_term.negate(self));
                 } else {
                     inner_terms.push(inner_term);
@@ -241,24 +285,22 @@
             }
         }
         inner_terms.shrink_to_fit();
-        let inner = self.make_sum((right_sum.constant < T::Constant::zero()) != minus,
-                                  &T::Constant::zero(),
-                                  &inner_terms);
+        let inner = self.make_sum((right_sum.constant < 0.into()) != minus, 0, inner_terms);
 
-        let mut last = self.number(&T::Constant::zero());
+        let mut last = self.number(0);
         for left_term in &left_sum.terms {
             for right_term in &right_sum.terms {
-                let combined_terms = self._combine_terms(&left_term.powers, &right_term.powers);
                 let last_coefficient = left_term.coefficient.clone() *
                                        right_term.coefficient.clone();
-                let product = self.make_product(&last_coefficient, &combined_terms).unwrap();
-                last = self.add(&last, &product);
+                let combined_terms = self._combine_terms(&left_term.powers, &right_term.powers);
+                let product = self.secret_make_product(last_coefficient, combined_terms);
+                last = self.add(last, product);
             }
         }
 
-        last = self.add(&last, &inner);
-        last = self.add(&last, &outer);
-        self.add(&last, &first)
+        last = self.add(last, inner);
+        last = self.add(last, outer);
+        self.add(last, first)
     }
 
     fn _combine_terms(&mut self,
@@ -275,14 +317,14 @@
         while left_index < left_length && right_index < right_length {
             let ref left_power = left[left_index];
             let ref right_power = right[right_length];
-            match left_power.fuzzy_cmp(&right_power) {
+            match left_power.fuzzy_cmp(right_power.as_ref()) {
                 Ordering::Less => {
                     powers.push(left_power.clone());
                     left_index += 1;
                 }
                 Ordering::Equal => {
                     let exponent = left_power.exponent.clone() + right_power.exponent.clone();
-                    powers.push(self.make_power(&left_power.primitive, &exponent).unwrap());
+                    powers.push(self.secret_make_power(left_power.primitive.clone(), exponent));
                     left_index += 1;
                     right_index += 1;
                 }
@@ -292,10 +334,10 @@
                 }
             }
             if left_index == left_length {
-                powers.extend_from_slice(&right[..].split_at(right_index).1);
+                powers.extend_from_slice(&right[right_index..]);
             }
             if right_index == right_length {
-                powers.extend_from_slice(&left[..].split_at(left_index).1);
+                powers.extend_from_slice(&left[left_index..]);
             }
         }
 
@@ -306,55 +348,37 @@
     }
 }
 
-trait Summable<T: NodeData> {
-    fn as_sum(&self, env: &mut Environment<T>) -> Container<Sum<T>>;
-}
-
-trait Productable<T: NodeData> {
-    fn as_product(&self, env: &mut Environment<T>) -> Container<Product<T>>;
-}
-
-fn _as_sum<T: NodeData>(productable: &Productable<T>,
-                        env: &mut Environment<T>)
-                        -> Container<Sum<T>> {
-    let product = productable.as_product(env);
-    if product.coefficient < T::Constant::zero() {
-        let product = product.negate(env);
-        env.make_sum(true, &T::Constant::zero(), &vec![product])
-    } else {
-        env.make_sum(false, &T::Constant::zero(), &vec![product])
-    }
-}
-
-impl<T: NodeData> Summable<T> for Product<T> {
-    fn as_sum(&self, env: &mut Environment<T>) -> Container<Sum<T>> {
-        _as_sum(self, env)
-    }
-}
-
-impl<T: NodeData> Summable<T> for Power<T> {
-    fn as_sum(&self, env: &mut Environment<T>) -> Container<Sum<T>> {
-        _as_sum(self, env)
-    }
-}
-
-impl<T: NodeData> Summable<T> for Primitive<T> {
-    fn as_sum(&self, env: &mut Environment<T>) -> Container<Sum<T>> {
-        _as_sum(self, env)
-    }
-}
-
-trait Node<T: NodeData>: Summable<T> {
+pub trait Node<T: NodeData>: Summable<T> + FancyClone<T> {
     fn partial_derivative(&self,
                           variable: &T::Parameter,
                           env: &mut Environment<T>)
-                          -> Container<Sum<T>>;
-
-    fn fancy_clone(&self, env: &mut Environment<T>) -> Self;
+                          -> Container<Sum<T>>
+        where Self: Sized + Clone + Eq + Hash
+    {
+        if !self.get_derivative_cache(env).contains_key(&(self.clone(), variable.clone())) {
+            let key = (self.fancy_clone(env), variable.clone());
+            let result = self.derivative_impl(variable, env);
+            self.get_derivative_cache(env).insert(key, result);
+        }
+        match self.get_derivative_cache(env).get(&(self.clone(), variable.clone())) {
+            Some(derivative) => derivative.clone(),
+            None => unreachable!(),
+        }
+    }
 
     fn get_storage<'a>(&self, env: &'a mut Environment<T>) -> &'a mut SelfMap<Self>
         where Self: Sized;
 
+    fn get_derivative_cache<'a>(&self,
+                                env: &'a mut Environment<T>)
+                                -> &'a mut HashMap<(Self, T::Parameter), Container<Sum<T>>>
+        where Self: Sized;
+
+    fn derivative_impl(&self,
+                       variable: &T::Parameter,
+                       env: &mut Environment<T>)
+                       -> Container<Sum<T>>;
+
     fn insert(&self, env: &mut Environment<T>) -> Container<Self>
         where Self: Sized + Clone + Eq + Hash
     {
@@ -364,173 +388,142 @@
             self.get_storage(env).insert(key, container(value));
         }
         match self.get_storage(env).get(self) {
-                Some(node) => node,
-                None => unreachable!(),
-            }
-            .clone()
-    }
-}
-
-impl<T: NodeData> Summable<T> for Sum<T> {
-    fn as_sum(&self, env: &mut Environment<T>) -> Container<Sum<T>> {
-        self.insert(env)
+            Some(node) => node.clone(),
+            None => unreachable!(),
+        }
     }
 }
 
 impl<T: NodeData> Node<T> for Sum<T> {
-    fn partial_derivative(&self,
-                          variable: &T::Parameter,
-                          env: &mut Environment<T>)
-                          -> Container<Sum<T>> {
-        if !env.sum_derivative.contains_key(&(self.clone(), variable.clone())) {
-            let key = (self.fancy_clone(env), variable.clone());
-            let mut result = env.number(&T::Constant::zero());
-            for term in &self.terms {
-                let partial = term.partial_derivative(variable, env);
-                result = env.add(&result, &partial);
-            }
-            if self.minus {
-                result = result.negate(env);
-            }
-            env.sum_derivative.insert(key, result);
+    fn derivative_impl(&self,
+                       variable: &T::Parameter,
+                       env: &mut Environment<T>)
+                       -> Container<Sum<T>> {
+        let mut result = env.number(0);
+        for term in &self.terms {
+            let partial = term.partial_derivative(variable, env);
+            result = env.add(result, partial);
         }
-        match env.sum_derivative.get(&(self.clone(), variable.clone())) {
-            Some(derivative) => derivative.clone(),
-            None => unreachable!(),
+        if self.minus {
+            result = result.negate(env);
         }
-    }
-
-    fn fancy_clone(&self, env: &mut Environment<T>) -> Sum<T> {
-        Sum {
-            pre_hash: self.pre_hash.clone(),
-            minus: self.minus.clone(),
-            constant: self.constant.clone(),
-            terms: self.terms
-                .iter()
-                .map(|p| p.insert(env))
-                .collect::<Vec<_>>(),
-        }
+        result
     }
 
     fn get_storage<'a>(&self, env: &'a mut Environment<T>) -> &'a mut SelfMap<Sum<T>> {
         &mut env.sum
     }
-}
 
-impl<T: NodeData> Productable<T> for Product<T> {
-    fn as_product(&self, env: &mut Environment<T>) -> Container<Product<T>> {
-        self.insert(env)
+    fn get_derivative_cache<'a>(&self,
+                                env: &'a mut Environment<T>)
+                                -> &'a mut HashMap<(Sum<T>, T::Parameter), Container<Sum<T>>>
+        where Self: Sized
+    {
+        &mut env.sum_derivative
     }
 }
 
 impl<T: NodeData> Node<T> for Product<T> {
-    fn partial_derivative(&self,
-                          variable: &T::Parameter,
-                          env: &mut Environment<T>)
-                          -> Container<Sum<T>> {
-        if !env.product_derivative.contains_key(&(self.clone(), variable.clone())) {
-            let key = (self.fancy_clone(env), variable.clone());
-            let result = if self.powers.len() == 1 {
-                self.powers[0].partial_derivative(variable, env)
-            } else {
-                let mut result = env.number(&T::Constant::zero());
-                unimplemented!()
-                /*for (index, focus) in self.powers.iter().enumerate() {
-                    let remainder = env.make_product(T::Constant::one(), )
-                }*/
-            };
-            env.product_derivative.insert(key, result);
-        }
-        match env.product_derivative.get(&(self.clone(), variable.clone())) {
-            Some(derivative) => derivative.clone(),
-            None => unreachable!(),
-        }
-    }
-
-    fn fancy_clone(&self, env: &mut Environment<T>) -> Product<T> {
-        Product {
-            pre_hash: self.pre_hash.clone(),
-            coefficient: self.coefficient.clone(),
-            powers: self.powers
-                .iter()
-                .map(|p| p.insert(env))
-                .collect::<Vec<_>>(),
-        }
+    fn derivative_impl(&self,
+                       variable: &T::Parameter,
+                       env: &mut Environment<T>)
+                       -> Container<Sum<T>> {
+        let coefficient = env.number(self.coefficient.clone());
+        let result = if self.powers.len() == 1 {
+            self.powers[0].partial_derivative(variable, env)
+        } else {
+            let mut result = env.number(0);
+            for (index, focus) in self.powers.iter().enumerate() {
+                let remainder = env.secret_make_product(1,
+                                                        self.powers[..index]
+                                                            .iter()
+                                                            .chain(self.powers[index + 1..]
+                                                                       .iter())
+                                                            .cloned()
+                                                            .collect());
+                let partial_derivative = focus.partial_derivative(variable, env);
+                let product = env.multiply(remainder, partial_derivative);
+                result = env.add(result, product);
+            }
+            result
+        };
+        env.multiply(result, coefficient)
     }
 
     fn get_storage<'a>(&self, env: &'a mut Environment<T>) -> &'a mut SelfMap<Product<T>> {
         &mut env.product
     }
-}
 
-impl<T: NodeData> Productable<T> for Power<T> {
-    fn as_product(&self, env: &mut Environment<T>) -> Container<Product<T>> {
-        let power = self.insert(env);
-        env.make_product(&T::Constant::one(), &vec![power]).unwrap()
+    fn get_derivative_cache<'a>(&self,
+                                env: &'a mut Environment<T>)
+                                -> &'a mut HashMap<(Product<T>, T::Parameter), Container<Sum<T>>>
+        where Self: Sized
+    {
+        &mut env.product_derivative
     }
 }
 
 impl<T: NodeData> Node<T> for Power<T> {
-    fn partial_derivative(&self,
-                          variable: &T::Parameter,
-                          env: &mut Environment<T>)
-                          -> Container<Sum<T>> {
-        unimplemented!();
-    }
-
-    fn fancy_clone(&self, env: &mut Environment<T>) -> Power<T> {
-        Power {
-            exponent: self.exponent.clone(),
-            primitive: self.primitive.insert(env),
+    fn derivative_impl(&self,
+                       variable: &T::Parameter,
+                       env: &mut Environment<T>)
+                       -> Container<Sum<T>> {
+        let derivative = self.primitive.partial_derivative(variable, env);
+        if self.exponent == 1.into() {
+            derivative
+        } else {
+            let coefficient = env.number(self.exponent.clone());
+            let remainder = env.secret_make_power(self.primitive.clone(),
+                                                  self.exponent.clone() - 1.into());
+            let product = env.multiply(derivative, remainder);
+            env.multiply(coefficient, product)
         }
     }
 
     fn get_storage<'a>(&self, env: &'a mut Environment<T>) -> &'a mut SelfMap<Power<T>> {
         &mut env.power
     }
-}
 
-impl<T: NodeData> Primitive<T> {
-    fn as_power(&self, env: &mut Environment<T>) -> Container<Power<T>> {
-        let primitive = self.insert(env);
-        env.make_power(&primitive, &T::Exponent::one()).unwrap()
-    }
-}
-
-impl<T: NodeData> Productable<T> for Primitive<T> {
-    fn as_product(&self, env: &mut Environment<T>) -> Container<Product<T>> {
-        match self {
-            &Primitive::Sigmoid(true, ref sum) => {
-                let sigmoid = env.make_sigmoid(false, sum);
-                let power = sigmoid.as_power(env);
-                env.make_product(&-T::Constant::one(), &vec![power]).unwrap()
-            }
-            _ => {
-                let power = self.as_power(env);
-                power.as_product(env)
-            }
-        }
+    fn get_derivative_cache<'a>(&self,
+                                env: &'a mut Environment<T>)
+                                -> &'a mut HashMap<(Power<T>, T::Parameter), Container<Sum<T>>>
+        where Self: Sized
+    {
+        &mut env.power_derivative
     }
 }
 
 impl<T: NodeData> Node<T> for Primitive<T> {
-    fn partial_derivative(&self,
-                          variable: &T::Parameter,
-                          env: &mut Environment<T>)
-                          -> Container<Sum<T>> {
-        unimplemented!();
-    }
-
-    fn fancy_clone(&self, env: &mut Environment<T>) -> Primitive<T> {
+    fn derivative_impl(&self,
+                       variable: &T::Parameter,
+                       env: &mut Environment<T>)
+                       -> Container<Sum<T>> {
         match self {
-            &Primitive::Sigmoid(minus, ref a) => Primitive::Sigmoid(minus, a.insert(env)),
-            _ => self.clone(),
+            &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);
+                let minus_self_squared = env.secret_make_product(-1, vec![self_squared]);
+                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 }),
+            _ => env.number(0),
         }
     }
 
     fn get_storage<'a>(&self, env: &'a mut Environment<T>) -> &'a mut SelfMap<Primitive<T>> {
         &mut env.primitive
     }
+
+    fn get_derivative_cache<'a>
+        (&self,
+         env: &'a mut Environment<T>)
+         -> &'a mut HashMap<(Primitive<T>, T::Parameter), Container<Sum<T>>>
+        where Self: Sized
+    {
+        &mut env.primitive_derivative
+    }
 }
 
 #[cfg(test)]
diff --git a/rust/adagio/src/node_conversions.rs b/rust/adagio/src/node_conversions.rs
new file mode 100644
--- /dev/null
+++ b/rust/adagio/src/node_conversions.rs
@@ -0,0 +1,83 @@
+use {Environment, Node};
+
+use node_defs::{Container, NodeData, Sum, Product, Power, Primitive};
+
+pub trait Summable<T: NodeData> {
+    fn as_sum(&self, env: &mut Environment<T>) -> Container<Sum<T>>;
+}
+
+trait Productable<T: NodeData> {
+    fn as_product(&self, env: &mut Environment<T>) -> Container<Product<T>>;
+}
+
+fn _as_sum<T: NodeData, U: Productable<T>>(productable: &U,
+                                           env: &mut Environment<T>)
+                                           -> Container<Sum<T>> {
+    let product = productable.as_product(env);
+    if product.coefficient < 0.into() {
+        let product = product.as_ref().negate(env);
+        env.make_sum(true, 0, vec![product])
+    } else {
+        env.make_sum(false, 0, vec![product])
+    }
+}
+
+impl<T: NodeData> Productable<T> for Product<T> {
+    fn as_product(&self, env: &mut Environment<T>) -> Container<Product<T>> {
+        self.insert(env)
+    }
+}
+
+impl<T: NodeData> Productable<T> for Power<T> {
+    fn as_product(&self, env: &mut Environment<T>) -> Container<Product<T>> {
+        let power = self.insert(env);
+        env.secret_make_product(1, vec![power])
+    }
+}
+
+impl<T: NodeData> Primitive<T> {
+    fn as_power(&self, env: &mut Environment<T>) -> Container<Power<T>> {
+        let primitive = self.insert(env);
+        env.secret_make_power(primitive, 1)
+    }
+}
+
+impl<T: NodeData> Productable<T> for Primitive<T> {
+    fn as_product(&self, env: &mut Environment<T>) -> Container<Product<T>> {
+        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])
+            }
+            _ => {
+                let power = self.as_power(env);
+                power.as_product(env)
+            }
+        }
+    }
+}
+
+impl<T: NodeData> Summable<T> for Sum<T> {
+    fn as_sum(&self, env: &mut Environment<T>) -> Container<Sum<T>> {
+        self.insert(env)
+    }
+}
+
+impl<T: NodeData> Summable<T> for Product<T> {
+    fn as_sum(&self, env: &mut Environment<T>) -> Container<Sum<T>> {
+        _as_sum(self, env)
+    }
+}
+
+impl<T: NodeData> Summable<T> for Power<T> {
+    fn as_sum(&self, env: &mut Environment<T>) -> Container<Sum<T>> {
+        _as_sum(self, env)
+    }
+}
+
+impl<T: NodeData> Summable<T> for Primitive<T> {
+    fn as_sum(&self, env: &mut Environment<T>) -> Container<Sum<T>> {
+        _as_sum(self, env)
+    }
+}
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,6 +5,8 @@
 
 use num::{Integer, Signed};
 
+use {Environment, Node};
+
 pub type Container<T> = Arc<T>;
 
 pub fn container<T>(data: T) -> Container<T> {
@@ -12,23 +14,28 @@
 }
 
 pub trait NodeData {
-    type Constant: Hash + Ord + Clone + Signed;
-    type Exponent: Hash + Clone + Integer;
+    type Constant: Hash + Ord + Clone + Signed + Into<Self::Coefficient> + From<i32>;
+    type Coefficient: Hash + Ord + Clone + Signed + Into<Self::Constant> + From<i32>;
+    type Exponent: Hash + Clone + Integer + Into<Self::Constant> + From<i32>;
     type Input: Hash + Ord + Clone;
     type SystemVariable: Hash + Ord + Clone;
     type Parameter: Hash + Ord + Clone;
 }
 
+pub trait FancyClone<T: NodeData> {
+    fn fancy_clone(&self, env: &mut Environment<T>) -> Self;
+}
+
 pub struct Sum<T: NodeData> {
-    pub pre_hash: u64, //I would like these fields to be private if possible.
+    pre_hash: u64, //I would like these fields to be private if possible.
     pub minus: bool,
     pub constant: T::Constant,
     pub terms: Vec<Container<Product<T>>>,
 }
 
 pub struct Product<T: NodeData> {
-    pub pre_hash: u64, //I would like these fields to be private if possible.
-    pub coefficient: T::Constant,
+    pre_hash: u64, //I would like these fields to be private if possible.
+    pub coefficient: T::Coefficient,
     pub powers: Vec<Container<Power<T>>>,
 }
 
@@ -145,6 +152,51 @@
     }
 }
 
+impl<T: NodeData> FancyClone<T> for Sum<T> {
+    fn fancy_clone(&self, env: &mut Environment<T>) -> Sum<T> {
+        Sum {
+            pre_hash: self.pre_hash.clone(),
+            minus: self.minus.clone(),
+            constant: self.constant.clone(),
+            terms: self.terms
+                .iter()
+                .map(|p| p.insert(env))
+                .collect(),
+        }
+    }
+}
+
+impl<T: NodeData> FancyClone<T> for Product<T> {
+    fn fancy_clone(&self, env: &mut Environment<T>) -> Product<T> {
+        Product {
+            pre_hash: self.pre_hash.clone(),
+            coefficient: self.coefficient.clone(),
+            powers: self.powers
+                .iter()
+                .map(|p| p.insert(env))
+                .collect(),
+        }
+    }
+}
+
+impl<T: NodeData> FancyClone<T> for Power<T> {
+    fn fancy_clone(&self, env: &mut Environment<T>) -> Power<T> {
+        Power {
+            exponent: self.exponent.clone(),
+            primitive: self.primitive.insert(env),
+        }
+    }
+}
+
+impl<T: NodeData> FancyClone<T> for Primitive<T> {
+    fn fancy_clone(&self, env: &mut Environment<T>) -> Primitive<T> {
+        match self {
+            &Primitive::Sigmoid(minus, ref a) => Primitive::Sigmoid(minus, a.insert(env)),
+            _ => self.clone(),
+        }
+    }
+}
+
 impl<T: NodeData> Clone for Sum<T> {
     fn clone(&self) -> Sum<T> {
         Sum {
@@ -202,7 +254,7 @@
 }
 
 impl<T: NodeData> Product<T> {
-    pub fn new(coefficient: T::Constant, powers: Vec<Container<Power<T>>>) -> Product<T> {
+    pub fn new(coefficient: T::Coefficient, powers: Vec<Container<Power<T>>>) -> Product<T> {
         let mut s = DefaultHasher::new();
         coefficient.hash(&mut s);
         powers.hash(&mut s);
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
@@ -1,6 +1,6 @@
 use std::cmp::Ordering;
 
-use node_defs::{Container, NodeData, Sum, Product, Power, Primitive};
+use node_defs::{NodeData, Sum, Product, Power, Primitive};
 
 #[derive(Eq, Ord, PartialEq, PartialOrd)]
 enum PrimitiveType {

Comments