mwchase icon

LC:NN upload 2a

mwchase | PRO | 03/12/17 05:48:48 PM UTC | 0 ⭐ | 854 👁️ | Never ⏰ | []
Rust |

16.64 KB

|

None

|

0 👍

/

0 👎

extern crate num;
 
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::hash_map::{DefaultHasher, Entry};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
 
use num::{Integer, One, Signed, Zero};
 
trait NodeData {
    type Constant: Hash + Eq + Ord + Clone + Signed;
    type Coefficient: Hash + Eq + Ord + Clone + Signed;
    type Exponent: Hash + Clone + Integer;
    type Input: Hash + Eq + Clone;
    type SystemVariable: Hash + Eq + Clone;
    type Parameter: Hash + Eq + Clone;
}
 
struct Sum<T: NodeData> {
    pre_hash: u64,
    minus: bool,
    constant: T::Constant,
    terms: Vec<Arc<Product<T>>>,
}
 
impl<T: NodeData> Sum<T> {
    fn negate(&self, env: &mut Environment<T>) -> Arc<Sum<T>> {
        env.make_sum(!self.minus, &self.constant, &self.terms)
    }
}
 
struct Product<T: NodeData> {
    pre_hash: u64,
    coefficient: T::Coefficient,
    powers: Vec<Arc<Power<T>>>,
}
 
impl<T: NodeData> Product<T> {
    fn negate(&self, env: &mut Environment<T>) -> Arc<Product<T>> {
        env.make_product(&-self.coefficient.clone(), &self.powers).unwrap()
    }
}
 
struct Power<T: NodeData> {
    exponent: T::Exponent,
    primitive: Arc<Primitive<T>>,
}
 
enum Primitive<T: NodeData> {
    Input(T::Input),
    SystemVariable(T::SystemVariable),
    Parameter(T::Parameter),
    Sigmoid(bool, Arc<Sum<T>>),
}
 
type SelfMap<T> = HashMap<T, Arc<T>>;
 
struct Environment<T: NodeData> {
    sum: SelfMap<Sum<T>>,
    product: SelfMap<Product<T>>,
    power: SelfMap<Power<T>>,
    primitive: SelfMap<Primitive<T>>, //I could put in the maps for the various partial
                                      //derivatives, but that's a
                                      //non-vital optimization.
}
 
impl<T: NodeData> Environment<T> {
    fn make_sum(&mut self,
                minus: bool,
                constant: &T::Constant,
                terms: &Vec<Arc<Product<T>>>)
                -> Arc<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::Coefficient,
                    powers: &Vec<Arc<Power<T>>>)
                    -> Option<Arc<Product<T>>> {
        if coefficient.clone() == T::Coefficient::zero() || powers.len() == 0 {
            None
        } else {
            Some(Product::new(coefficient.clone(), powers.clone()).insert(self))
        }
    }
 
    fn make_power(&mut self,
                  primitive: &Arc<Primitive<T>>,
                  exponent: &T::Exponent)
                  -> Option<Arc<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) -> Arc<Primitive<T>> {
        Primitive::SystemVariable(index.clone()).insert(self)
    }
 
    fn make_parameter(&mut self, name: &T::Parameter) -> Arc<Primitive<T>> {
        Primitive::Parameter(name.clone()).insert(self)
    }
 
    fn make_input(&mut self, index: &T::Input) -> Arc<Primitive<T>> {
        Primitive::Input(index.clone()).insert(self)
    }
 
    fn make_sigmoid(&mut self, minus: bool, sum: &Arc<Sum<T>>) -> Arc<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 number(&mut self, &T::Constant)
}
 
trait Summable<T: NodeData> {
    fn as_sum(&self, env: &mut Environment<T>) -> Arc<Sum<T>>;
}
 
trait Productable<T: NodeData> {
    fn as_product(&self, env: &mut Environment<T>) -> Arc<Product<T>>;
}
 
fn _as_sum<T: NodeData>(productable: &Productable<T>, env: &mut Environment<T>) -> Arc<Sum<T>> {
    let product = productable.as_product(env);
    if product.coefficient < T::Coefficient::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>) -> Arc<Sum<T>> {
        _as_sum(self, env)
    }
}
 
impl<T: NodeData> Summable<T> for Power<T> {
    fn as_sum(&self, env: &mut Environment<T>) -> Arc<Sum<T>> {
        _as_sum(self, env)
    }
}
 
impl<T: NodeData> Summable<T> for Primitive<T> {
    fn as_sum(&self, env: &mut Environment<T>) -> Arc<Sum<T>> {
        _as_sum(self, env)
    }
}
 
trait Node<T: NodeData>: Summable<T> {
    fn partial_derivative(&self, variable: &T::Parameter, env: &mut Environment<T>) -> Arc<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>) -> Arc<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, Arc::new(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>) -> Arc<Sum<T>> {
        self.insert(env)
    }
}
 
impl<T: NodeData> Node<T> for Sum<T> {
    fn partial_derivative(&self, variable: &T::Parameter, env: &mut Environment<T>) -> Arc<Sum<T>> {
        unimplemented!();
    }
 
    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>) -> Arc<Product<T>> {
        self.insert(env)
    }
}
 
impl<T: NodeData> Node<T> for Product<T> {
    fn partial_derivative(&self, variable: &T::Parameter, env: &mut Environment<T>) -> Arc<Sum<T>> {
        unimplemented!();
    }
 
    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>) -> Arc<Product<T>> {
        let power = self.insert(env);
        env.make_product(&T::Coefficient::one(), &vec![power]).unwrap()
    }
}
 
impl<T: NodeData> Node<T> for Power<T> {
    fn partial_derivative(&self, variable: &T::Parameter, env: &mut Environment<T>) -> Arc<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>) -> Arc<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>) -> Arc<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::Coefficient::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>) -> Arc<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
    }
}
 
impl<T: NodeData> Sum<T> {
    fn new(minus: bool, constant: T::Constant, terms: Vec<Arc<Product<T>>>) -> Sum<T> {
        let mut s = DefaultHasher::new();
        minus.hash(&mut s);
        constant.hash(&mut s);
        terms.hash(&mut s);
        Sum {
            pre_hash: s.finish(),
            minus: minus,
            constant: constant,
            terms: terms,
        }
    }
}
 
impl<T: NodeData> Product<T> {
    fn new(coefficient: T::Coefficient, powers: Vec<Arc<Power<T>>>) -> Product<T> {
        let mut s = DefaultHasher::new();
        coefficient.hash(&mut s);
        powers.hash(&mut s);
        Product {
            pre_hash: s.finish(),
            coefficient: coefficient,
            powers: powers,
        }
    }
}
 
impl<T: NodeData> Power<T> {
    fn new(exponent: T::Exponent, primitive: Arc<Primitive<T>>) -> Power<T> {
        Power {
            exponent: exponent,
            primitive: primitive,
        }
    }
}
 
impl<T: NodeData> Ord for Sum<T> {
    fn cmp(&self, other: &Sum<T>) -> Ordering {
        let cmp_result = self.constant.cmp(&other.constant);
        match cmp_result {
            Ordering::Equal => {
                for (self_term, other_term) in
                    self.terms
                        .clone()
                        .into_iter()
                        .zip(other.terms.clone()) {
                    let cmp_result = self_term.cmp(&other_term);
                    match cmp_result {
                        Ordering::Equal => (),
                        _ => return cmp_result,
                    }
                }
                let cmp_result = self.terms.len().cmp(&other.terms.len());
                match cmp_result {
                    Ordering::Equal => self.minus.cmp(&other.minus),
                    _ => cmp_result,
                }
            }
            _ => cmp_result,
        }
    }
}
 
impl<T: NodeData> Ord for Product<T> {
    fn cmp(&self, other: &Product<T>) -> Ordering {
        let cmp_result = self.fuzzy_cmp(other);
        match cmp_result {
            Ordering::Equal => self.coefficient.cmp(&other.coefficient),
            _ => cmp_result,
        }
    }
}
 
impl<T: NodeData> Product<T> {
    fn fuzzy_cmp(&self, other: &Product<T>) -> Ordering {
        for (self_power, other_power) in
            self.powers
                .clone()
                .into_iter()
                .zip(other.powers.clone()) {
            let cmp_result = self_power.cmp(&other_power);
            match cmp_result {
                Ordering::Equal => (),
                _ => return cmp_result,
            }
        }
        self.powers.len().cmp(&other.powers.len())
    }
}
 
impl<T: NodeData> Ord for Power<T> {
    fn cmp(&self, other: &Power<T>) -> Ordering {
        let cmp_result = self.primitive.cmp(&other.primitive);
        match cmp_result {
            Ordering::Equal => self.exponent.cmp(&other.exponent),
            _ => cmp_result,
        }
    }
}
 
impl<T: NodeData> Ord for Primitive<T> {
    fn cmp(&self, other: &Primitive<T>) -> Ordering {
        unimplemented!()
    }
}
 
impl<T: NodeData> PartialOrd for Sum<T> {
    fn partial_cmp(&self, other: &Sum<T>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl<T: NodeData> PartialOrd for Product<T> {
    fn partial_cmp(&self, other: &Product<T>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl<T: NodeData> PartialOrd for Power<T> {
    fn partial_cmp(&self, other: &Power<T>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl<T: NodeData> PartialOrd for Primitive<T> {
    fn partial_cmp(&self, other: &Primitive<T>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
 
impl<T: NodeData> Hash for Sum<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.pre_hash.hash(state);
    }
}
 
impl<T: NodeData> Hash for Product<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.pre_hash.hash(state);
    }
}
 
impl<T: NodeData> Hash for Power<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.exponent.hash(state);
        self.primitive.hash(state);
    }
}
 
impl<T: NodeData> Hash for Primitive<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            &Primitive::Input(ref a) => {
                "Input".hash(state);
                a.hash(state)
            }
            &Primitive::SystemVariable(ref a) => {
                "SystemVariable".hash(state);
                a.hash(state)
            }
            &Primitive::Parameter(ref a) => {
                "Parameter".hash(state);
                a.hash(state)
            }
            &Primitive::Sigmoid(minus, ref a) => {
                "Sigmoid".hash(state);
                minus.hash(state);
                a.hash(state)
            }
        }
    }
}
 
impl<T: NodeData> PartialEq for Sum<T> {
    fn eq(&self, other: &Sum<T>) -> bool {
        self.minus == other.minus && self.constant == other.constant && self.terms == other.terms
    }
}
 
impl<T: NodeData> PartialEq for Product<T> {
    fn eq(&self, other: &Product<T>) -> bool {
        self.coefficient == other.coefficient && self.powers == other.powers
    }
}
 
impl<T: NodeData> PartialEq for Power<T> {
    fn eq(&self, other: &Power<T>) -> bool {
        self.exponent == other.exponent && self.primitive == other.primitive
    }
}
 
impl<T: NodeData> PartialEq for Primitive<T> {
    fn eq(&self, other: &Primitive<T>) -> bool {
        match (self, other) {
            (&Primitive::Input(ref a), &Primitive::Input(ref b)) => a == b,
            (&Primitive::SystemVariable(ref a), &Primitive::SystemVariable(ref b)) => a == b,
            (&Primitive::Parameter(ref a), &Primitive::Parameter(ref b)) => a == b,
            (&Primitive::Sigmoid(minus_a, ref a), &Primitive::Sigmoid(minus_b, ref b)) => {
                minus_a == minus_b && a == b
            }
            _ => false,
        }
    }
}
 
impl<T: NodeData> Eq for Sum<T> {}
impl<T: NodeData> Eq for Product<T> {}
impl<T: NodeData> Eq for Power<T> {}
impl<T: NodeData> Eq for Primitive<T> {}
 
impl<T: NodeData> Clone for Sum<T> {
    fn clone(&self) -> Sum<T> {
        Sum {
            pre_hash: self.pre_hash.clone(),
            minus: self.minus.clone(),
            constant: self.constant.clone(),
            terms: self.terms.clone(),
        }
    }
}
 
impl<T: NodeData> Clone for Product<T> {
    fn clone(&self) -> Product<T> {
        Product {
            pre_hash: self.pre_hash.clone(),
            coefficient: self.coefficient.clone(),
            powers: self.powers.clone(),
        }
    }
}
 
impl<T: NodeData> Clone for Power<T> {
    fn clone(&self) -> Power<T> {
        Power {
            exponent: self.exponent.clone(),
            primitive: self.primitive.clone(),
        }
    }
}
 
impl<T: NodeData> Clone for Primitive<T> {
    fn clone(&self) -> Primitive<T> {
        match self {
            &Primitive::Input(ref a) => Primitive::Input(a.clone()),
            &Primitive::SystemVariable(ref a) => Primitive::SystemVariable(a.clone()),
            &Primitive::Parameter(ref a) => Primitive::Parameter(a.clone()),
            &Primitive::Sigmoid(minus, ref a) => Primitive::Sigmoid(minus, a.clone()),
        }
    }
}
 
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {}
}

Comments