Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 20 additions & 33 deletions src/display.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
//! Printing of boxed tensors: those whose domain is a hrect. The `Display`
//! implementations live here rather than next to each tensor, since they all
//! render the same way, by walking a shape.
//! Printing of functions on a hrect: tensors, and multiset-valued tensors before
//! a reduction. The `Display` implementations live here rather than next to
//! each tensor, since they all render the same way, by walking a shape.

use std::fmt;
use std::fmt::{Display, Write as _};

use crate::domain::HRect;
use crate::mapping::{BoxedMapping, BoxedPowersetMapping};
use crate::tensor::{
LeftComposition, Physical, Reduction, ReductionTensor, RightComposition, Tensor,
};
use crate::function::{Composition, Function};
use crate::sets::HRect;
use crate::sets::Set;
use crate::tensor::{Physical, Tensor};

/// A tensor whose domain is a hrect. Only these can be printed, since formatting
/// walks a shape and visits every index of it.
pub trait BoxedTensor<A = f32>: Tensor<A> {
fn hrect(&self) -> &HRect;
}

/// Renders a boxed tensor. Every `Display` implementation below defers to this,
/// and each is written for the boxed instantiation of its tensor only.
pub fn format_boxed<A: Display>(tensor: &dyn BoxedTensor<A>) -> String {
format_tensor(tensor.hrect().shape(), &|index| tensor.get(index))
/// Renders a tensor on a hrect.
pub fn format_boxed<A: Display>(tensor: &dyn Tensor<A>) -> String {
format_tensor(tensor.domain().shape(), &|index| tensor.apply(index))
}

impl<A: Clone + Display> Display for Physical<A> {
Expand All @@ -29,23 +21,18 @@ impl<A: Clone + Display> Display for Physical<A> {
}
}

impl<M: BoxedMapping + ?Sized, A: Display> Display for RightComposition<M, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&format_boxed(self))
}
}

impl<U: BoxedTensor<A> + ?Sized, A, B: Display> Display for LeftComposition<U, A, B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&format_boxed(self))
}
}

impl<R: Reduction<A>, M: BoxedPowersetMapping + ?Sized, A: Display> Display
for ReductionTensor<R, M, A>
// A composition prints when the function applied first is one on a hrect and the
// one applied last gives values that print, multisets included.
impl<F, G> Display for Composition<F, G>
where
F: Function<Domain = G::Codomain>,
G: Function<Domain = HRect>,
<F::Codomain as Set>::Owned: Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&format_boxed(self))
f.write_str(&format_tensor(self.domain().shape(), &|index| {
self.apply(index)
}))
}
}

Expand Down
75 changes: 0 additions & 75 deletions src/domain.rs

This file was deleted.

147 changes: 147 additions & 0 deletions src/function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
//! The building bricks: functions, their composition, and their extension to
//! multisets. Tensors, index maps, index functions and reductions are all
//! functions, and every operation on them is built from these.

use std::borrow::Borrow;
use std::rc::Rc;

use crate::sets::{Multisets, Set, Values};

/// A function from its domain to its codomain, which may be sets of indices,
/// of values, or of multisets of either. A tensor goes from indices to values,
/// an index map from indices to indices, an index function from indices to
/// multisets of indices, and a reduction from multisets of values to values.
pub trait Function {
type Domain: Set;
type Codomain: Set;

fn apply(&self, x: &<Self::Domain as Set>::Element) -> <Self::Codomain as Set>::Owned;

fn domain(&self) -> Self::Domain;

fn codomain(&self) -> Self::Codomain;
}

// A shared function is the same function, so that one can be used in several
// compositions, or have its type erased as an `Rc<dyn Tensor<A>>`.
impl<T: Function + ?Sized> Function for Rc<T> {
type Domain = T::Domain;
type Codomain = T::Codomain;

fn apply(&self, x: &<Self::Domain as Set>::Element) -> <Self::Codomain as Set>::Owned {
(**self).apply(x)
}

fn domain(&self) -> Self::Domain {
(**self).domain()
}

fn codomain(&self) -> Self::Codomain {
(**self).codomain()
}
}

/// `f ∘ g`: applies `g`, then `f` to the result.
pub struct Composition<F, G> {
f: F,
g: G,
}

/// Composes `f` after `g`. Composing a tensor after an index map re-indexes it,
/// and a function on values after a tensor applies it element-wise. The
/// codomain of `g` must lie in the domain of `f`.
pub fn compose<F, G>(f: F, g: G) -> Composition<F, G>
where
F: Function<Domain = G::Codomain>,
G: Function,
{
let (codomain, domain) = (g.codomain(), f.domain());
assert!(
codomain.lies_in(&domain),
"the inner function lands in {codomain:?}, which does not lie in the domain {domain:?} of the outer one"
);
Composition { f, g }
}

impl<F, G> Function for Composition<F, G>
where
F: Function<Domain = G::Codomain>,
G: Function,
{
type Domain = G::Domain;
type Codomain = F::Codomain;

fn apply(&self, x: &<Self::Domain as Set>::Element) -> <Self::Codomain as Set>::Owned {
self.f.apply(self.g.apply(x).borrow())
}

fn domain(&self) -> Self::Domain {
self.g.domain()
}

fn codomain(&self) -> Self::Codomain {
self.f.codomain()
}
}

/// A function from multisets to multisets: the extension of `f` to multisets,
/// written `f̄` in the paper. It maps a multiset to the multiset of the images
/// of its elements, repeats included, so it goes from the multisets of the
/// domain of `f` to those of its codomain.
pub struct MultiFunction<F> {
f: F,
}

pub fn multiset_lift<F: Function>(f: F) -> MultiFunction<F> {
MultiFunction { f }
}

impl<F: Function> Function for MultiFunction<F> {
type Domain = Multisets<F::Domain>;
type Codomain = Multisets<F::Codomain>;

fn apply(&self, x: &<Self::Domain as Set>::Element) -> <Self::Codomain as Set>::Owned {
x.iter()
.map(|element| self.f.apply(element.borrow()))
.collect()
}

fn domain(&self) -> Self::Domain {
Multisets::new(self.f.domain())
}

fn codomain(&self) -> Self::Codomain {
Multisets::new(self.f.codomain())
}
}

/// A function on values, given as a closure, from `A` to `B`. Composed after a
/// tensor, it applies element-wise.
pub struct ValueFunction<A, B = A> {
// Held as a closure rather than a plain `fn` pointer so that a function can
// carry values of its own, as `scale` and `clamp` do.
f: Rc<dyn Fn(A) -> B>,
}

impl<A, B> ValueFunction<A, B> {
pub fn new(f: impl Fn(A) -> B + 'static) -> Self {
Self { f: Rc::new(f) }
}
}

impl<A: Clone, B> Function for ValueFunction<A, B> {
type Domain = Values<A>;
type Codomain = Values<B>;

fn apply(&self, x: &A) -> B {
(self.f)(x.clone())
}

fn domain(&self) -> Values<A> {
Values::new()
}

fn codomain(&self) -> Values<B> {
Values::new()
}
}
Loading
Loading