use std::ops::Add;
use static_assertions::assert_impl_all;
use crate::{Differentiable, Tensorial};
use super::{Designation, Misbinding, ValueRef, Witness};
assert_impl_all!(Field<f64>: Send, Sync);
#[derive(Debug, Clone)]
pub struct Field<Data> {
witness: Witness,
payloads: Vec<Data>,
}
pub type Gradients<Data> = Field<Data>;
impl<Data: Differentiable> Field<Data> {
pub(crate) fn new(witness: Witness, payloads: Vec<Data>) -> Self {
Self { witness, payloads }
}
pub(crate) fn locate(&self, designation: Designation<'_, Data>) -> Result<usize, Misbinding> {
let coverage = self.payloads.len();
match designation {
Designation::Bound { tape, id } => {
if !tape.same_origin(&self.witness) {
return Err(Misbinding::ForeignOrigin);
}
if !tape.agrees_with(&self.witness, coverage) {
return Err(Misbinding::DivergentBranch);
}
if id.index() >= coverage {
return Err(Misbinding::OutOfCoverage);
}
Ok(id.index())
}
Designation::Named(symbol) => self.witness.probe(symbol, coverage).map(|id| id.index()),
}
}
pub fn of(&self, value: impl ValueRef<Data>) -> &Data {
let designation = value.designation();
let subject = match &designation {
Designation::Bound { .. } => "value",
Designation::Named(_) => "symbol",
};
let index = match self.locate(designation) {
Ok(index) => index,
Err(Misbinding::ForeignOrigin) => {
panic!("{subject} belongs to a different network lineage")
}
Err(Misbinding::DivergentBranch) => {
panic!("{subject} belongs to a divergent fork of the network")
}
Err(Misbinding::OutOfCoverage) => {
panic!("{subject} was allocated after this field was produced")
}
};
&self.payloads[index]
}
pub fn map(&self, transform: impl Fn(&Data) -> Data) -> Self {
Self {
witness: self.witness.clone(),
payloads: self.payloads.iter().map(transform).collect(),
}
}
pub fn zip(&self, other: &Self, combine: impl Fn(&Data, &Data) -> Data) -> Self {
self.assert_compatible_witness(other);
Self {
witness: self.witness.clone(),
payloads: self
.payloads
.iter()
.zip(&other.payloads)
.map(|(left, right)| combine(left, right))
.collect(),
}
}
pub(crate) fn payloads(&self) -> &[Data] {
&self.payloads
}
pub(crate) fn witness(&self) -> &Witness {
&self.witness
}
fn assert_compatible_witness(&self, other: &Self) {
assert!(
self.witness.same_origin(&other.witness),
"fields belong to different network lineages"
);
assert_eq!(
self.payloads.len(),
other.payloads.len(),
"fields cover different generations of the network"
);
assert!(
self.witness
.agrees_with(&other.witness, self.payloads.len()),
"fields belong to divergent forks of the network"
);
}
}
impl<Data: Tensorial> Field<Data> {
pub fn scale(&self, factor: &Data) -> Self {
self.map(|value| value.clone() * factor.broadcast_like(value))
}
}
impl<Data: Differentiable> Add for &Field<Data> {
type Output = Field<Data>;
fn add(self, rhs: Self) -> Field<Data> {
self.zip(rhs, |left, right| left.clone() + right.clone())
}
}
impl<Data: Differentiable> Add for Field<Data> {
type Output = Field<Data>;
fn add(self, rhs: Self) -> Field<Data> {
&self + &rhs
}
}
#[cfg(test)]
#[path = "tests/field_tests.rs"]
mod tests;