mwchase icon

LC:NN upload 3 - lib.rs

mwchase | PRO | 03/20/17 02:28:01 AM UTC | 0 ⭐ | 831 👁️ | Never ⏰ | []
Rust |

19.14 KB

|

None

|

0 👍

/

0 👎

extern crate num;
 
use std::cmp::Ordering;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
 
use num::{One, Zero};
 
mod node_defs;
mod node_ordering;
 
use node_defs::{Container, container, NodeData, Sum, Product, Power, Primitive};
 
impl<T: NodeData> Sum<T> {
    fn negate(&self, env: &mut Environment<T>) -> Container<Sum<T>> {
        env.make_sum(!self.minus, &self.constant, &self.terms)
    }
}
 
impl<T: NodeData> Product<T> {
    fn negate(&self, env: &mut Environment<T>) -> Container<Product<T>> {
        env.make_product(&-self.coefficient.clone(), &self.powers).unwrap()
    }
}
 
type SelfMap<T> = HashMap<T, Container<T>>;
 
struct Environment<T: NodeData> {
    sum: SelfMap<Sum<T>>,
    product: SelfMap<Product<T>>,
    power: SelfMap<Power<T>>,
    primitive: SelfMap<Primitive<T>>,
    sum_derivative: HashMap<(Sum<T>, T::Parameter), Container<Sum<T>>>,
    product_derivative: HashMap<(Product<T>, T::Parameter), Container<Sum<T>>>,
    power_derivative: HashMap<(Power<T>, T::Parameter), Container<Sum<T>>>,
    primitive_derivative: HashMap<(Primitive<T>, T::Parameter), Container<Sum<T>>>,
}
 
impl<T: NodeData> Sum<T> {
    fn at_zero(&self) -> T::Constant {
        if self.minus {
            -self.constant.clone()
        } else {
            self.constant.clone()
        }
    }
 
    fn adjust_constant(&self,
                       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)
    }
 
    fn conditional_negate(&self, env: &mut Environment<T>) -> Vec<Container<Product<T>>> {
        if self.minus {
            env.negate_products(&self.terms)
        } else {
            self.terms
                .iter()
                .map(|p| p.insert(env))
                .collect::<Vec<_>>()
        }
    }
}
 
impl<T: NodeData> Environment<T> {
    fn make_sum(&mut self,
                minus: bool,
                constant: &T::Constant,
                terms: &Vec<Container<Product<T>>>)
                -> Container<Sum<T>> {
        if minus && terms.len() == 0 {
            self.make_sum(false, &-constant.clone(), terms)
        } else {
            Sum::new(minus, constant.clone(), terms.clone()).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 {
            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))
        }
    }
 
    fn make_system_variable(&mut self, index: &T::SystemVariable) -> Container<Primitive<T>> {
        Primitive::SystemVariable(index.clone()).insert(self)
    }
 
    fn make_parameter(&mut self, name: &T::Parameter) -> Container<Primitive<T>> {
        Primitive::Parameter(name.clone()).insert(self)
    }
 
    fn make_input(&mut self, index: &T::Input) -> Container<Primitive<T>> {
        Primitive::Input(index.clone()).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()) {
            let sum = sum.negate(self);
            self.make_sigmoid(!minus, &sum)
        } else {
            Primitive::Sigmoid(minus, sum.clone()).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 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);
        }
        if right_sum.terms.len() == 0 {
            return left_sum.adjust_constant(&constant, self);
        }
 
        // Begin port of weird logic
        let left_terms = left_sum.conditional_negate(self);
        let right_terms = right_sum.conditional_negate(self);
        let left_length = left_terms.len();
        let right_length = right_terms.len();
 
        let mut terms = Vec::with_capacity(left_length + right_length);
        let mut left_index = 0;
        let mut right_index = 0;
 
        while left_index < left_length && right_index < right_length {
            let ref left_term = left_terms[left_index];
            let ref right_term = right_terms[right_length];
            match left_term.fuzzy_cmp(right_term.as_ref()) {
                Ordering::Less => {
                    terms.push(left_term.clone());
                    left_index += 1;
                }
                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());
                    }
                    left_index += 1;
                    right_index += 1;
                }
                Ordering::Greater => {
                    terms.push(right_term.clone());
                    right_index += 1;
                }
            }
            if left_index == left_length {
                terms.extend_from_slice(&right_terms[..].split_at(right_index).1);
            }
            if right_index == right_length {
                terms.extend_from_slice(&left_terms[..].split_at(left_index).1);
            }
        }
 
        terms.shrink_to_fit(); // Why not.
 
        if terms.len() == 0 || terms[0].coefficient >= T::Constant::zero() {
            self.make_sum(false, &constant, &terms)
        } else {
            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>> {
        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 mut outer_terms = Vec::with_capacity(right_sum.terms.len());
        if left_sum.constant != T::Constant::zero() {
            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() {
                    outer_terms.push(outer_term.negate(self));
                } else {
                    outer_terms.push(outer_term);
                }
            }
        }
        outer_terms.shrink_to_fit();
        let outer = self.make_sum((left_sum.constant < T::Constant::zero()) != minus,
                                  &T::Constant::zero(),
                                  &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() {
            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() {
                    inner_terms.push(inner_term.negate(self));
                } else {
                    inner_terms.push(inner_term);
                }
            }
        }
        inner_terms.shrink_to_fit();
        let inner = self.make_sum((right_sum.constant < T::Constant::zero()) != minus,
                                  &T::Constant::zero(),
                                  &inner_terms);
 
        let mut last = self.number(&T::Constant::zero());
        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);
            }
        }
 
        last = self.add(&last, &inner);
        last = self.add(&last, &outer);
        self.add(&last, &first)
    }
 
    fn _combine_terms(&mut self,
                      left: &Vec<Container<Power<T>>>,
                      right: &Vec<Container<Power<T>>>)
                      -> Vec<Container<Power<T>>> {
        let left_length = left.len();
        let right_length = right.len();
 
        let mut powers = Vec::with_capacity(left_length + right_length);
        let mut left_index = 0;
        let mut right_index = 0;
 
        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) {
                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());
                    left_index += 1;
                    right_index += 1;
                }
                Ordering::Greater => {
                    powers.push(right_power.clone());
                    right_index += 1;
                }
            }
            if left_index == left_length {
                powers.extend_from_slice(&right[..].split_at(right_index).1);
            }
            if right_index == right_length {
                powers.extend_from_slice(&left[..].split_at(left_index).1);
            }
        }
 
        powers.shrink_to_fit(); // Why not.
 
        powers
 
    }
}
 
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> {
    fn partial_derivative(&self,
                          variable: &T::Parameter,
                          env: &mut Environment<T>)
                          -> Container<Sum<T>>;
 
    fn fancy_clone(&self, env: &mut Environment<T>) -> Self;
 
    fn get_storage<'a>(&self, env: &'a mut Environment<T>) -> &'a mut SelfMap<Self>
        where Self: Sized;
 
    fn insert(&self, env: &mut Environment<T>) -> Container<Self>
        where Self: Sized + Clone + Eq + Hash
    {
        if !self.get_storage(env).contains_key(self) {
            let key = self.fancy_clone(env);
            let value = key.clone();
            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)
    }
}
 
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);
        }
        match env.sum_derivative.get(&(self.clone(), variable.clone())) {
            Some(derivative) => derivative.clone(),
            None => unreachable!(),
        }
    }
 
    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<_>>(),
        }
    }
 
    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)
    }
}
 
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 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()
    }
}
 
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 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)
            }
        }
    }
}
 
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> {
        match self {
            &Primitive::Sigmoid(minus, ref a) => Primitive::Sigmoid(minus, a.insert(env)),
            _ => self.clone(),
        }
    }
 
    fn get_storage<'a>(&self, env: &'a mut Environment<T>) -> &'a mut SelfMap<Primitive<T>> {
        &mut env.primitive
    }
}
 
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {}
}

Comments