use crate::beam::{Beam, Optic};
use crate::Prism;
use std::convert::Infallible;
use terni::{Imperfect, Metric};
pub trait GroupStructure {
fn identity() -> Self;
fn inverse(&self) -> Self;
fn compose(&self, other: &Self) -> Self;
}
pub trait LawvereFixedPoint {
fn is_idempotent_under<F>(&self, endomap: F) -> bool
where
F: Fn(&Self) -> Self,
Self: PartialEq + Sized,
{
let once = endomap(self);
let twice = endomap(&once);
once == twice
}
fn in_kernel(&self) -> bool;
}
pub trait Fiber {
type State;
}
pub trait Connection: Fiber
where
Self::Optic: Prism,
<<Self::Optic as Prism>::Input as crate::Beam>::In: Sized,
{
type Optic;
fn connection(&self) -> &Self::Optic;
}
pub trait Gauge: Connection
where
Self::Optic: Prism,
<<Self::Optic as Prism>::Input as crate::Beam>::In: Sized,
{
type Group: GroupStructure;
fn gauge(&self) -> &Self::Group;
fn act_on(&self, state: &Self::State) -> Self::State;
}
pub trait Transport: Gauge
where
Self::Optic: Prism,
<<Self::Optic as Prism>::Input as crate::Beam>::In: Sized,
{
type Holonomy: Metric;
fn transport(&self, state: &Self::State) -> Imperfect<Self::State, Infallible, Self::Holonomy>;
}
pub trait Closure: Transport
where
Self::Optic: Prism,
<<Self::Optic as Prism>::Input as crate::Beam>::In: Sized,
{
type Fixed: LawvereFixedPoint;
fn close(&self) -> &Self::Fixed;
}
pub trait Bundle: Closure
where
Self::Optic: Prism,
<<Self::Optic as Prism>::Input as crate::Beam>::In: Sized,
{
}
impl<T> Bundle for T
where
T: Closure,
T::Optic: Prism,
<<T::Optic as Prism>::Input as crate::Beam>::In: Sized,
{
}
pub struct IdentityPrism<S: Clone> {
_marker: std::marker::PhantomData<S>,
}
impl<S: Clone> IdentityPrism<S> {
pub fn new() -> Self {
Self {
_marker: std::marker::PhantomData,
}
}
}
impl<S: Clone> Default for IdentityPrism<S> {
fn default() -> Self {
Self::new()
}
}
impl<S: Clone + 'static> Prism for IdentityPrism<S> {
type Input = Optic<(), S>;
type Focused = Optic<S, S>;
type Projected = Optic<S, S>;
type Refracted = Optic<S, S>;
fn focus(&self, beam: Self::Input) -> Self::Focused {
let v = beam
.value()
.cloned()
.expect("IdentityPrism::focus on dark beam");
beam.next(v)
}
fn project(&self, beam: Self::Focused) -> Self::Projected {
let v = beam
.value()
.cloned()
.expect("IdentityPrism::project on dark beam");
beam.next(v)
}
fn settle(&self, beam: Self::Projected) -> Self::Refracted {
let v = beam
.value()
.cloned()
.expect("IdentityPrism::settle on dark beam");
beam.next(v)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Cyclic<const N: u8>(pub u8);
impl<const N: u8> Cyclic<N> {
pub fn new(k: u8) -> Self {
Cyclic(k % N)
}
pub fn value(&self) -> u8 {
self.0
}
}
impl<const N: u8> GroupStructure for Cyclic<N> {
fn identity() -> Self {
Cyclic(0)
}
fn inverse(&self) -> Self {
Cyclic((N - self.0) % N)
}
fn compose(&self, other: &Self) -> Self {
Cyclic(((self.0 as u16 + other.0 as u16) % N as u16) as u8)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct StableFiber<S> {
pub state: S,
pub kernel: bool,
}
impl<S: Clone + PartialEq> LawvereFixedPoint for StableFiber<S> {
fn in_kernel(&self) -> bool {
self.kernel
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ScalarLoss;
struct TestFiber;
impl Fiber for TestFiber {
type State = [f64; 4];
}
#[test]
fn fiber_has_state() {
let _f = TestFiber;
let _: <TestFiber as Fiber>::State = [1.0, 2.0, 3.0, 4.0];
}
struct TestConnection {
optic: IdentityPrism<[f64; 4]>,
}
impl Fiber for TestConnection {
type State = [f64; 4];
}
impl Connection for TestConnection {
type Optic = IdentityPrism<[f64; 4]>;
fn connection(&self) -> &IdentityPrism<[f64; 4]> {
&self.optic
}
}
#[test]
fn connection_requires_fiber() {
let c = TestConnection {
optic: IdentityPrism::new(),
};
let _ = c.connection();
}
#[test]
fn identity_prism_passes_through() {
let p: IdentityPrism<[f64; 4]> = IdentityPrism::new();
let beam: Optic<(), [f64; 4]> = Optic::ok((), [1.0, 2.0, 3.0, 4.0]);
let focused = p.focus(beam);
let projected = p.project(focused);
let refracted = p.settle(projected);
assert_eq!(refracted.value(), Some(&[1.0, 2.0, 3.0, 4.0]));
}
type C5 = Cyclic<5>;
#[test]
fn cyclic_identity_law_left_and_right() {
let id = C5::identity();
for k in 0..5u8 {
let x = Cyclic::<5>::new(k);
assert_eq!(id.compose(&x), x);
assert_eq!(x.compose(&id), x);
}
}
#[test]
fn cyclic_inverse_law() {
let id = C5::identity();
for k in 0..5u8 {
let x = Cyclic::<5>::new(k);
assert_eq!(x.compose(&x.inverse()), id);
assert_eq!(x.inverse().compose(&x), id);
}
}
#[test]
fn cyclic_associativity() {
for a in 0..5u8 {
for b in 0..5u8 {
for c in 0..5u8 {
let x = Cyclic::<5>::new(a);
let y = Cyclic::<5>::new(b);
let z = Cyclic::<5>::new(c);
let lhs = x.compose(&y).compose(&z);
let rhs = x.compose(&y.compose(&z));
assert_eq!(lhs, rhs);
}
}
}
}
struct TestBundle {
optic: IdentityPrism<[f64; 4]>,
strategy: Cyclic<4>,
fixed: StableFiber<[f64; 4]>,
}
impl Fiber for TestBundle {
type State = [f64; 4];
}
impl Connection for TestBundle {
type Optic = IdentityPrism<[f64; 4]>;
fn connection(&self) -> &IdentityPrism<[f64; 4]> {
&self.optic
}
}
impl Gauge for TestBundle {
type Group = Cyclic<4>;
fn gauge(&self) -> &Cyclic<4> {
&self.strategy
}
fn act_on(&self, state: &[f64; 4]) -> [f64; 4] {
let k = self.strategy.0 as usize % 4;
let mut out = [0.0; 4];
for i in 0..4 {
out[i] = state[(i + k) % 4];
}
out
}
}
impl Transport for TestBundle {
type Holonomy = ScalarLoss;
fn transport(&self, state: &[f64; 4]) -> Imperfect<[f64; 4], Infallible, ScalarLoss> {
let compressed = [state[0], state[1], 0.0, 0.0];
let loss = state[2].abs() + state[3].abs();
if loss == 0.0 {
Imperfect::success(compressed)
} else {
Imperfect::partial(compressed, ScalarLoss::new(loss))
}
}
}
impl Closure for TestBundle {
type Fixed = StableFiber<[f64; 4]>;
fn close(&self) -> &StableFiber<[f64; 4]> {
&self.fixed
}
}
fn make_bundle(strategy_shift: u8, kernel: bool) -> TestBundle {
TestBundle {
optic: IdentityPrism::new(),
strategy: Cyclic::<4>::new(strategy_shift),
fixed: StableFiber {
state: [1.0, 2.0, 0.0, 0.0],
kernel,
},
}
}
#[test]
fn gauge_requires_connection() {
let b = make_bundle(3, true);
assert_eq!(b.gauge().value(), 3);
}
#[test]
fn gauge_act_on_consistency() {
let s = [10.0_f64, 20.0, 30.0, 40.0];
for g_k in 0..4u8 {
for h_k in 0..4u8 {
let bg = make_bundle(g_k, true);
let bh = make_bundle(h_k, true);
let composed_k = bg.gauge().compose(bh.gauge()).value();
let bc = make_bundle(composed_k, true);
let after_h = bh.act_on(&s);
let then_g = bg.act_on(&after_h);
let direct = bc.act_on(&s);
assert_eq!(
then_g, direct,
"action-consistency failed for g={}, h={}",
g_k, h_k
);
}
}
}
#[test]
fn transport_returns_partial() {
let b = make_bundle(3, true);
let state = [1.0, 2.0, 3.0, 4.0];
let result = b.transport(&state);
assert!(result.is_partial());
}
#[test]
fn transport_holonomy_measures_loss() {
let b = make_bundle(3, true);
let state = [1.0, 2.0, 3.0, 4.0];
match b.transport(&state) {
Imperfect::Partial(compressed, loss) => {
assert_eq!(compressed, [1.0, 2.0, 0.0, 0.0]);
assert_eq!(loss.as_f64(), 7.0);
}
_ => panic!("expected Partial"),
}
}
#[test]
fn transport_zero_loss_returns_success() {
let b = make_bundle(3, true);
let state = [1.0, 2.0, 0.0, 0.0];
let result = b.transport(&state);
assert!(result.is_ok());
}
#[test]
fn transport_holonomy_is_a_metric() {
fn requires_metric<T: Transport>(_b: &T)
where
T::Holonomy: Metric,
{
}
let b = make_bundle(3, true);
requires_metric(&b);
}
#[test]
fn closure_is_in_kernel_when_certified() {
let b = make_bundle(0, true);
assert!(b.close().in_kernel());
}
#[test]
fn closure_not_in_kernel_when_uncertified() {
let b = make_bundle(0, false);
assert!(!b.close().in_kernel());
}
#[test]
fn closure_idempotence_under_identity() {
let s = StableFiber {
state: [1.0, 2.0, 0.0, 0.0_f64],
kernel: true,
};
assert!(s.is_idempotent_under(|x| x.clone()));
}
#[test]
fn closure_idempotence_under_transport_projection() {
let s = StableFiber {
state: [1.0, 2.0, 0.0, 0.0_f64],
kernel: true,
};
let project = |x: &StableFiber<[f64; 4]>| StableFiber {
state: [x.state[0], x.state[1], 0.0, 0.0],
kernel: x.kernel,
};
assert!(s.is_idempotent_under(project));
}
#[test]
fn full_tower_is_bundle() {
fn accepts_bundle<B: Bundle>(_b: &B)
where
B::Optic: Prism,
<<B::Optic as Prism>::Input as crate::Beam>::In: Sized,
{
}
let b = make_bundle(3, true);
accepts_bundle(&b);
}
#[test]
fn bundle_associated_types_accessible() {
let b = make_bundle(3, true);
let _: &<TestBundle as Fiber>::State = &[0.0; 4];
let _conn: &<TestBundle as Connection>::Optic = b.connection();
let _gauge: &<TestBundle as Gauge>::Group = b.gauge();
let _fixed: &<TestBundle as Closure>::Fixed = b.close();
assert!(b.close().in_kernel());
}
}