use crate::{
array,
linear_algebra::{FixDimension, Vector},
Model,
};
use serde_derive::{Deserialize, Serialize};
impl Model for f64 {
type Features = ();
type Target = f64;
fn num_coefficients(&self) -> usize {
1
}
fn coefficient(&mut self, coefficient: usize) -> &mut f64 {
match coefficient {
0 => self,
_ => panic!("coefficient index out of range"),
}
}
fn predict(&self, _: &()) -> f64 {
*self
}
fn gradient(&self, coefficient: usize, _: &()) -> f64 {
match coefficient {
0 => 1.0,
_ => panic!("coefficient index out of range"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Linear<V> {
pub m: V,
pub c: f64,
}
impl<V: Vector> Linear<V> {
pub fn with_feature_dimension(dimension: usize) -> Self {
Linear {
m: V::zero_from_dimension(dimension),
c: 0f64,
}
}
}
impl<V> Default for Linear<V>
where
V: FixDimension,
{
fn default() -> Self {
Linear {
m: V::zero(),
c: 0f64,
}
}
}
impl<V> Model for Linear<V>
where
V: Vector,
{
type Features = V;
type Target = f64;
fn num_coefficients(&self) -> usize {
self.m.dimension() + 1
}
fn coefficient(&mut self, coefficient: usize) -> &mut f64 {
if coefficient == self.m.dimension() {
&mut self.c
} else {
self.m.at_mut(coefficient)
}
}
fn predict(&self, input: &V) -> f64 {
self.m.dot(input) + self.c
}
fn gradient(&self, coefficient: usize, input: &V) -> f64 {
if coefficient == self.m.dimension() {
1.0 } else {
input.at(coefficient) }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Logistic<V>(Linear<V>);
impl<V: Vector> Logistic<V> {
pub fn with_feature_dimension(dimension: usize) -> Self {
Logistic(Linear::with_feature_dimension(dimension))
}
}
impl<V> Default for Logistic<V>
where
V: FixDimension,
{
fn default() -> Self {
Logistic(Linear::default())
}
}
impl<V> Model for Logistic<V>
where
Linear<V>: Model<Features = V, Target = f64>,
{
type Features = V;
type Target = f64;
fn num_coefficients(&self) -> usize {
self.0.num_coefficients()
}
fn coefficient(&mut self, coefficient: usize) -> &mut f64 {
self.0.coefficient(coefficient)
}
fn predict(&self, input: &V) -> f64 {
1.0 / (1.0 + self.0.predict(input).exp())
}
fn gradient(&self, coefficient: usize, input: &V) -> f64 {
let p = self.predict(input);
-p * (1.0 - p) * self.0.gradient(coefficient, input)
}
}
#[derive(Clone)]
pub struct GeneralizedLinearModel<V, G, Dg> {
pub linear: Linear<V>,
pub g: G,
pub g_derivate: Dg,
}
impl<V, G, Dg> GeneralizedLinearModel<V, G, Dg>
where
G: Fn(f64) -> f64,
Dg: Fn(f64) -> f64,
{
pub fn new(g: G, g_derivate: Dg) -> GeneralizedLinearModel<V, G, Dg>
where
V: FixDimension,
{
GeneralizedLinearModel {
linear: Linear::default(),
g,
g_derivate,
}
}
}
impl<V, F, Df> Model for GeneralizedLinearModel<V, F, Df>
where
F: Fn(f64) -> f64,
Df: Fn(f64) -> f64,
Linear<V>: Model<Features = V, Target = f64>,
{
type Features = V;
type Target = f64;
fn num_coefficients(&self) -> usize {
self.linear.num_coefficients()
}
fn coefficient(&mut self, coefficient: usize) -> &mut f64 {
self.linear.coefficient(coefficient)
}
fn predict(&self, input: &V) -> f64 {
let f = &self.g;
f(self.linear.predict(&input))
}
fn gradient(&self, coefficient: usize, input: &V) -> f64 {
let f = &self.g_derivate;
f(self.linear.predict(&input)) * self.linear.gradient(coefficient, input)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct OneVsRest<T>(T);
impl<T> OneVsRest<T> {
pub fn new(t: T) -> Self {
OneVsRest(t)
}
}
impl<T> Model for OneVsRest<T>
where
T: array::Array,
T::Element: Model<Target = f64>,
{
type Features = <T::Element as Model>::Features;
type Target = T::Vector;
fn num_coefficients(&self) -> usize {
let models = &self.0;
models.length() * models.at_ref(0).num_coefficients()
}
fn coefficient(&mut self, index: usize) -> &mut f64 {
let models = &mut self.0;
let class = index % models.length();
let n = index / models.length();
models.at_mut(class).coefficient(n)
}
fn predict(&self, input: &Self::Features) -> Self::Target {
let models = &self.0;
let mut result = Self::Target::zero_from_dimension(models.length());
for i in 0..models.length() {
*result.at_mut(i) = models.at_ref(i).predict(input);
}
result
}
fn gradient(&self, coefficient: usize, input: &Self::Features) -> Self::Target {
let models = &self.0;
let class = coefficient % models.length();
let mut result = Self::Target::zero_from_dimension(models.length());
*result.at_mut(class) = models
.at_ref(class)
.gradient(coefficient / models.length(), input);
result
}
}