pub struct PolydatProgram { /* private fields */ }Expand description
The immutable compiled DAG. Shared across fibers via Arc.
Implementations§
Source§impl PolydatProgram
impl PolydatProgram
Sourcepub fn ast(&self) -> Option<&Arc<PolydatFile>>
pub fn ast(&self) -> Option<&Arc<PolydatFile>>
The retained AST that produced this program, if any.
SRD-13f §“Wire-reference classification” — the subscope
synthesizer queries this to integrate parent bindings’
graph structure into child scopes. Returns None for
programs built via programmatic (non-DSL) paths.
Sourcepub fn binding_ast_for(&self, name: &str) -> Option<&Statement>
pub fn binding_ast_for(&self, name: &str) -> Option<&Statement>
Find the Statement that defines binding name in this
program’s retained AST. Matches both single-target
InitBinding/CycleBinding and tuple-target destructuring
bindings (where name is one of several targets). Returns
None if no AST is retained or no binding defines name.
Sourcepub fn local_inclusion_chain<'a>(
&'a self,
name: &str,
excluded: &HashSet<String>,
) -> Vec<&'a Statement>
pub fn local_inclusion_chain<'a>( &'a self, name: &str, excluded: &HashSet<String>, ) -> Vec<&'a Statement>
Compute the transitive closure of bindings needed to
materialise name locally in a descendant scope.
SRD-13f §“Wire-reference classification” — case 3 (local
matter inclusion).
Starting from the binding that defines name, recursively
walk the RHS expression tree following Ident references.
For each referenced name, if it’s defined by another
binding in this program’s AST AND is not effectively final
(the four-case rule treats final as a separate cascade),
include that binding too and recurse.
Termination boundaries:
final/sharedoutputs (effectively const upstream; caller emits as promoted-final in case 1)externports (caller handles as case 2 cascade)- Input slots (
cycle, etc.) - Names defined nowhere (will surface as unresolved at compile time of the child scope)
Returns the bindings in topological order (dependencies
first). Names already in excluded are not re-walked,
letting callers express “stop here — this name is locally
defined / coordinated / already collected”.
Sourcepub fn input_kind(&self, idx: usize) -> Option<InputKind>
pub fn input_kind(&self, idx: usize) -> Option<InputKind>
Read the input classification for slot idx.
Sourcepub fn output_map_lookup(&self, name: &str) -> Option<&(usize, usize)>
pub fn output_map_lookup(&self, name: &str) -> Option<&(usize, usize)>
Look up name in the output map, returning (node_idx, port_idx).
Public surface for the scope-init pass and other consumers
outside the kernel module.
Sourcepub fn output_map_iter(
&self,
) -> impl Iterator<Item = (&String, &(usize, usize))>
pub fn output_map_iter( &self, ) -> impl Iterator<Item = (&String, &(usize, usize))>
Iterate every (output-name, (node_idx, port_idx)) pair. Used by the eval-panic enricher to reverse-resolve which output(s) a given node feeds when reporting which binding the panic originated from.
Sourcepub fn output_modifier(&self, name: &str) -> BindingModifier
pub fn output_modifier(&self, name: &str) -> BindingModifier
Query the binding modifier for a named output.
Return all output names that have the shared modifier.
Sourcepub fn mark_inherited(&mut self, name: &str)
pub fn mark_inherited(&mut self, name: &str)
Mark name as an inherited (cascade-propagated) output —
declared on this program only to flow the value through
to descendants via materialize_wiring_from_outer, not because this
scope’s own bindings or specs reference it.
Sourcepub fn is_inherited(&self, name: &str) -> bool
pub fn is_inherited(&self, name: &str) -> bool
Is name an inherited (cascade-propagated) output? See
Self::mark_inherited.
Sourcepub fn own_output_names(&self) -> Vec<&str>
pub fn own_output_names(&self) -> Vec<&str>
Return only the outputs owned by this program — names
the scope’s own bindings, externs, or specs declared,
excluding inherited cascade-propagation outputs. Used by
the scenario tree pre-map and TUI to render per-scope
“what’s defined here” without listing every inherited
name. Output order matches output_names.
Sourcepub fn const_outputs(&self) -> Vec<&str>
pub fn const_outputs(&self) -> Vec<&str>
Return all output names that have the const modifier.
Sourcepub fn cursor_schemas(&self) -> &[SourceSchema]
pub fn cursor_schemas(&self) -> &[SourceSchema]
Source schemas declared in this program. The runtime queries these to discover data sources, their extents, and projections.
Sourcepub fn create_state(&self) -> PolydatState
pub fn create_state(&self) -> PolydatState
Create a new evaluation state for this program.
Sourcepub fn create_raw_state(&self) -> RawState
pub fn create_raw_state(&self) -> RawState
Create a raw state (no provenance). For benchmarking.
Sourcepub fn create_provscan_state(&self) -> ProvScanState
pub fn create_provscan_state(&self) -> ProvScanState
Create the provenance-scan engine state (for benchmarking).
Sourcepub fn input_names(&self) -> Vec<String>
pub fn input_names(&self) -> Vec<String>
Return the names of all inputs.
Sourcepub fn coord_count(&self) -> usize
pub fn coord_count(&self) -> usize
Return the number of coordinate inputs.
Sourcepub fn find_input(&self, name: &str) -> Option<usize>
pub fn find_input(&self, name: &str) -> Option<usize>
Find an input by name. Returns its index.
Sourcepub fn input_port_type(&self, name: &str) -> Option<PortType>
pub fn input_port_type(&self, name: &str) -> Option<PortType>
Lookup the declared port type of a named input.
Returns None if the name isn’t an input of this program.
Sourcepub fn input_port_type_by_idx(&self, idx: usize) -> Option<PortType>
pub fn input_port_type_by_idx(&self, idx: usize) -> Option<PortType>
Lookup the declared port type of an input by index.
Returns None if idx is out of range. Used by the
typed-write fast path so [Dataflow::set_wire_idx] can
type-check without reverse-resolving an index to a name.
Sourcepub fn input_default_by_idx(&self, idx: usize) -> Option<&Value>
pub fn input_default_by_idx(&self, idx: usize) -> Option<&Value>
Lookup the declared name of an input by index. Used by
the typed-write API to render diagnostic messages
referencing the slot the caller addressed.
The declared default for input idx — the wire’s initial
element. The capture layer’s reset semantics (an empty
min/max fold restores the wire to its author-declared
identity rather than leaving Value::None on a typed slot)
read it through this accessor.
pub fn input_name_by_idx(&self, idx: usize) -> Option<&str>
Sourcepub fn output_count(&self) -> usize
pub fn output_count(&self) -> usize
Number of declared outputs.
Sourcepub fn output_name(&self, idx: usize) -> &str
pub fn output_name(&self, idx: usize) -> &str
Output name at index (declaration order).
Sourcepub fn output_names(&self) -> Vec<&str>
pub fn output_names(&self) -> Vec<&str>
Return all output names in declaration order.
Sourcepub fn resolve_output(&self, name: &str) -> Option<(usize, usize)>
pub fn resolve_output(&self, name: &str) -> Option<(usize, usize)>
Resolve an output name to its (node_index, port_index).
Dotted names follow the field-access wire convention
(q.cursor.idx is the wire q__cursor__idx), so a
text-context reference resolves through the same
flattening the DSL compiler applies — mirroring
PolydatKernel::lookup.
Sourcepub fn resolve_output_by_index(&self, idx: usize) -> (usize, usize)
pub fn resolve_output_by_index(&self, idx: usize) -> (usize, usize)
Resolve an output index to its (node_index, port_index).
Sourcepub fn outputs_with_side_effects(&self) -> Vec<String>
pub fn outputs_with_side_effects(&self) -> Vec<String>
Output names whose dependency cone contains a side-effecting
(Purity::SideChannel) node — log_*, diagnostics, etc. These
are the outputs a per-cycle “fire side effects” pass must pull so
the effect runs even when the value is unused. An output whose
cone is side-effect-free — including a pure or volatile
metric-reader value — is excluded: it is evaluated only when its
value is actually consumed, never per cycle just to fire a
non-existent effect.
pub fn output_index(&self, name: &str) -> Option<usize>
Sourcepub fn output_port_type(&self, name: &str) -> Option<PortType>
pub fn output_port_type(&self, name: &str) -> Option<PortType>
Look up an output’s crate::ast::PortType by name.
Returns None for names not declared as outputs of this
program. Used by the binder verification path
(polydat::binder::verify_against_kernel) to type-check
adapter binding shapes against the actual kernel wire
types — symmetric counterpart to input_port_type.
Sourcepub fn input_provenance_for(&self, node_idx: usize) -> Option<&ProvMask>
pub fn input_provenance_for(&self, node_idx: usize) -> Option<&ProvMask>
Get the provenance mask for a node by index. None for an
out-of-range node index.
Sourcepub fn is_equivalent_to(&self, other: &PolydatProgram) -> bool
pub fn is_equivalent_to(&self, other: &PolydatProgram) -> bool
SRD-13d §3.2: hash-compare two programs for AST /
constant equivalence. Two programs that produce the
same canonical_hash are functionally equivalent at
compile time; their runtime instances would differ
only by parent-bound values, which materialize_wiring_from_outer
handles. Cheap (one hash compare); doesn’t allocate
state. The pre-walker uses this to flatten one scope
into another that materialises identical content.
Sourcepub fn is_subset_of(&self, parent: &PolydatProgram) -> bool
pub fn is_subset_of(&self, parent: &PolydatProgram) -> bool
SRD-13d §3.2: “can-flatten?” predicate. Returns true
when this program adds no Polydat content the parent
program doesn’t already supply — i.e. when the inner
scope’s contribution is structurally a subset of the
parent’s. The pre-walker uses this for nodes that
classified as PolydatMatter::Definitions to detect cases
where the new content turns out to be parent-equivalent
(rare, but correct: a binding that duplicates a parent
declaration is structurally a no-op).
Current implementation: structural — true when the inner program has zero outputs and zero inputs beyond what the parent already exposes. The semantic- equivalence form (new bindings whose definitions equal parent bindings) is documented as future work in SRD-13d §8.2 item 4 (hash normalisation depth).
Sourcepub fn instance_hash(&self, ancestors: &[&PolydatProgram]) -> [u8; 32]
pub fn instance_hash(&self, ancestors: &[&PolydatProgram]) -> [u8; 32]
Canonical content-addressable hash of this program.
SHA-256 over a deterministic byte sequence describing every node’s kind + constant slots, every wiring edge, and the named input / output declarations. Stable across compilations of equivalent input — two programs produced from identical source + identical workload- scope state hash to the same value, and a change that affects what the program actually computes (a renamed output, a new node, a const-slot value change, a re-routed wire) shifts the hash.
Used by checkpointing (SRD-44 §“Why hash the compiled
program, not the YAML body”) for per-phase identity:
the resume planner skips a phase only when the saved
hash matches the freshly-compiled program’s hash, so a
{dataset} change that ripples into a phase’s
compiled form correctly invalidates that phase’s
saved status, while phases whose programs are
unaffected stay skip-eligible.
§Determinism contract
- Outputs are emitted in alphabetical order (not the compiler’s declaration order, which can shuffle slightly across compilation passes).
- For each output, the producing node and its transitive input chain are walked in deterministic order — wire-source list iterated in port-position order, recursion uses the producer’s stable (already-canonical) hash as the wire reference.
- Const slots are iterated in
NodeMeta.insorder, which is the DSL-declared positional order and is compiler-invariant. Input(idx)wires are translated to the input’s name (stable across runs) rather than its index (a compile-time positional choice).- Floating-point constants hash via their bit representation, so 0.0 vs -0.0 hash differently and NaNs are distinguishable from each other only by their bit pattern (rare but consistent).
Aggregate identity over this program plus an outer
chain of ancestor programs (innermost first; the
workload-root program is last). The result is a
SHA-256 over each program’s canonical_hash in
declaration order, prefixed with a versioned tag so
future reshapings can be detected.
Use this when callers need “did anything in scope
change?” — including upstream bindings that feed
in via auto-extern. canonical_hash (the per-program
flavour) covers only this program’s own AST and
cannot detect a workload-param edit that lands in a
parent kernel’s const slots.
canonical_hash stays a pure local operation (no
kernel-chain dependency); Polydat refuses to walk parent
scopes inside a per-program hash. The runtime owns
the parent-chain walk and feeds the resulting program
chain here. Callers are responsible for ensuring every
piece of state that should affect identity lives in
some attached Polydat module — e.g. a host injects workload
params: as a synthetic root module
(build_workload_params_kernel) whose const bindings
land in const slots canonical_hash covers.
Sourcepub fn extern_closure(&self, outputs: &[&str]) -> Vec<String>
pub fn extern_closure(&self, outputs: &[&str]) -> Vec<String>
Names of the non-coordinate inputs (iteration externs and external-write ports) that transitively feed the given outputs — the backward dataflow slice a scope needs from its enclosing scopes to produce exactly those outputs.
A projection of the construction-time node inventory (see
Self::compute_node_inventory — no traversal here):
union the producing nodes’ provenance masks, then map set
bits to input names whose kind is not
super::InputKind::Coordinate (coordinates are runtime
dimensions like cycle, not outer-scope matter). Requested
names this program does not declare as outputs are ignored
— the caller keeps them unresolved and continues up its
chain. Sorted, deduplicated.
SRD-107 uses this per-ancestor to derive a phase’s consumed-params closure: which workload params actually reach a given phase through the scope chain.
Sourcepub fn owned_extern_closure(&self) -> Vec<String>
pub fn owned_extern_closure(&self) -> Vec<String>
Self::extern_closure over this program’s OWNED outputs
— inherited passthrough re-exports excluded. Ownership is
what distinguishes consumption from plumbing: the scope
cascade re-exports every inherited name so descendants can
materialize it, and those passthroughs must not read as
“this scope needs the name”.
Sourcepub fn resolve_externs_through(
seed: impl IntoIterator<Item = String>,
ancestors: &[&PolydatProgram],
) -> Vec<String>
pub fn resolve_externs_through( seed: impl IntoIterator<Item = String>, ancestors: &[&PolydatProgram], ) -> Vec<String>
Resolve a seed of unresolved extern names THROUGH a chain
of enclosing scope programs — innermost first, the same
chain shape Self::instance_hash takes. Each name an
ancestor outputs is replaced by that output’s own extern
slice (Self::extern_closure — per-output dataflow, so
sibling outputs’ externs are never dragged in); a
passthrough re-export removes and re-adds the name, which
is exactly “keep walking up”; a name no ancestor outputs
stays. The returned TERMINAL set is what the outermost
scope (e.g. a host’s synthetic params module) must
satisfy — SRD-107’s consumed-params derivation intersects
it with the declared param names. Sorted, deduplicated.
pub fn canonical_hash(&self) -> [u8; 32]
Sourcepub fn node_count(&self) -> usize
pub fn node_count(&self) -> usize
Number of nodes in the program.
Sourcepub fn wire_count(&self) -> usize
pub fn wire_count(&self) -> usize
Total wire count (sum of all node input edges).
Sourcepub fn avg_degree(&self) -> f64
pub fn avg_degree(&self) -> f64
Average in-degree (wires per node).
Sourcepub fn node_ref(&self, idx: usize) -> &dyn PolydatNode
pub fn node_ref(&self, idx: usize) -> &dyn PolydatNode
Access a node by index (trait object). Read-only introspection surface for reporting (SRD-105 lattice report) — evaluation stays behind the kernel APIs.
Sourcepub fn node_wiring(&self, idx: usize) -> &[WireSource]
pub fn node_wiring(&self, idx: usize) -> &[WireSource]
Access the wiring for a node by index.
Returns the list of WireSources feeding this node’s inputs.
Sourcepub fn node_compile_level(&self, idx: usize) -> CompileLevel
pub fn node_compile_level(&self, idx: usize) -> CompileLevel
Probe the compile level of a node by index.
Sourcepub fn last_node_compile_level(&self) -> CompileLevel
pub fn last_node_compile_level(&self) -> CompileLevel
Probe the compile level of the last node.
Sourcepub fn is_deterministic(&self) -> bool
pub fn is_deterministic(&self) -> bool
Fold init-time constant nodes.
Returns Err only when the init-binding contract (SRD 11
§“Init Binding Contract” Plan A) is violated; non-fatal
warnings (config-wire / non-determinism / implicit coercion)
continue to be log-emitted and don’t surface here.
True when no node declares Purity::Nondeterministic: the
program’s outputs are a pure function of its inputs, so two
kernels compiled from the same source produce bit-identical
pulls. The SRD-105 differential battery keys on this to
decide whether a force-compiled twin can be compared
value-for-value against the interpreter form.
pub fn fold_init_constants(&mut self) -> Result<usize, String>
Sourcepub fn fold_init_constants_with_log(
&mut self,
log: Option<&mut CompileEventLog>,
) -> Result<usize, String>
pub fn fold_init_constants_with_log( &mut self, log: Option<&mut CompileEventLog>, ) -> Result<usize, String>
Fold init-time constants, emitting diagnostic events to the log.
Returns Err for init-binding contract violations (Plan A).
Sourcepub fn fold_init_constants_strict(
&mut self,
log: Option<&mut CompileEventLog>,
strict: bool,
) -> Result<usize, String>
pub fn fold_init_constants_strict( &mut self, log: Option<&mut CompileEventLog>, strict: bool, ) -> Result<usize, String>
Fold init-time constants with strict mode.