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 Sum { fn negate(&self, env: &mut Environment) -> Container> { env.make_sum(!self.minus, &self.constant, &self.terms) } } impl Product { fn negate(&self, env: &mut Environment) -> Container> { env.make_product(&-self.coefficient.clone(), &self.powers).unwrap() } } type SelfMap = HashMap>; struct Environment { sum: SelfMap>, product: SelfMap>, power: SelfMap>, primitive: SelfMap>, sum_derivative: HashMap<(Sum, T::Parameter), Container>>, product_derivative: HashMap<(Product, T::Parameter), Container>>, power_derivative: HashMap<(Power, T::Parameter), Container>>, primitive_derivative: HashMap<(Primitive, T::Parameter), Container>>, } impl Sum { 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) -> Container> { env.make_sum(self.minus, &(if self.minus { -constant.clone() } else { constant.clone() }), &self.terms) } fn conditional_negate(&self, env: &mut Environment) -> Vec>> { if self.minus { env.negate_products(&self.terms) } else { self.terms .iter() .map(|p| p.insert(env)) .collect::>() } } } impl Environment { fn make_sum(&mut self, minus: bool, constant: &T::Constant, terms: &Vec>>) -> Container> { 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>>) -> Option>> { 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>, exponent: &T::Exponent) -> Option>> { 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::SystemVariable(index.clone()).insert(self) } fn make_parameter(&mut self, name: &T::Parameter) -> Container> { Primitive::Parameter(name.clone()).insert(self) } fn make_input(&mut self, index: &T::Input) -> Container> { Primitive::Input(index.clone()).insert(self) } fn make_sigmoid(&mut self, minus: bool, sum: &Container>) -> Container> { 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>>) -> Vec>> { terms.iter().map(|p| p.negate(self)).collect::>() } fn number(&mut self, number: &T::Constant) -> Container> { self.make_sum(false, number, &vec![]) } fn add, B: Node>(&mut self, left: &Container, right: &Container) -> Container> { 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, B: Node>(&mut self, left: &Container, right: &Container) -> Container> { 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>>, right: &Vec>>) -> Vec>> { 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 { fn as_sum(&self, env: &mut Environment) -> Container>; } trait Productable { fn as_product(&self, env: &mut Environment) -> Container>; } fn _as_sum(productable: &Productable, env: &mut Environment) -> Container> { 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 Summable for Product { fn as_sum(&self, env: &mut Environment) -> Container> { _as_sum(self, env) } } impl Summable for Power { fn as_sum(&self, env: &mut Environment) -> Container> { _as_sum(self, env) } } impl Summable for Primitive { fn as_sum(&self, env: &mut Environment) -> Container> { _as_sum(self, env) } } trait Node: Summable { fn partial_derivative(&self, variable: &T::Parameter, env: &mut Environment) -> Container>; fn fancy_clone(&self, env: &mut Environment) -> Self; fn get_storage<'a>(&self, env: &'a mut Environment) -> &'a mut SelfMap where Self: Sized; fn insert(&self, env: &mut Environment) -> Container 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 Summable for Sum { fn as_sum(&self, env: &mut Environment) -> Container> { self.insert(env) } } impl Node for Sum { fn partial_derivative(&self, variable: &T::Parameter, env: &mut Environment) -> Container> { 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) -> Sum { 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::>(), } } fn get_storage<'a>(&self, env: &'a mut Environment) -> &'a mut SelfMap> { &mut env.sum } } impl Productable for Product { fn as_product(&self, env: &mut Environment) -> Container> { self.insert(env) } } impl Node for Product { fn partial_derivative(&self, variable: &T::Parameter, env: &mut Environment) -> Container> { 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) -> Product { Product { pre_hash: self.pre_hash.clone(), coefficient: self.coefficient.clone(), powers: self.powers .iter() .map(|p| p.insert(env)) .collect::>(), } } fn get_storage<'a>(&self, env: &'a mut Environment) -> &'a mut SelfMap> { &mut env.product } } impl Productable for Power { fn as_product(&self, env: &mut Environment) -> Container> { let power = self.insert(env); env.make_product(&T::Constant::one(), &vec![power]).unwrap() } } impl Node for Power { fn partial_derivative(&self, variable: &T::Parameter, env: &mut Environment) -> Container> { unimplemented!(); } fn fancy_clone(&self, env: &mut Environment) -> Power { Power { exponent: self.exponent.clone(), primitive: self.primitive.insert(env), } } fn get_storage<'a>(&self, env: &'a mut Environment) -> &'a mut SelfMap> { &mut env.power } } impl Primitive { fn as_power(&self, env: &mut Environment) -> Container> { let primitive = self.insert(env); env.make_power(&primitive, &T::Exponent::one()).unwrap() } } impl Productable for Primitive { fn as_product(&self, env: &mut Environment) -> Container> { 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 Node for Primitive { fn partial_derivative(&self, variable: &T::Parameter, env: &mut Environment) -> Container> { unimplemented!(); } fn fancy_clone(&self, env: &mut Environment) -> Primitive { match self { &Primitive::Sigmoid(minus, ref a) => Primitive::Sigmoid(minus, a.insert(env)), _ => self.clone(), } } fn get_storage<'a>(&self, env: &'a mut Environment) -> &'a mut SelfMap> { &mut env.primitive } } #[cfg(test)] mod tests { #[test] fn it_works() {} }