Skip to main content

PolydatProgram

Struct PolydatProgram 

Source
pub struct PolydatProgram { /* private fields */ }
Expand description

The immutable compiled DAG. Shared across fibers via Arc.

Implementations§

Source§

impl PolydatProgram

Source

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.

Source

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.

Source

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 / shared outputs (effectively const upstream; caller emits as promoted-final in case 1)
  • extern ports (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”.

Source

pub fn input_kind(&self, idx: usize) -> Option<InputKind>

Read the input classification for slot idx.

Source

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.

Source

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.

Source

pub fn output_modifier(&self, name: &str) -> BindingModifier

Query the binding modifier for a named output.

Source

pub fn shared_outputs(&self) -> Vec<&str>

Return all output names that have the shared modifier.

Source

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.

Source

pub fn is_inherited(&self, name: &str) -> bool

Is name an inherited (cascade-propagated) output? See Self::mark_inherited.

Source

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.

Source

pub fn const_outputs(&self) -> Vec<&str>

Return all output names that have the const modifier.

Source

pub fn source(&self) -> &str

The original source text that produced this program.

Source

pub fn context(&self) -> &str

Diagnostic context (e.g., “workload.yaml bindings”).

Source

pub fn cursor_schemas(&self) -> &[SourceSchema]

Source schemas declared in this program. The runtime queries these to discover data sources, their extents, and projections.

Source

pub fn create_state(&self) -> PolydatState

Create a new evaluation state for this program.

Source

pub fn create_raw_state(&self) -> RawState

Create a raw state (no provenance). For benchmarking.

Source

pub fn create_provscan_state(&self) -> ProvScanState

Create the provenance-scan engine state (for benchmarking).

Source

pub fn input_names(&self) -> Vec<String>

Return the names of all inputs.

Source

pub fn coord_count(&self) -> usize

Return the number of coordinate inputs.

Source

pub fn find_input(&self, name: &str) -> Option<usize>

Find an input by name. Returns its index.

Source

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.

Source

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.

Source

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.

Source

pub fn input_name_by_idx(&self, idx: usize) -> Option<&str>

Source

pub fn output_count(&self) -> usize

Number of declared outputs.

Source

pub fn output_name(&self, idx: usize) -> &str

Output name at index (declaration order).

Source

pub fn output_names(&self) -> Vec<&str>

Return all output names in declaration order.

Source

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.

Source

pub fn resolve_output_by_index(&self, idx: usize) -> (usize, usize)

Resolve an output index to its (node_index, port_index).

Source

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.

Source

pub fn output_index(&self, name: &str) -> Option<usize>

Source

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.

Source

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.

Source

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.

Source

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).

Source

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.ins order, 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.

Source

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.

Source

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”.

Source

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.

Source

pub fn canonical_hash(&self) -> [u8; 32]

Source

pub fn node_count(&self) -> usize

Number of nodes in the program.

Source

pub fn wire_count(&self) -> usize

Total wire count (sum of all node input edges).

Source

pub fn avg_degree(&self) -> f64

Average in-degree (wires per node).

Source

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.

Source

pub fn node_meta(&self, idx: usize) -> &NodeMeta

Access a node’s metadata by index.

Source

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.

Source

pub fn node_compile_level(&self, idx: usize) -> CompileLevel

Probe the compile level of a node by index.

Source

pub fn last_node_compile_level(&self) -> CompileLevel

Probe the compile level of the last node.

Source

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.

Source

pub fn fold_init_constants(&mut self) -> Result<usize, String>

Source

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).

Source

pub fn fold_init_constants_strict( &mut self, log: Option<&mut CompileEventLog>, strict: bool, ) -> Result<usize, String>

Fold init-time constants with strict mode.

Trait Implementations§

Source§

impl Debug for PolydatProgram

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Send for PolydatProgram

Source§

impl Sync for PolydatProgram

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.