use crate::ast::{PortType, Value};
use crate::kernel::{Construction, Dataflow, Metadata, PolydatKernel};
impl Metadata for PolydatKernel {
#[inline]
fn find_input(&self, name: &str) -> Option<usize> {
self.program().find_input(name)
}
#[inline]
fn input_names(&self) -> Vec<String> {
self.program().input_names()
}
#[inline]
fn output_names(&self) -> Vec<String> {
self.program()
.output_names()
.iter()
.map(|s| s.to_string())
.collect()
}
#[inline]
fn coord_count(&self) -> usize {
self.program().coord_count()
}
#[inline]
fn input_port_type(&self, name: &str) -> Option<PortType> {
self.program().input_port_type(name)
}
#[inline]
fn input_port_type_by_idx(&self, idx: usize) -> Option<PortType> {
self.program().input_port_type_by_idx(idx)
}
#[inline]
fn output_port_type(&self, name: &str) -> Option<PortType> {
self.program().output_port_type(name)
}
}
impl Dataflow for PolydatKernel {
#[inline]
fn get_wire_idx(&self, idx: usize) -> Value {
self.state_ref().get_input(idx)
}
}
impl Construction for PolydatKernel {
type Error = crate::kernel::subcontext::ContractViolation;
fn root(matter: crate::kernel::subcontext::PolydatMatter<'_>) -> Result<Self, Self::Error> {
use crate::kernel::subcontext::PolydatMatterInner;
match matter.inner {
PolydatMatterInner::Source(s) => {
let options = crate::dsl::compile::CompileOptions {
source_dir: s.options.workload_dir.clone(),
lib_paths: s.options.polydat_lib_paths,
required_outputs: s.options.required_outputs.clone(),
strict: s.options.strict,
context: s
.options
.context_label
.clone()
.unwrap_or_else(|| s.label.clone()),
cursor_limit: s.options.cursor_limit,
input_variance: s.options.input_variance,
inferred_externs: Vec::new(),
ledger: None,
resources: None,
engine: crate::Engine::default(),
};
crate::dsl::compile::compile_polydat_interpreter_with_options(
&s.body, &options, None,
)
.map_err(|e| crate::kernel::subcontext::ContractViolation::Compile(e.to_string()))
}
PolydatMatterInner::Statements(s) => {
let file = crate::dsl::ast::PolydatFile {
statements: s.statements,
};
let options = crate::dsl::compile::CompileOptions {
source_dir: s.options.workload_dir.clone(),
lib_paths: s.options.polydat_lib_paths,
required_outputs: s.options.required_outputs.clone(),
strict: s.options.strict,
context: s
.options
.context_label
.clone()
.unwrap_or_else(|| s.label.clone()),
cursor_limit: None,
input_variance: s.options.input_variance,
inferred_externs: Vec::new(),
ledger: None,
resources: None,
engine: crate::Engine::default(),
};
crate::dsl::compile::compile_ast_interpreter_with_options(&file, "", &options, None)
.map_err(|e| {
crate::kernel::subcontext::ContractViolation::Compile(e.to_string())
})
}
PolydatMatterInner::Program(p) => {
let engine = p.program.engine();
let program = p.program.as_interpreter().ok_or_else(|| {
crate::kernel::subcontext::ContractViolation::Compile(format!(
"a root interpreter kernel needs an interpreter program; this one is \
on the {engine} engine, whose `create_kernel` makes its root"
))
})?;
let mut k = PolydatKernel::from_program(program);
for (var, value) in p.iter_bindings {
if let Some(idx) = k.program().find_input(var) {
k.state().set_input(idx, value.clone());
}
}
Ok(k)
}
}
}
fn subscope(
&self,
matter: crate::kernel::subcontext::PolydatMatter<'_>,
) -> Result<Box<dyn crate::kernel::Kernel>, Self::Error> {
matter.build_under(self)
}
}
impl crate::kernel::Kernel for PolydatKernel {
fn engine(&self) -> crate::compile::select::Engine {
crate::compile::select::Engine::Interpreter(self.program().cone_mode())
}
fn set_inputs(&mut self, coords: &[u64]) {
PolydatKernel::set_inputs(self, coords);
}
fn set_input(&mut self, name: &str, value: Value) -> Result<(), crate::kernel::WriteError> {
PolydatKernel::set_input(self, name, value)
}
fn set_cursor(
&mut self,
name: &str,
partition: &crate::iteration::cursor_partition::Partition,
) -> Result<(), crate::kernel::WriteError> {
PolydatKernel::set_cursor(self, name, partition)
}
fn eval(&mut self) {
PolydatKernel::eval_read(self);
}
fn pull(&mut self, name: &str) -> Value {
PolydatKernel::pull_ref(self, name).clone()
}
fn input_names(&self) -> Vec<String> {
Metadata::input_names(self)
}
fn output_names(&self) -> Vec<String> {
Metadata::output_names(self)
}
fn output_type(&self, name: &str) -> Option<PortType> {
Metadata::output_port_type(self, name)
}
fn externs(&self) -> Vec<(String, PortType)> {
let program = self.program();
Metadata::input_names(self)
.into_iter()
.enumerate()
.filter(|(i, _)| program.input_kind(*i) != Some(crate::kernel::InputKind::Coordinate))
.filter_map(|(i, name)| Metadata::input_port_type_by_idx(self, i).map(|t| (name, t)))
.collect()
}
fn cursor_schemas(&self) -> &[crate::iteration::source::SourceSchema] {
self.program().cursor_schemas()
}
fn input_value(&self, name: &str) -> Option<Value> {
let idx = self.program().find_input(name)?;
Some(self.state_ref().get_input(idx))
}
fn input_index(&self, name: &str) -> Option<usize> {
self.program().find_input(name)
}
fn set_input_at(
&mut self,
index: usize,
value: Value,
) -> Result<(), crate::kernel::WriteError> {
PolydatKernel::set_input_at(self, index, value)
}
fn output_index(&self, name: &str) -> Option<usize> {
self.program().output_index(name)
}
fn const_inits(&self) -> &[crate::kernel::ConstInit] {
self.program().const_inits()
}
fn init_input_at(
&mut self,
index: usize,
value: Value,
) -> Result<(), crate::kernel::WriteError> {
PolydatKernel::init_input_at(self, index, value)
}
fn pull_at(&mut self, index: usize) -> Value {
PolydatKernel::pull_ref_at(self, index).clone()
}
fn traversals(&self) -> &[crate::dsl::traversal::Traversal] {
self.program().traversals()
}
fn plan(&self) -> crate::EnginePlan {
self.program().engine_plan()
}
fn traverse(&mut self, index: usize) -> Result<crate::kernel::TraversalStream, String> {
PolydatKernel::traverse(self, index)
}
fn invalidate_all(&mut self) {
self.state().invalidate_all();
}
fn shared_cells(&self) -> Vec<crate::kernel::SharedCellEntry> {
self.shared_cells_in_scope()
}
fn output_cell(&self, name: &str) -> Option<crate::kernel::SharedCell> {
self.state_ref().core.output_cell(self.program(), name)
}
fn output_modifier(&self, name: &str) -> crate::dsl::ast::BindingModifier {
self.program().output_modifier(name)
}
fn cells_in_scope(&self) -> Vec<crate::kernel::SharedCellEntry> {
self.shared_cells_in_scope()
}
fn set_transit_cells(&mut self, cells: Vec<crate::kernel::SharedCellEntry>) {
self.replace_transit_cells(cells);
}
fn scope_coordinates(&self) -> &[crate::kernel::ScopeCoord] {
PolydatKernel::scope_coordinates(self)
}
fn extend_scope_coordinates(&mut self, outer: &[crate::kernel::ScopeCoord]) {
PolydatKernel::extend_scope_coordinates(self, outer);
}
fn input_port_type(&self, name: &str) -> Option<crate::ast::PortType> {
self.program().input_port_type(name)
}
fn bind_input_cell(&mut self, name: &str, cell: crate::kernel::SharedCell) -> bool {
let Some(idx) = self.program().find_input(name) else {
return false;
};
self.state().attach_shared_cell(idx, cell);
true
}
fn attach_shared_cell(
&mut self,
name: &str,
cell: crate::kernel::SharedCell,
) -> Result<(), String> {
let program = self.program().clone();
let shared = program.shared_outputs();
let idx = program.find_input(name).filter(|_| shared.contains(&name));
let Some(idx) = idx else {
return Err(format!(
"no `shared` binding named '{name}'; this kernel's shared bindings are {shared:?}"
));
};
self.state().attach_shared_cell(idx, cell);
Ok(())
}
fn into_program(self: Box<Self>) -> std::sync::Arc<dyn crate::kernel::KernelProgram> {
PolydatKernel::into_program(*self)
}
fn ledger(&self) -> &std::sync::Arc<crate::kernel::CompileLedger> {
self.program().ledger()
}
fn resources(&self) -> &crate::resource::ResourceScope {
self.program().resources()
}
fn canonical_hash(&self) -> [u8; 32] {
self.program().canonical_hash()
}
fn coord_count(&self) -> usize {
self.program().coord_count()
}
fn input_value_at(&self, index: usize) -> Option<Value> {
(index < self.state_ref().core.inputs.len())
.then(|| self.state_ref().read_input_value(index))
}
fn input_default_at(&self, index: usize) -> Option<Value> {
self.program().input_default_by_idx(index).cloned()
}
fn input_is_cell_bound(&self, index: usize) -> bool {
self.state_ref().shared_cell(index).is_some()
}
fn reset_inputs(&mut self) {
let from = self.program().coord_count();
self.state().reset_inputs_from(from);
}
fn fork(&self) -> Box<dyn crate::kernel::Kernel> {
Box::new(self.fork_kernel())
}
fn publish_broadcasts(&mut self) {
self.advance_broadcasts();
}
fn commit_write_throughs(&mut self) -> Result<(), String> {
PolydatKernel::commit_write_throughs(self)
}
fn program_id(&self) -> crate::kernel::ProgramId {
crate::kernel::ProgramId(std::sync::Arc::as_ptr(self.program()) as *const () as usize)
}
fn input_type_origin(&self, name: &str) -> Option<crate::kernel::TypeOrigin> {
self.program().input_type_origin(name)
}
fn as_interpreter(&self) -> Option<&PolydatKernel> {
Some(self)
}
fn as_interpreter_mut(&mut self) -> Option<&mut PolydatKernel> {
Some(self)
}
}
impl crate::kernel::KernelInternals for PolydatKernel {
fn set_inherited_outputs(&mut self, names: Vec<String>) {
PolydatKernel::mark_inherited_outputs(self, names);
}
fn set_write_throughs(&mut self, pairs: Vec<(String, String)>) {
PolydatKernel::set_write_throughs(
self,
pairs
.into_iter()
.map(
|(export_name, source_output)| crate::kernel::KernelWriteThrough {
export_name,
source_output,
},
)
.collect(),
);
}
fn set_traversals(
&mut self,
traversals: Vec<crate::dsl::traversal::Traversal>,
producers: Vec<crate::dsl::traversal::Producer>,
) {
PolydatKernel::set_traversals(self, traversals, producers);
}
fn folded_value(&self, name: &str) -> Option<Value> {
if !self.program().is_fixed_output(name) {
return None;
}
self.get_constant(name).cloned()
}
fn set_cursor_extent(&mut self, index: usize, extent: u64) {
let mut schemas = self.program().cursor_schemas().to_vec();
if let Some(schema) = schemas.get_mut(index) {
schema.extent = Some(extent);
self.set_cursor_schemas(schemas);
}
}
}
impl crate::kernel::KernelProgram for crate::kernel::PolydatProgram {
fn engine(&self) -> crate::compile::select::Engine {
crate::compile::select::Engine::Interpreter(self.cone_mode())
}
fn as_interpreter(self: std::sync::Arc<Self>) -> Option<std::sync::Arc<Self>> {
Some(self)
}
fn create_uninitialized(self: std::sync::Arc<Self>) -> Box<dyn crate::kernel::Kernel> {
Box::new(PolydatKernel::from_program(self))
}
fn ledger(&self) -> &std::sync::Arc<crate::kernel::CompileLedger> {
crate::kernel::PolydatProgram::ledger(self)
}
fn resources(&self) -> &crate::resource::ResourceScope {
crate::kernel::PolydatProgram::resources(self)
}
fn canonical_hash(&self) -> [u8; 32] {
crate::kernel::PolydatProgram::canonical_hash(self)
}
fn program_id(&self) -> crate::kernel::ProgramId {
crate::kernel::ProgramId(self as *const Self as *const () as usize)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dsl::compile::compile_polydat_interpreter;
#[test]
fn dataflow_indexed_get() {
let mut k = compile_polydat_interpreter("input cycle: u64\nconst x := 7\n").unwrap();
k.set_inputs(&[42]);
assert_eq!(k.get_wire(0_usize), Some(Value::U64(42)));
}
#[test]
fn dataflow_named_set_get() {
let mut k = compile_polydat_interpreter("input cycle: u64\nextern n: u64\n").unwrap();
k.set_input("n", Value::U64(5)).expect("typed write");
match k.get_wire("n") {
Some(Value::U64(5)) => {}
other => panic!("expected U64(5), got {other:?}"),
}
}
#[test]
fn dataflow_string_key() {
let mut k = compile_polydat_interpreter("input cycle: u64\nextern n: u64\n").unwrap();
let name = String::from("n");
k.set_input(&name, Value::U64(99)).expect("typed write");
assert_eq!(k.get_wire(&name), Some(Value::U64(99)));
assert_eq!(k.get_wire(name.clone()), Some(Value::U64(99)));
}
#[test]
fn dataflow_unknown_name_safe() {
let mut k = compile_polydat_interpreter("input cycle: u64\n").unwrap();
let err = k.set_input("nonexistent", Value::U64(1)).unwrap_err();
assert!(matches!(
err,
crate::kernel::api::WriteError::UnknownWire { .. }
));
assert!(k.get_wire("nonexistent").is_none());
}
#[test]
fn dataflow_type_mismatch_rejected() {
let mut k = compile_polydat_interpreter("input cycle: u64\nextern n: u64\n").unwrap();
let err = k
.set_input(
"n",
Value::VecF32(crate::ast::SliceArc::from_vec(vec![1.0_f32, 2.0])),
)
.unwrap_err();
match err {
crate::kernel::api::WriteError::TypeMismatch {
slot,
expected,
got,
} => {
assert_eq!(slot, "n");
assert_eq!(expected, PortType::U64);
assert_eq!(got, PortType::VecF32);
}
other => panic!("expected TypeMismatch, got {other:?}"),
}
}
#[test]
fn vec_to_scalar_diagnostic_mentions_explicit_helpers() {
let err = crate::kernel::api::WriteError::TypeMismatch {
slot: "score".into(),
expected: PortType::F64,
got: PortType::VecF32,
};
let msg = err.to_string();
assert!(
msg.contains("reduction node"),
"missing reduction hint: {msg}"
);
assert!(msg.contains("vec_dot"), "missing vec_dot hint: {msg}");
}
#[test]
fn typed_write_converts_through_to_port() {
let mut k = compile_polydat_interpreter("input cycle: u64\nextern x: f64\n").unwrap();
assert!(matches!(
k.set_input("x", Value::U64(42)),
Err(crate::kernel::api::WriteError::TypeMismatch { .. })
));
let converted =
crate::convert::to_port(Value::U64(42), PortType::F64).expect("u64 converts to f64");
k.set_input("x", converted).expect("converted value");
match k.get_wire("x") {
Some(Value::F64(42.0)) => {}
other => panic!("expected converted F64(42.0), got {other:?}"),
}
}
#[test]
fn dataflow_none_passes_through_any_slot() {
let mut k = compile_polydat_interpreter("input cycle: u64\nextern n: u64\n").unwrap();
k.set_input("n", Value::None)
.expect("None always permitted");
}
#[test]
fn metadata_listings() {
let k = compile_polydat_interpreter(
"input (cycle: u64, thread: u64)\nextern n: u64\nconst x := 7\n",
)
.unwrap();
let inputs: Vec<String> = k.input_names();
assert!(inputs.iter().any(|s| s == "cycle"));
assert!(inputs.iter().any(|s| s == "n"));
assert_eq!(k.coord_count(), 2); assert!(k.find_input("n").is_some());
assert_eq!(k.input_port_type("n"), Some(PortType::U64));
}
#[test]
fn construction_symmetric_paths() {
let root_opts = crate::kernel::subcontext::CompileOptions {
workload_dir: None,
polydat_lib_paths: Vec::new(),
strict: false,
required_outputs: Vec::new(),
context_label: Some("root".to_string()),
cursor_limit: None,
..Default::default()
};
let root_matter = crate::kernel::subcontext::PolydatMatter::builder()
.label("root")
.source("input cycle: u64\nshared flag := 0\n")
.options(root_opts)
.build()
.expect("matter build");
let root =
<PolydatKernel as Construction>::root(root_matter).expect("root from source matter");
let sub_opts = crate::kernel::subcontext::CompileOptions {
workload_dir: None,
polydat_lib_paths: Vec::new(),
strict: false,
required_outputs: Vec::new(),
context_label: Some("sub".to_string()),
cursor_limit: None,
..Default::default()
};
let sub_matter = crate::kernel::subcontext::PolydatMatter::builder()
.label("sub")
.source("input cycle: u64\n")
.options(sub_opts)
.build()
.expect("matter build");
let _sub = root
.subscope(sub_matter)
.expect("subscope from source matter");
}
#[test]
fn construction_root_from_program() {
let template = compile_polydat_interpreter("input cycle: u64\nextern n: u64\n").unwrap();
let program = template.program().clone();
let matter = crate::kernel::subcontext::PolydatMatter::builder()
.program(program)
.build()
.expect("matter build");
let mut root =
<PolydatKernel as Construction>::root(matter).expect("root from program matter");
root.set_input("n", Value::U64(13)).expect("set_input");
assert_eq!(root.get_wire("n"), Some(Value::U64(13)));
}
#[test]
fn builder_rejects_multiple_forms() {
let template = compile_polydat_interpreter("input cycle: u64\n").unwrap();
match crate::kernel::subcontext::PolydatMatter::builder()
.source("input cycle: u64\n")
.program(template.program().clone())
.build()
{
Err(msg) => assert!(
msg.contains("multiple"),
"expected multiple-forms error, got: {msg}"
),
Ok(_) => panic!("multiple forms must error"),
}
}
#[test]
fn builder_rejects_empty() {
match crate::kernel::subcontext::PolydatMatter::builder().build() {
Err(msg) => assert!(
msg.contains("no input form"),
"expected no-form error, got: {msg}"
),
Ok(_) => panic!("empty matter must error"),
}
}
}