Ir.Tnodemodule Lazy = Utils.Lazymodule Nd = Ndarraytype memory_mode = | Effectively_constantA constant, or a subset of Virtual.
| VirtualThe tensor node's computations are inlined on a per-scalar basis. The node has no buffer in any context; its defining computation is tracked (in Low_level.optimize_ctx), so it remains observable: its value can be recomputed on demand, including by later routines and for printing. Observability is inductive, not intrinsic: recomputation requires the nodes the tracked computation reads to be observable themselves, so a Virtual node that depends -- directly or transitively through other Virtual nodes -- on a Local node inherits its unobservability (the recompilation raises the same User_error).
| Never_virtualAn as-yet-unresolved request; resolves to Local or On_device.
| LocalRoutine-scoped scratch: the tensor node exists only for the duration of a single call to a compiled function, stored to whatever degree the optimizer decides on (e.g. a stack array), and is not persisted across calls. It is not materialized (owns no context buffer) and is not available for merging across devices. Unlike Virtual -- which stays observable via recomputation -- Local is unobservable, and the sole source of unobservability: its computation is not tracked, and compiling a later routine that reads it raises a User_error directing to mark it as materialized before its first use. This mode is only ever assigned by the compiler (never requested), reserving the freedom to optimize placement of nodes whose lifetime is confined to one routine.
| On_deviceThe tensor node is stored on the devices that compute with it and persisted across function calls. It is available for merging across devices (for devices that support merging / P2P). CPU-side access (printing, persistence, inspection) is on-demand via context-mediated device-to-host transfers; no host copy is stored on the node.
*)val memory_mode_of_sexp : Sexplib0.Sexp.t -> memory_modeval sexp_of_memory_mode : memory_mode -> Sexplib0.Sexp.tval compare_memory_mode : memory_mode -> memory_mode -> Base.intval equal_memory_mode : memory_mode -> memory_mode -> Base.boolval delayed_prec_of_sexp : Sexplib0.Sexp.t -> delayed_precval sexp_of_delayed_prec : delayed_prec -> Sexplib0.Sexp.tval equal_delayed_prec : delayed_prec -> delayed_prec -> Base.booltype bounds_state = | Bounds_unknownNo proposal yet: readers fall back to the precision's machine range.
*)| Bounds_proposed of Interval.tJoin of all proposals so far.
*)| Bounds_settled of Interval.tConsumed by a guard fold; subsequent proposals must fit or raise.
*)Per-tensor scalar value bounds: the interprocedural layer of the interval analysis (docs/proposals/interval-analysis-scalar-t.md), the third instance of the delayed_prec propose/settle lifecycle. Writers propose bounds (joined, like Inferred promotion); a reader that discharges a guard against the candidate settles it (like forcing the prec lazy); a post-settlement proposal that does not fit the settled interval is an error (like update_prec on a settled precision) -- otherwise already-generated code that folded a bounds guard away would become unsound.
Phase B v1 execution anchoring (binding constraint 3): every compiled device write of a node proposes Interval.top at lowering time (see Low_level.optimize_proc), so a candidate can only be narrower than top for host-initialized, never-device-written tensors; guard folds therefore never depend on device-computed values, sidestepping the runs-never/runs-later/runs-repeatedly hazards.
val bounds_state_of_sexp : Sexplib0.Sexp.t -> bounds_stateval sexp_of_bounds_state : bounds_state -> Sexplib0.Sexp.tval equal_bounds_state : bounds_state -> bounds_state -> Base.boolNamespaces must be legal C-family identifiers so they can prefix generated-code identifiers and debug names verbatim (rendered as ns__n42); see docs/proposals gh-ocannl-372.
type t = {prec : Ops.prec Lazy.t;dims : Base.int Base.array Lazy.t;padding : (Ops.axis_padding Base.array * Base.float Base.option) Base.option
Lazy.t;If the tensor node is pre-padded, this is the pair of (left padding, right padding) per axis and the padding/neutral value. The inner float option is None when the tensor is used by operations with different neutral elements, requiring margin resets before each operation.
size_in_bytes : Base.int Lazy.t;id : Base.int;namespace : Base.string;The namespace qualifying id, stamped at creation from the ambient current_namespace unless created with an explicit ?namespace (persistence loads, schedule-internal nodes). Grads share their value node's session namespace. Elided from sexps and renderings when it is default_namespace.
uid : Base.int;Process-unique identity, from a counter that no reinitialization ever resets -- unlike id, which restarts at 0 on Tensor.unsafe_reinitialize for deterministic printing. All comparison/hashing (hence every tnode-keyed map, set and cache in the process) uses uid, so a stale entry surviving a reinitialization can never alias a fresh tnode that reuses its id. Excluded from sexps to keep debug output reinitialization-deterministic.
label : Base.string Base.list;Display information. It is better if the last element of the list is the most narrow or alphanumeric, e.g. an identifier.
*)mutable delayed_prec_unsafe : delayed_prec;mutable bounds : bounds_state;mutable memory_mode : (memory_mode * Base.int) Base.option;The tnode's declared intent -- requests made at graph-construction time (parameter and constant marking, Train.set_materialized, op-support Never_virtual), paired with a provenance identifier. Since the context-scoped memory-modes split (docs/proposals/context-scoped-memory-modes.md) this is monotone, side-effect free to read, and never written by the compilation pipeline: placement decisions are per-compilation-lineage, recorded in Placements tables riding Low_level.optimize_ctx.
mutable observable : Base.bool;Declared host-observation intent (docs/proposals/context-scoped-memory-modes.md category 2): someone intends to read this node's values (printing, persistence, inspection). Monotone-upward (set, never cleared) and side-effect free to read. Observation does not require materialization -- a Virtual resolution stays observable via recomputation -- so the only constraint this imposes on placement is: do not resolve the node into the Local-dependent unobservable class (Placements.default_to_most_local resolves Never_virtual to On_device rather than Local for observable nodes).
mutable host_constant : Base.bool;Declared value-constancy: the node's values are fixed at construction and always equal its registered host-init data (an ndarray-backed literal). Set by Tensor.ndarray (and eligible loaders), never cleared. This carries the constancy that Effectively_constant intent cannot: ndarray-backed nodes are minted On_device (provenance 49), so the memory-mode lattice has no room left for the constant marking. Consumed by Schedule.Stage's hoisted packing (gh-ocannl-470) to justify materializing a repacked copy once per device.
mutable alias_of : (t * Indexing.static_symbol) Base.option;When Some (parent, batch_idx), this node is a zero-copy slice-alias *view* of parent: it owns no buffer of its own, and every read/write of it is redirected (during lowering) to parent with batch_idx prepended as the leading index. Set by Assignments.lower for alias-eligible Fetch.Slices; orthogonal to memory_mode. The strong reference to parent also keeps it reachable for as long as the alias is.
mutable slice_of : (t * Indexing.static_symbol) Base.option;When Some (parent, batch_idx), this node is an \@| sub-tensor slice of parent. Set *eagerly at construction* (independent of alias eligibility), so it is a superset of alias_of: every confirmed alias is a slice, but an ineligible slice (precision- converting, padded, virtual parent) is still a slice that falls back to a materializing copy. Used to reject direct host access (read/write the parent instead) -- including the window before lowering decides eligibility, where alias_of is still None.
mutable backend_info : Base.Sexp.t;mutable code_name : Base.string Base.option;}val sexp_of_t : t -> Sexplib0.Sexp.tval num_elems : t -> Base__Int.tval dims_without_padding : t -> Base.int Base.arrayval get_padding :
t ->
(Ops.axis_padding Base.array * Base.float Base.option) Base.optionval id : t -> Base.String.tval label : t -> Base.String.tval debug_name : t -> Base.String.tval debug_memory_mode : (memory_mode * Base.Int.t) option -> Base.String.tval log_debug_info : from_log_level:int -> t -> unitval most_local_materialized_mode : t -> memory_modeThe mode Never_virtual resolves to when defaulted: Local if the node fits the stack threshold, On_device otherwise.
val is_observable : t -> Base.boolval set_observable : t -> unitDeclares host-observation intent (see observable). Monotone: there is no unset.
val is_alias : t -> boolA slice-alias view (see alias_of). Such a node owns no buffer of its own; its accesses are redirected to its parent during lowering.
val alias_of : t -> (t * Indexing.static_symbol) Base.optionval set_alias_of : t -> parent:t -> batch_idx:Indexing.static_symbol -> unitMarks tn as a zero-copy slice-alias view of parent with leading index batch_idx. Idempotent when re-marked with the same parent.
val is_slice : t -> boolval slice_of : t -> (t * Indexing.static_symbol) Base.optionval set_slice_of : t -> parent:t -> batch_idx:Indexing.static_symbol -> unitMarks tn as an \@| slice of parent eagerly at construction (before alias eligibility is known). Idempotent.
val known_not_materialized : t -> boolval known_constant : t -> boolval known_host_constant : t -> Base.boolWhether tn's values are declared fixed at construction, forever equal to its registered host-init data (see host_constant). Includes known_constant intent: small constants keep the Effectively_constant marking (their creation does not pass through the On_device-minting ndarray-backed path).
val set_host_constant : t -> unitDeclares that tn's values are fixed at construction (host-init-backed literal). Monotone: set, never cleared.
val known_non_virtual : t -> boolval known_virtual : t -> boolval mode_is_unspecified : t -> boolval transition_memory_mode :
debug_name:string ->
(memory_mode * Base.Int.t) option ->
memory_mode ->
Base.Int.t ->
memory_mode * Base.Int.tThe pure memory-mode lattice transition, shared between update_memory_mode (tnode-level declared intent) and per-context placement resolution (Placements). Returns the new state; raises on conflicting requests.
val update_memory_mode : t -> memory_mode -> Base.Int.t -> unit(see bounds_state)
val propose_bounds : what:Base.String.t -> t -> Interval.t -> unitJoins iv into the node's candidate bounds. Post-settlement, instead validates that iv fits the settled interval and raises Utils.User_error otherwise -- a wider write after a reader already folded a guard against the bounds would leave compiled code unsound. what names the proposing write for the error message.
val pin_bounds_top : what:Base.String.t -> t -> unitPins the candidate to Interval.top: Phase B v1 execution anchoring for compiled device writes. Post-settlement (of non-top bounds) this raises like any non-fitting proposal.
val bounds_candidate : t -> Interval.t optionThe current candidate (or settled) bounds, if any proposal was made. Readers should intersect with the precision's machine range themselves.
val settle_bounds : t -> unitMarks the candidate as consumed by a guard fold. Idempotent; raises if nothing was ever proposed (folds must only consume existing candidates).
val bounds_scan_worthwhile : Ops.prec -> boolWhether host-upload scans can profit for this precision: integer storage only -- the current consumers (gather-guard discharge, index-width selection) act on integer-valued facts, and the Phase A folding policy ignores float bounds (binding constraint 8: gate the O(n) scans).
val scan_host_bounds : Ops.prec -> Nd.t -> Interval.tScans a host buffer about to initialize tn's device data and proposes the observed bounds (pre-settlement) or validates them (post-settlement). Call on every host-write path (Context.from_host-mediated writes and link-time Host_inits uploads). No-op for float and opaque precisions, and when the candidate is already pinned to top.
include sig ... endval comparator : (t, comparator_witness) Base__Comparator.comparatorval hash : t -> Base__Ppx_hash_lib.Std.Hash.hash_valueval hash_fold_t :
Base__.Ppx_hash_lib.Std.Hash.state ->
t ->
Base__.Ppx_hash_lib.Std.Hash.stateval hash_t : t -> Base__Ppx_hash_lib.Std.Hash.hash_valuemodule Comp : sig ... endmodule Placements : sig ... endval sexp_of_t_set : (t, 'a) Base.Set.t -> Sexplib0.Sexp.ttype 'a t_map = (t, 'a, comparator_witness) Base.Map.tval sexp_of_t_map :
('a -> Sexplib0.Sexp.t) ->
(t, 'a, 'b) Base.Map.t ->
Sexplib0.Sexp.tval dims_to_string : ?with_axis_numbers:bool -> t -> Base.String.tval no_grad_ident_label : t -> bool * Base.String.t optionval styled_ident :
repeating_nograd_idents:(Base.String.t, 'a) Base.Hashtbl.t ->
repeating_grad_idents:(Base.String.t, 'a) Base.Hashtbl.t ->
[< `Heuristic_ocannl of [< `Dot_grad | `Under_grad ]
| `Name_and_label
| `Name_only ] ->
t ->
Base.String.tval update_code_name : t -> Base.string -> unitval header : t -> stringmodule Registry : sig ... endval registry : Registry.tval create :
?namespace:Base.String.t ->
delayed_prec ->
id:Base__Int.t ->
label:Base.String.t Base.List.t ->
unpadded_dims:Base__Int.t Base.Array.t Lazy.t ->
padding:
(Ops.axis_padding Base.array * Base.float Base.option) Base.option Lazy.t ->
unit ->
tval create_from_padded :
?namespace:Base.String.t ->
id:Base__Int.t ->
label:Base.String.t Base.List.t ->
ndarray:Nd.t ->
padding:(Ops.axis_padding Base.array * Base.float Base.option) Base.option ->
unit ->
t * Nd.t lazy_tval initial_default_prec : Ops.precval find_namespaced :
namespace:Base.string ->
id:Base.int ->
Registry.data optionval find : id:Base.int -> Registry.data optionHost-side value access (get_value, set_value, get_values, set_values, points_1d, points_2d) now lives in Context: it requires an explicit context and performs an on-demand device-to-host transfer. There is no host copy stored on the tensor node.
val print_accessible_headers :
?pred:(Registry.data -> bool) ->
unit ->
Base.unitval log_accessible_headers : ?pred:(Registry.data -> bool) -> unit -> unit