use std::fmt;
use std::ops::{Add, Div, Mul, Neg, Sub};
use std::ptr;
use static_assertions::assert_impl_all;
use crate::{Element, MapOperation, Shape, Tensor};
use crate::op::Op;
use super::{Symbol, Tape};
assert_impl_all!(Value<'static, f64>: Send, Sync, Copy);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct ValueId(pub(crate) usize);
impl ValueId {
pub(crate) fn index(self) -> usize {
self.0
}
}
pub struct Value<'tape, E> {
tape: &'tape Tape<E>,
id: ValueId,
}
impl<'tape, E: Element> Value<'tape, E> {
pub(crate) fn bind(tape: &'tape Tape<E>, id: ValueId) -> Self {
Self { tape, id }
}
pub(crate) fn id(&self) -> ValueId {
self.id
}
pub fn tape(&self) -> &'tape Tape<E> {
self.tape
}
pub fn symbol(&self) -> Symbol {
Symbol {
origin: self.tape.origin(),
id: self.id,
}
}
}
impl<E: Element> From<Value<'_, E>> for Symbol {
fn from(value: Value<'_, E>) -> Symbol {
value.symbol()
}
}
impl<'tape, E: Element> Value<'tape, E> {
#[cfg(test)]
pub(crate) fn op(&self) -> Op<Tensor<E>> {
self.tape.with_node(self.id, |op| op.clone())
}
#[cfg(test)]
pub(crate) fn operands(&self) -> Vec<ValueId> {
self.tape.operands_of(self.id).as_slice().to_vec()
}
pub fn shape(&self) -> Shape {
self.tape.shape(self.id)
}
pub fn payload(&self) -> Option<Tensor<E>> {
self.tape.payload_of(self.id)
}
fn apply(&self, op: Op<Tensor<E>>, operands: &[ValueId]) -> Self {
let id = self.tape.record_node(op, operands);
Self::bind(self.tape, id)
}
pub(crate) fn literal(&self, data: Tensor<E>) -> Self {
Self::bind(self.tape, self.tape.record_node(Op::leaf(data), &[]))
}
fn assert_same_tape(&self, other: &Self) {
assert!(
ptr::eq(self.tape, other.tape),
"values belong to different tapes"
);
}
}
impl<'tape, E: Element> Value<'tape, E> {
pub fn tanh(self) -> Self {
self.apply(Op::map(MapOperation::Tanh), &[self.id])
}
pub fn exp(self) -> Self {
self.apply(Op::map(MapOperation::Exp), &[self.id])
}
pub fn ln(self) -> Self {
self.apply(Op::map(MapOperation::Ln), &[self.id])
}
pub fn sqrt(self) -> Self {
self.apply(Op::map(MapOperation::Sqrt), &[self.id])
}
pub fn sin(self) -> Self {
self.apply(Op::map(MapOperation::Sin), &[self.id])
}
pub fn cos(self) -> Self {
self.apply(Op::map(MapOperation::Cos), &[self.id])
}
pub fn log1p(self) -> Self {
self.apply(Op::map(MapOperation::Log1p), &[self.id])
}
pub fn expm1(self) -> Self {
self.apply(Op::map(MapOperation::Expm1), &[self.id])
}
pub fn erf(self) -> Self {
self.apply(Op::map(MapOperation::Erf), &[self.id])
}
pub fn erf_derivative(self) -> Self {
self.apply(Op::map(MapOperation::ErfDerivative), &[self.id])
}
pub fn powf(self, exponent: Self) -> Self {
self.assert_same_tape(&exponent);
self.apply(Op::powf(), &[self.id, exponent.id])
}
pub fn maximum(self, rhs: Self) -> Self {
self.assert_same_tape(&rhs);
self.apply(Op::maximum(), &[self.id, rhs.id])
}
pub fn step(self, threshold: Self) -> Self {
self.assert_same_tape(&threshold);
self.apply(Op::step(), &[self.id, threshold.id])
}
}
impl<'tape, E: Element> Value<'tape, E> {
pub fn matmul(self, rhs: Self) -> Self {
self.assert_same_tape(&rhs);
self.apply(Op::matmul(), &[self.id, rhs.id])
}
pub fn sum(self) -> Self {
self.apply(Op::sum(), &[self.id])
}
pub fn sum_along(self, axis: usize) -> Self {
self.apply(Op::sum_along(axis), &[self.id])
}
pub fn broadcast(self, shape: impl Into<Shape>) -> Self {
self.apply(Op::broadcast(shape.into()), &[self.id])
}
pub fn broadcast_along(self, axis: usize, extent: usize) -> Self {
self.apply(Op::broadcast_along(axis, extent), &[self.id])
}
pub fn reshape(self, shape: impl Into<Shape>) -> Self {
self.apply(Op::reshape(shape.into()), &[self.id])
}
pub fn permute(self, order: impl IntoIterator<Item = usize>) -> Self {
self.apply(Op::permute(order), &[self.id])
}
pub fn narrow(self, axis: usize, start: usize, len: usize) -> Self {
self.apply(Op::narrow(axis, start, len), &[self.id])
}
pub fn pad(self, axis: usize, start: usize, full_extent: usize) -> Self {
self.apply(Op::pad(axis, start, full_extent), &[self.id])
}
pub fn unfold(self, axis: usize, size: usize, step: usize, dilation: usize) -> Self {
self.apply(Op::unfold(axis, size, step, dilation), &[self.id])
}
pub fn fold(
self,
axis: usize,
size: usize,
step: usize,
dilation: usize,
extent: usize,
) -> Self {
self.apply(Op::fold(axis, size, step, dilation, extent), &[self.id])
}
pub fn gather(self, selection: Self) -> Self {
self.assert_same_tape(&selection);
self.apply(Op::gather(), &[self.id, selection.id])
}
pub fn scatter(self, selection: Self) -> Self {
self.assert_same_tape(&selection);
self.apply(Op::scatter(), &[self.id, selection.id])
}
pub fn log_softmax(self, axis: usize) -> Self {
self.apply(Op::log_softmax(axis), &[self.id])
}
pub fn logsumexp(self, axis: usize) -> Self {
self.apply(Op::log_sum_exp(axis), &[self.id])
}
}
impl<E> Clone for Value<'_, E> {
fn clone(&self) -> Self {
*self
}
}
impl<E> Copy for Value<'_, E> {}
impl<E> fmt::Debug for Value<'_, E> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("Value")
.field("id", &self.id)
.finish()
}
}
impl<'tape, E: Element> Add for Value<'tape, E> {
type Output = Value<'tape, E>;
fn add(self, rhs: Self) -> Self::Output {
self.assert_same_tape(&rhs);
self.apply(Op::add(), &[self.id, rhs.id])
}
}
impl<'tape, E: Element> Sub for Value<'tape, E> {
type Output = Value<'tape, E>;
fn sub(self, rhs: Self) -> Self::Output {
self.assert_same_tape(&rhs);
self.apply(Op::sub(), &[self.id, rhs.id])
}
}
impl<'tape, E: Element> Mul for Value<'tape, E> {
type Output = Value<'tape, E>;
fn mul(self, rhs: Self) -> Self::Output {
self.assert_same_tape(&rhs);
self.apply(Op::mul(), &[self.id, rhs.id])
}
}
impl<'tape, E: Element> Div for Value<'tape, E> {
type Output = Value<'tape, E>;
fn div(self, rhs: Self) -> Self::Output {
self.assert_same_tape(&rhs);
self.apply(Op::div(), &[self.id, rhs.id])
}
}
impl<'tape, E: Element> Neg for Value<'tape, E> {
type Output = Value<'tape, E>;
fn neg(self) -> Self::Output {
self.apply(Op::neg(), &[self.id])
}
}
impl<'tape, E: Element> Add<Tensor<E>> for Value<'tape, E> {
type Output = Value<'tape, E>;
fn add(self, rhs: Tensor<E>) -> Self::Output {
let literal = self.literal(rhs);
self + literal
}
}
impl<'tape, E: Element> Sub<Tensor<E>> for Value<'tape, E> {
type Output = Value<'tape, E>;
fn sub(self, rhs: Tensor<E>) -> Self::Output {
let literal = self.literal(rhs);
self - literal
}
}
impl<'tape, E: Element> Mul<Tensor<E>> for Value<'tape, E> {
type Output = Value<'tape, E>;
fn mul(self, rhs: Tensor<E>) -> Self::Output {
let literal = self.literal(rhs);
self * literal
}
}
impl<'tape, E: Element> Div<Tensor<E>> for Value<'tape, E> {
type Output = Value<'tape, E>;
fn div(self, rhs: Tensor<E>) -> Self::Output {
let literal = self.literal(rhs);
self / literal
}
}
impl<'tape, E: Element> Add<E> for Value<'tape, E> {
type Output = Value<'tape, E>;
fn add(self, rhs: E) -> Self::Output {
self + Tensor::from(rhs)
}
}
impl<'tape, E: Element> Sub<E> for Value<'tape, E> {
type Output = Value<'tape, E>;
fn sub(self, rhs: E) -> Self::Output {
self - Tensor::from(rhs)
}
}
impl<'tape, E: Element> Mul<E> for Value<'tape, E> {
type Output = Value<'tape, E>;
fn mul(self, rhs: E) -> Self::Output {
self * Tensor::from(rhs)
}
}
impl<'tape, E: Element> Div<E> for Value<'tape, E> {
type Output = Value<'tape, E>;
fn div(self, rhs: E) -> Self::Output {
self / Tensor::from(rhs)
}
}
#[cfg(test)]
#[path = "tests/value_tests.rs"]
mod tests;