1pub mod activation;
50mod api;
51mod api_impl;
52pub(crate) mod engines;
53pub mod intern;
54pub mod interp;
55mod manifest;
56mod opt;
57mod program;
58mod scope;
59mod state;
60pub mod subcontext;
61pub use activation::{Activation, CursorSlice, TraversalStream};
62
63pub(crate) use api::SharedKernel;
64pub(crate) use api::internals::KernelInternals;
65pub use api::{Construction, Dataflow, Kernel, KernelProgram, Metadata, WireKey, WriteError};
66pub use engines::*;
67pub use intern::{StaticInterner, static_pair};
68pub use manifest::{ManifestEntry, extract_manifest};
69pub use opt::KernelOptLevel;
70pub use program::*;
71pub use scope::{ScopeCoord, format_scope_coordinate_path};
72pub use state::*;
73
74use crate::ast::Value;
75
76#[derive(Debug, Clone)]
78pub enum WireSource {
79 Input(usize),
82 NodeOutput(usize, usize),
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub enum InputKind {
97 Coordinate,
100 IterationExtern,
104 ExternalWrite,
108}
109
110#[derive(Debug, Clone)]
119pub struct InputDef {
120 pub name: String,
122 pub default: Value,
125 pub port_type: crate::ast::PortType,
128 pub kind: InputKind,
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use std::collections::HashMap;
138 use std::sync::Arc;
139
140 #[test]
141 fn capture_inputs_persist_across_set_inputs() {
142 let program = Arc::new(PolydatProgram::with_inputs(
144 vec![],
145 vec![],
146 vec![
147 InputDef {
148 name: "cycle".into(),
149 default: Value::U64(0),
150 port_type: crate::ast::PortType::U64,
151 kind: InputKind::Coordinate,
152 },
153 InputDef {
154 name: "balance".into(),
155 default: Value::F64(0.0),
156 port_type: crate::ast::PortType::F64,
157 kind: InputKind::ExternalWrite,
158 },
159 InputDef {
160 name: "auth_token".into(),
161 default: Value::Str("anonymous".into()),
162 port_type: crate::ast::PortType::Str,
163 kind: InputKind::ExternalWrite,
164 },
165 ],
166 1, HashMap::new(),
168 Vec::new(),
169 "",
170 "(test)",
171 crate::kernel::CompileLedger::new(),
172 ));
173 let mut state = program.create_state();
174
175 assert_eq!(state.get_input(1), Value::F64(0.0));
177 assert_eq!(state.get_input(2), Value::Str("anonymous".into()));
178
179 state.set_input(1, Value::F64(1234.56));
181 state.set_input(2, Value::Str("token_abc".into()));
182 assert_eq!(state.get_input(1), Value::F64(1234.56));
183 assert_eq!(state.get_input(2), Value::Str("token_abc".into()));
184
185 state.set_inputs(&[42]);
187 assert_eq!(state.get_input(1), Value::F64(1234.56));
188 assert_eq!(state.get_input(2), Value::Str("token_abc".into()));
189 }
190
191 #[test]
192 fn reset_inputs_restores_capture_defaults() {
193 let program = Arc::new(PolydatProgram::with_inputs(
194 vec![],
195 vec![],
196 vec![
197 InputDef {
198 name: "cycle".into(),
199 default: Value::U64(0),
200 port_type: crate::ast::PortType::U64,
201 kind: InputKind::Coordinate,
202 },
203 InputDef {
204 name: "token".into(),
205 default: Value::Str("anon".into()),
206 port_type: crate::ast::PortType::Str,
207 kind: InputKind::ExternalWrite,
208 },
209 ],
210 1,
211 HashMap::new(),
212 Vec::new(),
213 "",
214 "(test)",
215 crate::kernel::CompileLedger::new(),
216 ));
217 let mut state = program.create_state();
218
219 state.set_input(1, Value::Str("alice".into()));
220 assert_eq!(state.get_input(1), Value::Str("alice".into()));
221
222 state.reset_inputs_from(1);
224 assert_eq!(state.get_input(1), Value::Str("anon".into()));
225 }
226
227 #[test]
228 fn invalidate_all_keeps_inputs_and_reset_restores_defaults() {
229 let program = Arc::new(PolydatProgram::with_inputs(
230 vec![],
231 vec![],
232 vec![
233 InputDef {
234 name: "cycle".into(),
235 default: Value::U64(0),
236 port_type: crate::ast::PortType::U64,
237 kind: InputKind::Coordinate,
238 },
239 InputDef {
240 name: "token".into(),
241 default: Value::Str("anon".into()),
242 port_type: crate::ast::PortType::Str,
243 kind: InputKind::ExternalWrite,
244 },
245 ],
246 1,
247 HashMap::new(),
248 Vec::new(),
249 "",
250 "(test)",
251 crate::kernel::CompileLedger::new(),
252 ));
253 let mut state = program.create_state();
254
255 state.set_inputs(&[42]);
256 state.set_input(1, Value::Str("alice".into()));
257
258 state.invalidate_all();
259 assert_eq!(state.get_input(0), Value::U64(42));
260 assert_eq!(state.get_input(1), Value::Str("alice".into()));
261 state.reset_inputs_from(0);
262 assert_eq!(state.get_input(0), Value::U64(0));
263 assert_eq!(state.get_input(1), Value::Str("anon".into()));
264 }
265
266 struct ConfigWireTestNode {
274 meta: crate::ast::NodeMeta,
275 }
276
277 impl ConfigWireTestNode {
278 fn new() -> Self {
279 use crate::ast::{Port, Slot};
280 Self {
281 meta: crate::ast::NodeMeta {
282 name: "config_test".into(),
283 outs: vec![Port::u64("output")],
284 ins: vec![
285 Slot::Wire(Port::u64("config_param").config()),
286 Slot::Wire(Port::u64("data_input")),
287 ],
288 },
289 }
290 }
291 }
292
293 impl crate::ast::PolydatNode for ConfigWireTestNode {
294 fn meta(&self) -> &crate::ast::NodeMeta {
295 &self.meta
296 }
297 fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
298 let config = inputs[0].as_u64();
299 let data = inputs[1].as_u64();
300 outputs[0] = Value::U64(config.wrapping_add(data));
301 }
302 }
303
304 #[test]
305 fn wire_cost_no_warning_when_config_is_init_time() {
306 use crate::compile::assembly::{PolydatAssembler, WireRef};
310 use crate::dsl::events::CompileEventLog;
311 use crate::library::identity::ConstU64;
312 use crate::library::identity::Identity;
313
314 let mut asm = PolydatAssembler::new(vec!["cycle".into()]);
315 asm.add_node("config_val", Box::new(ConstU64::new(42)), vec![]);
316 asm.add_node(
317 "hashed",
318 Box::new(Identity::new(crate::ast::PortType::U64)),
319 vec![WireRef::input("cycle")],
320 );
321 asm.add_node(
322 "test_node",
323 Box::new(ConfigWireTestNode::new()),
324 vec![WireRef::node("config_val"), WireRef::node("hashed")],
325 );
326 asm.add_output("result", WireRef::node("test_node"));
327
328 let mut log = CompileEventLog::new();
329 let k = asm.compile_with_log(Some(&mut log)).unwrap();
330 let _program = k.into_program();
331
332 let warnings: Vec<_> = log
334 .events()
335 .iter()
336 .filter(|e| {
337 matches!(
338 e,
339 crate::dsl::events::CompileEvent::ConfigWireCycleWarning { .. }
340 )
341 })
342 .collect();
343 assert!(
344 warnings.is_empty(),
345 "no warning expected when config wire is init-time: {warnings:?}"
346 );
347 }
348
349 #[test]
350 fn wire_cost_warning_when_config_is_cycle_time() {
351 use crate::compile::assembly::{PolydatAssembler, WireRef};
355 use crate::dsl::events::CompileEventLog;
356 use crate::library::identity::Identity;
357
358 let mut asm = PolydatAssembler::new(vec!["cycle".into()]);
359 asm.add_node(
360 "hashed",
361 Box::new(Identity::new(crate::ast::PortType::U64)),
362 vec![WireRef::input("cycle")],
363 );
364 asm.add_node(
365 "test_node",
366 Box::new(ConfigWireTestNode::new()),
367 vec![
368 WireRef::node("hashed"), WireRef::input("cycle"), ],
371 );
372 asm.add_output("result", WireRef::node("test_node"));
373
374 let mut log = CompileEventLog::new();
375 let _k = asm.compile_with_log(Some(&mut log)).unwrap();
376
377 let warnings: Vec<_> = log
378 .events()
379 .iter()
380 .filter(|e| {
381 matches!(
382 e,
383 crate::dsl::events::CompileEvent::ConfigWireCycleWarning { .. }
384 )
385 })
386 .collect();
387 assert_eq!(
388 warnings.len(),
389 1,
390 "expected exactly one config wire warning: {warnings:?}"
391 );
392 }
393
394 #[test]
395 fn wire_cost_warning_when_config_is_coordinate_direct() {
396 use crate::compile::assembly::{PolydatAssembler, WireRef};
399 use crate::dsl::events::CompileEventLog;
400
401 let mut asm = PolydatAssembler::new(vec!["cycle".into()]);
402 asm.add_node(
403 "test_node",
404 Box::new(ConfigWireTestNode::new()),
405 vec![
406 WireRef::input("cycle"), WireRef::input("cycle"), ],
409 );
410 asm.add_output("result", WireRef::node("test_node"));
411
412 let mut log = CompileEventLog::new();
413 let _k = asm.compile_with_log(Some(&mut log)).unwrap();
414
415 let warnings: Vec<_> = log
416 .events()
417 .iter()
418 .filter(|e| {
419 matches!(
420 e,
421 crate::dsl::events::CompileEvent::ConfigWireCycleWarning { .. }
422 )
423 })
424 .collect();
425 assert_eq!(warnings.len(), 1, "config wire from coordinate should warn");
426 }
427
428 #[test]
429 fn wire_cost_no_warning_data_wire_from_cycle() {
430 use crate::compile::assembly::{PolydatAssembler, WireRef};
434 use crate::dsl::events::CompileEventLog;
435 use crate::library::identity::ConstU64;
436
437 let mut asm = PolydatAssembler::new(vec!["cycle".into()]);
438 asm.add_node("config_val", Box::new(ConstU64::new(10)), vec![]);
439 asm.add_node(
440 "test_node",
441 Box::new(ConfigWireTestNode::new()),
442 vec![
443 WireRef::node("config_val"), WireRef::input("cycle"), ],
446 );
447 asm.add_output("result", WireRef::node("test_node"));
448
449 let mut log = CompileEventLog::new();
450 let _k = asm.compile_with_log(Some(&mut log)).unwrap();
451
452 let warnings: Vec<_> = log
453 .events()
454 .iter()
455 .filter(|e| {
456 matches!(
457 e,
458 crate::dsl::events::CompileEvent::ConfigWireCycleWarning { .. }
459 )
460 })
461 .collect();
462 assert!(warnings.is_empty(), "data wire from cycle should not warn");
463 }
464
465 #[test]
466 fn wire_cost_diamond_config_from_init() {
467 use crate::compile::assembly::{PolydatAssembler, WireRef};
473 use crate::dsl::events::CompileEventLog;
474 use crate::library::identity::ConstU64;
475 use crate::library::identity::Identity;
476
477 let mut asm = PolydatAssembler::new(vec!["cycle".into()]);
478 asm.add_node("a", Box::new(ConstU64::new(5)), vec![]);
479 asm.add_node("b", Box::new(ConstU64::new(3)), vec![]);
480 asm.add_node(
481 "inner",
482 Box::new(ConfigWireTestNode::new()),
483 vec![WireRef::node("a"), WireRef::node("b")],
484 );
485 asm.add_node(
486 "hashed",
487 Box::new(Identity::new(crate::ast::PortType::U64)),
488 vec![WireRef::input("cycle")],
489 );
490 asm.add_node(
491 "outer",
492 Box::new(ConfigWireTestNode::new()),
493 vec![
494 WireRef::node("inner"), WireRef::node("hashed"), ],
497 );
498 asm.add_output("result", WireRef::node("outer"));
499
500 let mut log = CompileEventLog::new();
501 let _k = asm.compile_with_log(Some(&mut log)).unwrap();
502
503 let warnings: Vec<_> = log
506 .events()
507 .iter()
508 .filter(|e| {
509 matches!(
510 e,
511 crate::dsl::events::CompileEvent::ConfigWireCycleWarning { .. }
512 )
513 })
514 .collect();
515 assert!(
516 warnings.is_empty(),
517 "init-time derived config should not warn: {warnings:?}"
518 );
519 }
520
521 #[test]
522 fn wire_cost_diamond_config_from_mixed() {
523 use crate::compile::assembly::{PolydatAssembler, WireRef};
529 use crate::dsl::events::CompileEventLog;
530 use crate::library::identity::ConstU64;
531
532 let mut asm = PolydatAssembler::new(vec!["cycle".into()]);
533 asm.add_node("five", Box::new(ConstU64::new(5)), vec![]);
534 asm.add_node(
535 "mixer",
536 Box::new(ConfigWireTestNode::new()),
537 vec![
538 WireRef::node("five"), WireRef::input("cycle"), ],
541 );
542 asm.add_node(
543 "outer",
544 Box::new(ConfigWireTestNode::new()),
545 vec![
546 WireRef::node("mixer"), WireRef::input("cycle"), ],
549 );
550 asm.add_output("result", WireRef::node("outer"));
551
552 let mut log = CompileEventLog::new();
553 let _k = asm.compile_with_log(Some(&mut log)).unwrap();
554
555 let warnings: Vec<_> = log
556 .events()
557 .iter()
558 .filter(|e| {
559 matches!(
560 e,
561 crate::dsl::events::CompileEvent::ConfigWireCycleWarning { .. }
562 )
563 })
564 .collect();
565 assert_eq!(
568 warnings.len(),
569 1,
570 "exactly one warning for outer's config: {warnings:?}"
571 );
572 }
573}