pub mod petri_law {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Marking<const P: usize>(pub [u32; P]);
impl<const P: usize> Marking<P> {
pub const EMPTY: Self = Self([0u32; P]);
#[inline]
pub fn total_tokens(&self) -> u32 {
let mut s = 0u32;
let mut i = 0;
while i < P {
s += self.0[i];
i += 1;
}
s
}
#[must_use]
#[inline]
pub fn at(&self, p: usize) -> Option<u32> {
self.0.get(p).copied()
}
}
impl<const P: usize> Default for Marking<P> {
fn default() -> Self {
Self::EMPTY
}
}
pub struct PreMatrix<const P: usize, const T: usize>
where
[(); P * T]: Sized,
{
pub weights: [u8; P * T],
}
impl<const P: usize, const T: usize> PreMatrix<P, T>
where
[(); P * T]: Sized,
{
pub const ZERO: Self = Self {
weights: [0u8; P * T],
};
#[inline]
pub fn w(&self, p: usize, t: usize) -> u8 {
self.weights[p * T + t]
}
#[inline]
pub fn is_enabled(&self, t: usize, m: &Marking<P>) -> bool {
(0..P).all(|p| m.0[p] >= self.weights[p * T + t] as u32)
}
}
impl<const P: usize, const T: usize> Default for PreMatrix<P, T>
where
[(); P * T]: Sized,
{
fn default() -> Self {
Self::ZERO
}
}
pub struct PostMatrix<const P: usize, const T: usize>
where
[(); T * P]: Sized,
{
pub weights: [u8; T * P],
}
impl<const P: usize, const T: usize> PostMatrix<P, T>
where
[(); T * P]: Sized,
{
pub const ZERO: Self = Self {
weights: [0u8; T * P],
};
#[inline]
pub fn w(&self, t: usize, p: usize) -> u8 {
self.weights[t * P + p]
}
#[inline]
pub fn fire(&self, t: usize, m: Marking<P>, pre: &PreMatrix<P, T>) -> Marking<P>
where
[(); P * T]: Sized,
{
let mut next = m;
let mut p = 0;
while p < P {
next.0[p] =
next.0[p] - pre.weights[p * T + t] as u32 + self.weights[t * P + p] as u32;
p += 1;
}
next
}
}
impl<const P: usize, const T: usize> Default for PostMatrix<P, T>
where
[(); T * P]: Sized,
{
fn default() -> Self {
Self::ZERO
}
}
}
pub mod powl_law {
use core::marker::ConstParamTy;
#[derive(Debug, Clone, Copy, PartialEq, Eq, ConstParamTy)]
pub enum PowlKind {
Atom,
Xor,
Loop,
Partial,
Silent,
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TypedNode<const KIND: PowlKind>(pub u32);
impl TypedNode<{ PowlKind::Atom }> {
#[inline]
pub const fn atom(id: u32) -> Self {
Self(id)
}
#[inline]
pub const fn is_observable(&self) -> bool {
true
}
}
impl TypedNode<{ PowlKind::Silent }> {
#[inline]
pub const fn silent(id: u32) -> Self {
Self(id)
}
#[inline]
pub const fn is_observable(&self) -> bool {
false
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct OrderEdge {
pub before: u32,
pub after: u32,
}
impl TypedNode<{ PowlKind::Partial }> {
#[inline]
pub const fn partial(id: u32) -> Self {
Self(id)
}
#[inline]
pub fn are_concurrent(&self, edges: &[OrderEdge], a: u32, b: u32) -> bool {
let ab = edges.iter().any(|e| e.before == a && e.after == b);
let ba = edges.iter().any(|e| e.before == b && e.after == a);
!ab && !ba
}
}
impl TypedNode<{ PowlKind::Xor }> {
#[inline]
pub const fn xor(id: u32) -> Self {
Self(id)
}
#[inline]
pub const fn min_branches() -> usize {
2
}
}
impl TypedNode<{ PowlKind::Loop }> {
#[inline]
pub const fn loop_node(id: u32) -> Self {
Self(id)
}
}
macro_rules! impl_id {
($kind:expr) => {
impl TypedNode<{ $kind }> {
#[inline]
pub const fn id(&self) -> u32 {
self.0
}
}
};
}
impl_id!(PowlKind::Atom);
impl_id!(PowlKind::Silent);
impl_id!(PowlKind::Partial);
impl_id!(PowlKind::Xor);
impl_id!(PowlKind::Loop);
}
pub mod evidence_law {
pub trait EvidenceKind {
fn kind_label(&self) -> &'static str;
}
impl<T> EvidenceKind for T {
default fn kind_label(&self) -> &'static str {
"raw"
}
}
#[repr(transparent)]
pub struct Admitted<T>(pub T);
impl<T> EvidenceKind for Admitted<T> {
fn kind_label(&self) -> &'static str {
"admitted"
}
}
}
pub mod token_law {
use core::simd::{cmp::SimdPartialOrd, u32x4, u32x8};
#[inline]
pub fn transition_enabled_4(marking: [u32; 4], pre_weights: [u32; 4]) -> bool {
u32x4::from_array(marking)
.simd_ge(u32x4::from_array(pre_weights))
.all()
}
#[inline]
pub fn transition_enabled_8(marking: [u32; 8], pre_weights: [u32; 8]) -> bool {
u32x8::from_array(marking)
.simd_ge(u32x8::from_array(pre_weights))
.all()
}
#[inline]
pub fn fire_4(marking: [u32; 4], pre: [u32; 4], post: [u32; 4]) -> [u32; 4] {
(u32x4::from_array(marking) - u32x4::from_array(pre) + u32x4::from_array(post)).to_array()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn marking_empty_is_zero() {
assert_eq!(petri_law::Marking::<4>::EMPTY.total_tokens(), 0);
}
#[test]
fn pre_matrix_enables_and_blocks() {
let mut pre = petri_law::PreMatrix::<2, 2>::ZERO;
pre.weights[0] = 1; pre.weights[3] = 1;
let m = petri_law::Marking([1u32, 0u32]);
assert!(pre.is_enabled(0, &m)); assert!(!pre.is_enabled(1, &m)); }
#[test]
fn firing_token_moves_correctly() {
let mut pre = petri_law::PreMatrix::<2, 1>::ZERO;
pre.weights[0] = 1; let mut post = petri_law::PostMatrix::<2, 1>::ZERO;
post.weights[1] = 1;
let m = petri_law::Marking([1u32, 0u32]);
assert!(pre.is_enabled(0, &m));
let m2 = post.fire(0, m, &pre);
assert_eq!(m2, petri_law::Marking([0u32, 1u32]));
}
#[test]
fn atom_observable_silent_not() {
assert!(powl_law::TypedNode::atom(1).is_observable());
assert!(!powl_law::TypedNode::silent(2).is_observable());
}
#[test]
fn partial_concurrency_correct() {
let p = powl_law::TypedNode::partial(0);
let edges = [powl_law::OrderEdge {
before: 1,
after: 2,
}];
assert!(!p.are_concurrent(&edges, 1, 2)); assert!(p.are_concurrent(&edges, 1, 3)); }
#[test]
fn xor_min_branches_is_two() {
assert_eq!(
powl_law::TypedNode::<{ powl_law::PowlKind::Xor }>::min_branches(),
2
);
}
#[test]
fn raw_u32_labels_raw() {
use evidence_law::EvidenceKind;
assert_eq!(42u32.kind_label(), "raw");
}
#[test]
fn admitted_wrapper_labels_admitted() {
use evidence_law::{Admitted, EvidenceKind};
assert_eq!(Admitted(42u32).kind_label(), "admitted");
}
#[test]
fn simd_enabled_all_met() {
assert!(token_law::transition_enabled_4([5, 3, 1, 0], [1, 1, 1, 0]));
}
#[test]
fn simd_enabled_one_unmet() {
assert!(!token_law::transition_enabled_4([5, 0, 1, 0], [1, 1, 1, 0]));
}
#[test]
fn simd_fire_moves_tokens() {
let m = token_law::fire_4([2, 0, 0, 0], [1, 0, 0, 0], [0, 1, 0, 0]);
assert_eq!(m, [1, 1, 0, 0]);
}
#[test]
fn simd_enabled_8_all_met() {
assert!(token_law::transition_enabled_8(
[9, 8, 7, 6, 5, 4, 3, 2],
[1, 1, 1, 1, 1, 1, 1, 1],
));
}
}