use sim_kernel::{Expr, Symbol};
pub const TOPOLOGY_API: &str = "sim.topology.v3";
pub const DEFAULT_GRAPH_VERSION: &str = "0.1.0";
pub const DEFAULT_MAX_STEPS: u32 = 256;
pub const DEFAULT_MAX_NODE_VISITS: u32 = 64;
pub const DEFAULT_MAX_EDGE_VISITS: u32 = 64;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NodeId(pub Symbol);
impl NodeId {
pub fn new(name: impl Into<String>) -> Self {
Self(Symbol::new(name.into()))
}
pub fn as_symbol(&self) -> &Symbol {
&self.0
}
}
impl From<Symbol> for NodeId {
fn from(value: Symbol) -> Self {
Self(value)
}
}
impl From<&str> for NodeId {
fn from(value: &str) -> Self {
Self::new(value)
}
}
impl From<String> for NodeId {
fn from(value: String) -> Self {
Self::new(value)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EdgeId(pub u32);
impl EdgeId {
pub const fn new(index: u32) -> Self {
Self(index)
}
}
impl From<u32> for EdgeId {
fn from(value: u32) -> Self {
Self(value)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PortRef {
pub node: NodeId,
pub port: Symbol,
}
impl PortRef {
pub fn new(node: impl Into<NodeId>, port: Symbol) -> Self {
Self {
node: node.into(),
port,
}
}
pub fn named(node: impl Into<NodeId>, port: impl Into<String>) -> Self {
Self::new(node, Symbol::new(port.into()))
}
pub fn input(node: impl Into<NodeId>) -> Self {
Self::named(node, "in")
}
pub fn output(node: impl Into<NodeId>) -> Self {
Self::named(node, "out")
}
}
#[derive(Clone, Debug)]
pub struct Graph {
pub name: Symbol,
pub version: String,
pub api: String,
pub input: Option<Expr>,
pub output: Option<Expr>,
pub nodes: Vec<Node>,
pub edges: Vec<Edge>,
pub cells: Vec<Cell>,
pub scheduler: Scheduler,
pub budget: Budget,
pub capabilities: Vec<Symbol>,
pub metadata: Vec<(Symbol, Expr)>,
pub tests: Vec<GraphTest>,
}
impl Graph {
pub fn new(name: Symbol) -> Self {
Self {
name,
version: DEFAULT_GRAPH_VERSION.to_owned(),
api: TOPOLOGY_API.to_owned(),
input: None,
output: None,
nodes: Vec::new(),
edges: Vec::new(),
cells: Vec::new(),
scheduler: Scheduler::default(),
budget: Budget::default(),
capabilities: Vec::new(),
metadata: Vec::new(),
tests: Vec::new(),
}
}
pub fn minimal(name: impl Into<String>) -> Self {
Self::new(Symbol::new(name.into()))
}
pub fn placeholder() -> Self {
Self::minimal("topology")
}
}
impl Default for Graph {
fn default() -> Self {
Self::minimal("topology")
}
}
#[derive(Clone, Debug)]
pub struct Node {
pub id: NodeId,
pub verb: Symbol,
pub inputs: Vec<Port>,
pub outputs: Vec<Port>,
pub target: Option<Expr>,
pub role: Option<Symbol>,
pub input: Option<Expr>,
pub output: Option<Expr>,
pub options: Vec<(Symbol, Expr)>,
}
impl Node {
pub fn new(id: impl Into<NodeId>, verb: Symbol) -> Self {
let (inputs, outputs) = default_ports_for_verb(&verb);
Self {
id: id.into(),
verb,
inputs,
outputs,
target: None,
role: None,
input: None,
output: None,
options: Vec::new(),
}
}
pub fn named(id: impl Into<NodeId>, verb: impl Into<String>) -> Self {
Self::new(id, Symbol::new(verb.into()))
}
pub fn with_ports(
id: impl Into<NodeId>,
verb: Symbol,
inputs: Vec<Port>,
outputs: Vec<Port>,
) -> Self {
Self {
id: id.into(),
verb,
inputs,
outputs,
target: None,
role: None,
input: None,
output: None,
options: Vec::new(),
}
}
}
#[derive(Clone, Debug)]
pub struct Port {
pub name: Symbol,
pub shape: Option<Expr>,
pub mode: PortMode,
pub required: bool,
}
impl Port {
pub fn new(name: Symbol, mode: PortMode, required: bool) -> Self {
Self {
name,
shape: None,
mode,
required,
}
}
pub fn value(name: impl Into<String>, required: bool) -> Self {
Self::new(Symbol::new(name.into()), PortMode::Value, required)
}
pub fn stream(name: impl Into<String>, required: bool) -> Self {
Self::new(Symbol::new(name.into()), PortMode::Stream, required)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum PortMode {
#[default]
Value,
Stream,
}
#[derive(Clone, Debug)]
pub struct Edge {
pub id: EdgeId,
pub from: PortRef,
pub to: PortRef,
pub when: Option<Expr>,
pub transform: Option<Expr>,
pub as_name: Option<Symbol>,
pub priority: i64,
pub max_visits: Option<u32>,
pub buffer: Option<Expr>,
pub metadata: Vec<(Symbol, Expr)>,
}
impl Edge {
pub fn new(id: impl Into<EdgeId>, from: PortRef, to: PortRef) -> Self {
Self {
id: id.into(),
from,
to,
when: None,
transform: None,
as_name: None,
priority: 0,
max_visits: None,
buffer: None,
metadata: Vec::new(),
}
}
}
#[derive(Clone, Debug)]
pub struct Cell {
pub name: Symbol,
pub shape: Option<Expr>,
pub initial: Expr,
pub merge: Option<Symbol>,
pub private: bool,
}
impl Cell {
pub fn new(name: Symbol, initial: Expr) -> Self {
Self {
name,
shape: None,
initial,
merge: None,
private: false,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Scheduler {
pub mode: SchedulerMode,
pub seed: Option<u64>,
pub max_concurrency: u32,
pub deterministic: bool,
}
impl Default for Scheduler {
fn default() -> Self {
Self {
mode: SchedulerMode::Sequential,
seed: None,
max_concurrency: 1,
deterministic: true,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum SchedulerMode {
#[default]
Sequential,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Budget {
pub max_steps: u32,
pub max_node_visits: u32,
pub max_edge_visits: u32,
pub max_outputs: u32,
pub max_child_runs: u32,
pub deadline_ms: Option<u64>,
pub on_exhausted: BudgetExhausted,
}
impl Default for Budget {
fn default() -> Self {
Self {
max_steps: DEFAULT_MAX_STEPS,
max_node_visits: DEFAULT_MAX_NODE_VISITS,
max_edge_visits: DEFAULT_MAX_EDGE_VISITS,
max_outputs: 64,
max_child_runs: 16,
deadline_ms: None,
on_exhausted: BudgetExhausted::Fail,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum BudgetExhausted {
#[default]
Fail,
Partial,
}
#[derive(Clone, Debug)]
pub struct GraphTest {
pub name: Symbol,
pub input: Expr,
pub expect: Expr,
pub fixtures: Vec<(Symbol, Expr)>,
}
impl GraphTest {
pub fn new(name: Symbol, input: Expr, expect: Expr) -> Self {
Self {
name,
input,
expect,
fixtures: Vec::new(),
}
}
}
fn default_ports_for_verb(verb: &Symbol) -> (Vec<Port>, Vec<Port>) {
match verb.name.as_ref() {
"in" => (Vec::new(), vec![Port::value("out", true)]),
"out" => (vec![Port::value("in", true)], Vec::new()),
"call" => (
vec![Port::value("in", true)],
vec![Port::value("out", true), Port::value("error", false)],
),
"branch" => (
vec![Port::value("in", true)],
vec![
Port::value("true", false),
Port::value("false", false),
Port::value("else", false),
],
),
"merge" => (
vec![Port::value("in", true)],
vec![Port::value("out", true)],
),
"tee" => (
vec![Port::value("in", true)],
vec![Port::value("out", true)],
),
_ => (
vec![Port::value("in", true)],
vec![Port::value("out", true)],
),
}
}