use crate::array::*;
use crate::category::Arrow;
use crate::finite_function::FiniteFunction;
use crate::strict::hypergraph::arrow::is_convex_subgraph_morphism;
use crate::strict::hypergraph::Hypergraph;
use crate::strict::open_hypergraph::OpenHypergraph;
use num_traits::Zero;
pub struct SmcRewriteRule<K: ArrayKind, O, A> {
lhs: OpenHypergraph<K, O, A>,
rhs: OpenHypergraph<K, O, A>,
}
impl<K: ArrayKind, O, A> SmcRewriteRule<K, O, A>
where
K::Type<K::I>: NaturalArray<K>,
K::Type<bool>: Array<K, bool>,
K::Type<O>: Array<K, O> + PartialEq,
K::Type<A>: Array<K, A>,
{
pub fn new(lhs: OpenHypergraph<K, O, A>, rhs: OpenHypergraph<K, O, A>) -> Option<Self> {
if Self::validate(&lhs, &rhs) {
Some(Self { lhs, rhs })
} else {
None
}
}
pub fn validate(lhs: &OpenHypergraph<K, O, A>, rhs: &OpenHypergraph<K, O, A>) -> bool {
Self::boundaries_match(lhs, rhs)
&& Self::boundary_legs_injective(lhs, rhs)
&& Self::lhs_boundary_legs_disjoint(lhs)
&& Self::sides_monogamous_acyclic(lhs, rhs)
}
pub fn boundaries_match(lhs: &OpenHypergraph<K, O, A>, rhs: &OpenHypergraph<K, O, A>) -> bool {
lhs.source() == rhs.source() && lhs.target() == rhs.target()
}
pub fn sides_monogamous_acyclic(
lhs: &OpenHypergraph<K, O, A>,
rhs: &OpenHypergraph<K, O, A>,
) -> bool {
lhs.is_monogamous() && lhs.is_acyclic() && rhs.is_monogamous() && rhs.is_acyclic()
}
fn boundary_legs_injective(
lhs: &OpenHypergraph<K, O, A>,
rhs: &OpenHypergraph<K, O, A>,
) -> bool {
lhs.s.is_injective() && lhs.t.is_injective() && rhs.s.is_injective() && rhs.t.is_injective()
}
fn lhs_boundary_legs_disjoint(lhs: &OpenHypergraph<K, O, A>) -> bool {
lhs.s.has_disjoint_image(&lhs.t)
}
}
impl<K: ArrayKind, O, A> Clone for SmcRewriteRule<K, O, A>
where
K::Type<O>: Clone,
K::Type<A>: Clone,
K::Type<K::I>: Clone,
{
fn clone(&self) -> Self {
Self {
lhs: self.lhs.clone(),
rhs: self.rhs.clone(),
}
}
}
impl<K: ArrayKind, O: core::fmt::Debug, A: core::fmt::Debug> core::fmt::Debug
for SmcRewriteRule<K, O, A>
where
K::Index: core::fmt::Debug,
K::Type<K::I>: core::fmt::Debug,
K::Type<O>: core::fmt::Debug,
K::Type<A>: core::fmt::Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SmcRewriteRule")
.field("lhs", &self.lhs)
.field("rhs", &self.rhs)
.finish()
}
}
pub struct SmcRewriteMatch<'a, K: ArrayKind, O, A> {
rule: &'a SmcRewriteRule<K, O, A>,
host: &'a OpenHypergraph<K, O, A>,
w: FiniteFunction<K>,
x: FiniteFunction<K>,
}
impl<'a, K: ArrayKind, O, A> SmcRewriteMatch<'a, K, O, A> {
pub fn new(
rule: &'a SmcRewriteRule<K, O, A>,
host: &'a OpenHypergraph<K, O, A>,
w: FiniteFunction<K>,
x: FiniteFunction<K>,
) -> Option<Self>
where
K::Type<K::I>: NaturalArray<K>,
K::Type<bool>: Array<K, bool>,
K::Type<O>: Array<K, O> + PartialEq,
K::Type<A>: Array<K, A> + PartialEq,
{
if !host.is_monogamous() || !host.is_acyclic() {
return None;
}
if is_convex_subgraph_morphism(&rule.lhs.h, &host.h, &w, &x) {
Some(Self { rule, host, w, x })
} else {
None
}
}
pub fn w(&self) -> &FiniteFunction<K> {
&self.w
}
pub fn x(&self) -> &FiniteFunction<K> {
&self.x
}
pub fn rule(&self) -> &SmcRewriteRule<K, O, A> {
self.rule
}
pub fn host(&self) -> &OpenHypergraph<K, O, A> {
self.host
}
}
impl<'a, K: ArrayKind, O, A> Clone for SmcRewriteMatch<'a, K, O, A>
where
K::Type<K::I>: Clone,
{
fn clone(&self) -> Self {
Self {
rule: self.rule,
host: self.host,
w: self.w.clone(),
x: self.x.clone(),
}
}
}
impl<'a, K: ArrayKind, O: core::fmt::Debug, A: core::fmt::Debug> core::fmt::Debug
for SmcRewriteMatch<'a, K, O, A>
where
K::Index: core::fmt::Debug,
K::Type<K::I>: core::fmt::Debug,
K::Type<O>: core::fmt::Debug,
K::Type<A>: core::fmt::Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SmcRewriteMatch")
.field("w", &self.w)
.field("x", &self.x)
.finish()
}
}
pub fn apply_smc_rewrite<'a, K: ArrayKind, O, A>(
m: &SmcRewriteMatch<'a, K, O, A>,
) -> Option<OpenHypergraph<K, O, A>>
where
K::Type<K::I>: NaturalArray<K>,
K::Type<bool>: Array<K, bool>,
K::Type<O>: Array<K, O> + PartialEq,
K::Type<A>: Array<K, A> + PartialEq,
O: PartialEq,
for<'b> K::Slice<'b, K::I>: From<&'b [K::I]>,
{
let rule = m.rule();
let host = m.host();
let lhs = rule.lhs();
let rhs = rule.rhs();
let lhs_inputs_in_host = (&lhs.s >> m.w())?;
let lhs_outputs_in_host = (&lhs.t >> m.w())?;
let kept_x_inj = m.x().image_complement_injection()?;
let s_kept = host.h.s.map_indexes(&kept_x_inj)?;
let t_kept = host.h.t.map_indexes(&kept_x_inj)?;
let kept_w_inj = m
.w()
.image_complement_injection()?
.coproduct_many(&[
&host.s,
&host.t,
&lhs_inputs_in_host,
&lhs_outputs_in_host,
&s_kept.values,
&t_kept.values,
])?
.canonical_image_injection()?;
let kept_w_inv = kept_w_inj.inverse_with_fill(K::I::zero())?;
let new_s = host.h.s.map_indexes(&kept_x_inj)?.map_values(&kept_w_inv)?;
let new_t = host.h.t.map_indexes(&kept_x_inj)?.map_values(&kept_w_inv)?;
let new_w = (&kept_w_inj >> &host.h.w)?;
let new_x = (&kept_x_inj >> &host.h.x)?;
let remainder = Hypergraph {
s: new_s,
t: new_t,
w: new_w,
x: new_x,
};
let host_inputs = host.s.factor_through_injective(&kept_w_inj);
let host_outputs = host.t.factor_through_injective(&kept_w_inj);
let lhs_outputs = lhs_outputs_in_host.factor_through_injective(&kept_w_inj);
let lhs_inputs = lhs_inputs_in_host.factor_through_injective(&kept_w_inj);
let ctx_inputs = (&host_inputs + &lhs_outputs)?;
let ctx_outputs = (&host_outputs + &lhs_inputs)?;
let context = OpenHypergraph::new(ctx_inputs, ctx_outputs, remainder).ok()?;
if !context.is_monogamous() {
return None;
}
pushout_rewrite(
&context,
rhs,
&host_inputs,
&host_outputs,
&lhs_inputs,
&lhs_outputs,
)
}
impl<K: ArrayKind, O, A> SmcRewriteRule<K, O, A> {
pub fn lhs(&self) -> &OpenHypergraph<K, O, A> {
&self.lhs
}
pub fn rhs(&self) -> &OpenHypergraph<K, O, A> {
&self.rhs
}
}
fn pushout_rewrite<K: ArrayKind, O, A>(
context: &OpenHypergraph<K, O, A>,
rhs: &OpenHypergraph<K, O, A>,
host_inputs: &FiniteFunction<K>,
host_outputs: &FiniteFunction<K>,
lhs_inputs: &FiniteFunction<K>,
lhs_outputs: &FiniteFunction<K>,
) -> Option<OpenHypergraph<K, O, A>>
where
K::Type<K::I>: NaturalArray<K>,
K::Type<O>: Array<K, O> + PartialEq,
K::Type<A>: Array<K, A> + PartialEq,
{
let f = (lhs_inputs + lhs_outputs)?;
let g = (&rhs.s + &rhs.t)?;
let (h, left_arrow, _right_arrow) = Hypergraph::pushout_along_span(&context.h, &rhs.h, &f, &g)?;
let s = host_inputs.compose(&left_arrow.w)?;
let t = host_outputs.compose(&left_arrow.w)?;
OpenHypergraph::new(s, t, h).ok()
}