pyxlog lets you compile and run XLOG programs from Python and exchange CUDA tensors with PyTorch, CuPy, JAX, and TensorFlow through DLPack. Transient inputs and query-result handoffs share device allocations; persistent relation imports and stored-relation exports make device-to-device ownership copies so session state cannot be mutated through an external tensor.
Always import pyxlog, never pyxlog._native. The package has two layers: a native PyO3 extension (pyxlog._native) and a pure-Python wrapper that re-exports it and adds convenience methods. Some documented methods — including evaluate_async, evaluate_stream, the relation callbacks, temporal provenance, and the nn/4 lineage helpers — live only on the wrapped classes. Importing from pyxlog._native directly will make those methods appear missing.

Overview

The pyxlog Python module provides:
  • Deterministic (ordinary, non-probabilistic) Datalog execution via LogicProgram
  • Probabilistic inference (facts and rules carry probabilities) via Program
  • Term embedding registration and lookup via register_embedding / forward_embedding
  • Differentiable inductive logic programming (ILP) — learning Datalog rules from labeled examples — via pyxlog.ilp
  • Reusable diagnostics for downstream applications (“external consumers”): learned-rule inventories, audits of the tight GPU inner loop (“hot loop”), and grouped transfer metrics
  • DLPack GPU tensor exchange, with zero-copy transient inputs and query-result handoffs plus owned persistent relation storage
  • Optional, experimental Apache Arrow C Device interop (enabled by a build feature)
  • Runtime introspection surfaces (“living-world diagnostics”): rule provenance, proof traces, incremental relation-change (“delta”) debugging, temporal relation metadata, native whole-fact relation evidence, and neural hot-loop audits
Convenience outputs that must be read on the host CPU (probabilities, gradients, confidence intervals) are behind a host-io Cargo build feature. Keeping them optional lets device-result code keep result buffers out of host memory. Individual routes can still observe bounded status or terminal-summary data; their documented transfer measurements, not the final tensor’s device field alone, establish residency. For the full map of runtime introspection surfaces, see Living-World Diagnostics.

Installation

To install a published wheel:
On import, pyxlog checks for bundled CUDA kernel artifacts under pyxlog/kernels/ and, when present, exports that directory to XLOG_CUBIN_DIR automatically. Any pilot script, probe harness, or artifact replay that runs outside the packaged wheel layout should set XLOG_CUBIN_DIR explicitly before importing pyxlog, for example:
This matters most for cold-start execution on saved inputs: without XLOG_CUBIN_DIR, startup can fail if the active install does not contain pyxlog/kernels/. For local development or an API present in this checkout but absent from the selected wheel:
Use the Python executable from the downstream project, not necessarily the Python from the xlog checkout. The helper stages generated CUDA artifacts, builds a wheel for that interpreter with maturin build -i, installs the wheel with the same interpreter’s pip, and verifies that the installed package has pyxlog/kernels/. Generated .ptx and .cubin files remain build artifacts and are not tracked in git.

Build Features

Build features are compile-time flags that turn optional API surfaces on:
  • host-io: enable host-read convenience APIs (e.g. CompiledProgram.evaluate(...))
  • arrow-device-import: enable experimental Arrow C Device export/import helpers
Example:

Semantic task binding

SemanticTransitionColdTask executes its admitted observer once during construction and exposes task_content before final task import. This read returns (query_identity, theory_program_identity, result_identity, truths) with three 32-byte identities and three truth codes (0=Neither, 1=True, 2=False, 3=Both). It carries no use authority. Final import executes the observer again and must reproduce the same identities and truths; a mismatch refuses the native session. Cold content observation does not run the model or prefill its cache. The semantic controller’s task_observation_roots and import_task methods share five explicit keyword arguments in addition to their admission and authority inputs. import_task also requires task_priority_levels: Import runs the authored program through XLOG’s native GPU evaluator and derives expected Boolean answers from its completed results. There is no expected-answer argument or built-in task. Source, selected queries, observed results, scoring, ordered priorities, masks, and actor eligibility become part of the task identity checked during restoration. The imported TaskUse.task_priority_levels() returns the exact native order before Proposal; completed task_ground.priority_levels returns the same record after execution. TaskUse.semantic_goal_root() returns the existing bound 32-byte native goal root after import and before Proposal; completed task ground retains that exact root. The read requires the creator thread and current issued task use, and does not recompute the goal or grant execution authority. For each goal the native truth match is normalized to zero or one. Selection compares weighted progress against the acquired base of the current step, level by level; forbidden truth statuses exclude a candidate before ranking. Predictive query agreement, lower measured cost, then lowest candidate index break remaining ties. The six scoring coefficients still measure work and return; they are not priority levels. See the Rust task contract for scoring and numerical-policy build requirements. These inputs replace the former implicit task; there is no legacy fallback.

Semantic replay carrier

Each nonempty replay_rows entry has this shape:
basis is episode or anchor. The adapter consumes the original producer’s training identity: manifest hash, example identifier, split, tokenizer hash and revision, mask-policy hash, training seed, epoch and variant. These nine values and their original digest must match the canonical record. Integers remain exact, including values larger than 64 bits. No additional native identity projection or rewritten record is required. The transported material pairs retain the original ordered references and exact bytes (or an explicitly declared reconstruction). The training-view material uses the producer’s training-view/v2 bytes; the older v1 layout is not accepted or converted. Its 264-byte header binds identity, source, dimensions, answer boundary and three declared branch spans (all zero without branches). The row then contains token IDs, MASK labels and weights, autoregressive and causal-memory retention labels, branch labels and IDs, and the forest’s source slots, logical positions, kinds and parents. The original bytes, dimensions, target coordinates and branch supervision are checked before device selection. An episode requires authentic native execution evidence. An anchor instead retains the original admission evidence and retention group; it cannot be selected as native execution replay or confer actor credit. Its record contains exactly example_id, group, training_view, training_view_identity and content_sha256 (plus the three task-content identities for a symbolic anchor). An admitted live anchor must carry the exact envelope supplied in live_authorities, the train partition, the durable-slow-consolidation purpose, and both cross-request reuse and durable-checkpoint rights. The current snapshot checks expiry and revocation on import and subsequent use. Corpus and live anchors use the same row owner and wire domain; neither gains an invocation’s model generation. Carrier integrity and admission do not replace the trusted application’s fresh use permissions. Importing a carrier does not establish that device-side training, Update publication or restoration has completed. task_observation_roots requires the same task_scope and live_authorities that will be passed to import_task. It binds a live anchor to that original authority before computing coverage; import_task still independently checks the current snapshot and use rights. During replay, restore_invocation(task_use, restored_parent, full_row, transition) returns (original_model_output, finalize_continuation, consumer_stream). The callable retains the same original invocation; it does not return or seal the final continuation during restoration. For Proposal, the importer first consumes the original pack_policy result and snapshots all nine CUDA operands before binding the same parent’s continuation. Native retains these pending banks and the original witness, joins the supplied consumer stream, and rechecks the original seals at continuation binding without replacing the snapshots. Pending snapshots cannot execute or draw until that join succeeds. The importer calls finalize_continuation(task_use, restored_parent, original_model_output) once after the policy handoff and before capture/launch, within the same guarded import read scope and without native locks. It returns (text_rows, text_row_count, selected_text, active_rows, active_row_count, numerical_admissibility, numerical_blocks, model_schema_digest, model_identity, tensors, producer_witness) for Proposal. Recompute and Drain omit the three physical-certificate fields and use the same delayed callback immediately before their continuation binding. The consumer stream is fixed by the first result; the final callback supplies the services and their final witness, not a new stream. This ordering does not supply a uniform numerical certificate or replace the required pre-draw admission. During proposal replay, the native importer owns final use of its original policy invocation and schedules native finalization before asking the Runtime to retire its temporary aliases; reader retirement then joins the named consumer streams. Full predecessor and successor comparisons remain required. The Runtime cleanup callback must not call public policy finish or backward methods during import. A failed final-use call quarantines the original invocation, and uncertain completion prevents reader release. Import does not retry final use, repeat publication, or substitute a backward call. For a prepared Proposal with a sealed physical-parameter roster, controller.bind_continuation(..., parent=step, bank=bank, numerical_blocks=blocks, model_content_witness=model_content) consumes the original witness returned by bind_model_content for that same Session, step and bank. Omit model_schema_digest and model_identity: the original bank guard must already have recorded its private acquired model-contract snapshot before the model forward. Native retains that snapshot, the model witness and its producer owners; the existing CUDA continuation node reads its schema and generation identity on each replay before validating the acquired record. There is no host identity readback, recapture or certificate reassignment after Update. producer_witness remains the distinct transient witness covering the complete continuation tensor roster and all seven services, including the original FP64 [N_physical,2,3] blocks as the seventh service. Non-drawing prepared steps omit both blocks and the model witness. Acquired replay retains the verified host identity interface above and rejects a prepared model witness. These bindings do not establish scientific numerical acceptance or measured ZERO TRANSFER. The original nine policy operands are CUDA FP32 text logits [32,V], Bool8 product support, FP32 packed parameters [P], FP32 component baselines [1,136], FP64 pointwise errors [P,2] and [1,136,2], then FP64 domains [P,4], [32,V,4] and [1,136,4]. Errors use (O,U) order; domains use (M,O,U,I) order. I bounds the ideal formula independently of actual magnitude and error radii. Domain O/U cover the allowed domain; they do not replace the pointwise errors. Supply the baseline’s independent ideal bound from its original producer, with the same parent, step/bank, physical model roster, generation and consumer stream as the witnessed policy pack. The actor keeps the frozen original FP32 baseline exact; the critic retains its live envelope. Late policy differentiation returns nine capsules: three actual FP32 parameter, text and baseline cotangents, three full FP64 error arrays with trailing extent 2, then contiguous FP64 domains [2,P,4], [2,32,V,4] and [2,1,136,4]. The first axis is actor/full; each entry is (M,O,U,I). Full errors and D.full.O/U come from the same result. Only the first three capsules enter the original GraphTask. These selected-tape bounds do not certify every allowed support or prefix before a draw, or the subsequent physical model accumulation. This replaces the former three-field layouts; existing compiled artifacts are not compatible with the new shapes.

Semantic runtime checkpoints

The fresh parent’s acknowledgement_payload_capacity_bytes reserves the full receipt bytes for every possible native intent. With N = intent_entry_capacity, P = intent_payload_capacity_bytes, and E = len(intent_effect), it must be at least 169*N + P + (N-1)*E. The native initializer checks this bound and owns the acknowledgement queue; callers cannot supply role 32 records. Proposal and Drain each reserve a continuation and a delivery-acknowledgement fuel unit. For an external effect, the trusted application binds its live receiver once with controller.bind_delivery_receiver(task_use, recipient_id=bytes32, verification_key=bytes32, expected_effect=bytes). The key stays in the local rights store, not the checkpoint. After FINAL, the application saves a durable checkpoint, obtains its native intent, and sends its exact stable identity, effect and payload to the receiver. The receiver durably records those bytes before returning a receipt. The receipt is b"xlog.delivery.receipt.v1\0" || stable_identity || recipient_id || LE64(len(effect)) || effect || LE64(len(payload)) || payload || record_digest || mac, where record_digest is SHA-256 of the same identity, recipient, length and content fields prefixed by b"xlog.delivery.record.v1\0", and mac is HMAC-SHA256 of the receipt body under the bound key. Both domain terminators are single NUL bytes. A retry of the same stable identity must return the same stored receipt, without applying the effect again. controller.acknowledge_delivery(task_use, parent=..., snapshot=..., receipt=...) checks the fresh use rights and full receiver receipt against the actual FINAL intent, then appends it through the native publication CAS. It returns the new (instance, word, logical_digest, state_digest); the caller must acquire that new parent and save a new checkpoint. The CAS does no model step and preserves the selected model allocation, text, theory, cache and RNG. The old parent cannot acknowledge again. After a cold restore, bind the same trusted receiver to the newly issued task use before invoking controller.checkpoint_pending_output_intents(task_use, checkpoint). That projection verifies both sealed queues and every stored receipt MAC, returning only unacknowledged (stable_identity, effect, payload) rows. The older checkpoint_output_intents(checkpoint) remains an all-intents audit read, not a delivery queue. An exhausted FINAL parent cannot start an unacknowledged delivery; an already acknowledged terminal intent cannot be activated again. SemanticTransitionController.save_checkpoint(task_use, *, parent, consumer_streams, snapshot, snapshot_model_state) returns one sealed bytes value containing the selected native publication, the task capsule, the session allocation contract, and the application’s exact model-state bytes. The controller joins every listed consumer stream before the cold native export. It then calls snapshot_model_state() exactly once on the creator thread without holding the native owner or task-state lock, and revalidates the task binding and acquired publication before sealing the result. The callback must return exact builtin bytes; failure or concurrent mutation returns no checkpoint. The serializer runs inside native’s temporary controlled-reader scope for that actual parent. It can bind and verify the selected model against the original publication even when the stable task is Imported. Native does not invent an inference/training operation for that state. Reader acquisition and release are allowed for the original serializer’s aliases; build, execution, nested import, and nested checkpoint save remain unavailable. The scope closes on return, exception, or unwind before the post-callback checks. A saved task reference does not retain that temporary permission. SemanticTransitionSession.restore_checkpoint(checkpoint, *, device_ordinal, snapshot, restore_model, training_domain) validates every section and the retained authority before allocating a fresh, never-reused native owner. snapshot must be newer than the snapshot sealed into the checkpoint. The application must project its original sealed input set and loader again into training_domain; it must exactly match the saved task capsule before allocation or any model callback. After restoring the selected native bank, the session creates one controller and task use, acquires their parent once, and invokes the trusted factory exactly once as restore_model(controller, task_use, parent, exact_model_bytes). The application must return its reconstructed semantic runtime. The frozen SemanticTransitionRestoredCheckpoint exposes that same session, controller, task_use, parent, and returned model through read-only properties; none is a replacement or second acquisition. The same controlled-reader owner encloses the factory and the existing cold arena/learning-phase model callbacks. It permits authentic native tensor/content reads, not segment execution or public activation of a private candidate. After the factory returns, native closes the scope and checks the original task phase, authority and actual selected bank. Failure retains the existing abort or private-pending custody; closing a reader scope is not phase acceptance or permission to retry an unknown operation. task_use.task_objective_law() returns (scoring_law_identity, (evaluator_min_bits, evaluator_max_bits), coefficient_bits, (cost_unit_identity, cost_cap)) before any Proposal and after cold restore. Identities are 32-byte values; the evaluator endpoints are exact FP64 bit patterns and the nine coefficients are exact FP32 bit patterns. The native task freezes the proven signed return interval with outward rounding, then sets the first six and ninth coefficients to one, the actor coefficient to RN32(1/M) and the critic coefficient to RN32(1/(M*M)), where M = max(1, abs(R_min), abs(R_max)) in FP64. Training arenas must retain these same bits and structural-cost law. Reading them grants no actor eligibility, selects no future denominator, and does not certify the full physical pullback. The scoring-law identity binds immutable task content, not the authority binding that a cold arena transfer must advance. Content, goal and authority checks remain separate. The projection follows the existing immutable TaskUse read scope, including the private arena’s model-restore callback. Task identity now binds this frozen numerical law; older task identities are not accepted as restoration matches. import_task, restore_checkpoint, and admit_training_arena require the training_domain keyword. For prospective training, pass this exact primitive projection before the first Proposal, including when replay_rows is empty:
The manifest rows must be a nonempty, strictly hash-sorted, duplicate-free projection of the original sealed set, with each hash’s own four caps. All hashes are lowercase SHA256 hex strings. Caps and loader counters/token IDs use exact nonnegative u64 values (positive caps, window, block size and batch size; 1 <= mask_permille <= 1000). The nonnegative integer seed is arbitrary-width and remains lossless. The trusted application validates the manifests and their cap/hash association; native code retains that projection, not an independent authentication of external data ownership. None explicitly binds no prospective training arena and cannot acquire one through later admission. The native task/checkpoint identities retain the entire domain. Its record bound is R = min(max_records_per_input) over the same manifests. Initial and restored arenas require at most R distinct episode rows and at most R distinct anchor rows, and every mandatory group keeps its actual, nonempty, nonrepeated membership and exact denominator in [1,R]. Regular task groups use episodes; retention groups use their corresponding anchor basis. Every packed view must name one of those manifests and retain the seed, mask-policy identity, window, block size and padding/mask tokens; its source extent must satisfy 2*L <= W. The existing packed-view parser bounds branch geometry and target arrays by W. Byte caps and arena storage capacities are never substituted for R. task_use.task_training_domain() returns (domain_identity_bytes, original_domain_projection) in the same immutable read scope as task_objective_law(), or None for a task without prospective training. This is metadata custody, not a selected future denominator, actor grant or uniform physical numerical certificate. Task capsules now use version 3; version 2 capsules and older task/scoring identities cannot restore under the new binding. Restore and late arena admission require exact original-domain equality; changed manifests, caps or loader fields cannot enlarge it. task_use.policy_numerical_domains(parent, bank, consumer_stream) returns three owner-bearing FP64 DLPack capsules in parameter, full-MASK text, and baseline order, with shapes [2,P,4], [2,32,V,4], and [2,1,136,4]. Region zero is the actor label; region one is the full label. Coordinates are (M,O,U,I), with an independent ideal bound and separate ordinary/underflow errors. These are native uniform pre-draw outputs, not the selected backward’s late numerical arrays. For a prepared Proposal, pass its original step and bank 0 or 1, after bind_prepared_policy has snapshotted all nine original operands, while its original model work is still recording and before freezing the continuation. During ordinary replay/restoration, pass the acquired parent and bank=None inside the original finalization callback, after those same snapshots. The producer joins supported category/prefix alternatives using the original policy primitives, the frozen task law, and all permissible training denominators. No selected receipt or second forward/backward supplies this domain. Consume the capsules in the same retained model numerical owner and keep every view alive through final use. The declared stream is ordered after the producer without a host result read. Export aliases are not authoritative: native retains the original coherent labels and checks the exports before draw. Changed bytes are content corruption; nonfinite bounds refuse the original proposal before its first draw. Reading these outputs grants neither actor eligibility nor a physical certificate: the original model consumer must still propagate both labels, accumulate all required loss groups into the actual physical parameters, and supply both independent physical-block checks. SemanticTransitionController.admit_training_arena(task_use, *, parent, consumer_streams, snapshot, snapshot_model_state, restore_model, replay_rows, training_objective, training_domain, dependencies, publication_grants, inference_grants, training_grants, capacities, memory_bytes, max_material_bytes, max_total_material_bytes, max_evidence_bytes, refresh_snapshot) performs a cold structural transfer from the current, successfully recomputed inference or training publication. This same transition can append original episodes to an already trained session; it does not reset the learning phase or parameters. The source must hold its original operation rights and training rights, and replay_rows must retain the complete existing roster as an unchanged prefix. The original pre-action training_domain must be unchanged. The method quiesces and seals the complete native/task/model checkpoint, restores it into a private session, then binds the larger training arena and current authority there. It preserves the saved model, optimizer, RNG, cache, learning-phase history, and native records; the new authority binding and publication have new identities and digests. restore_model has the same four-argument factory contract as ordinary restore. After that factory returns, refresh_snapshot() must return a newer authorized snapshot. Only after both sessions pass revalidation does the method revoke the source task use and return the single runnable SemanticTransitionRestoredCheckpoint. A preparation failure leaves the source runnable only when it can still be revalidated; it never grants execution to the unfinished candidate. Task import requires proposal_capacity=original_limit to bind the original whole-task nonnegative U64 nominal Proposal limit. It has no omitted or None unbounded mode. A fresh Session has no task bound until import; re-import must provide the same original bound and retains its already spent places. Neither the limit nor expense is inferred from fuel or effective RNG draws. This is not a phase-program budget. The native-issued task_use.proposal_expense returns (capacity, spent). The complete nominal Proposal roster is charged before segment construction enters any producer callback, not from successful RNG draws or published results. The single-transition admission path charges its nominal Proposal before native admission as well. Failed construction, refused/skipped slots, failed submission and abandoned private candidates do not refund expense. Cold arena and phase successors share that original expense owner; ordinary restore reconstructs the exact saved value. This cold accounting adds no host/device observation inside a captured resident loop. The full checkpoint’s task capsule uses format 5 and carries the original bound, nominal expense and checkpoint-source registration, separately from paired RNG/model coordinates. Earlier formats lack some of this information and are not silently upgraded or assigned fabricated values. A coherent source/artifact migration is required before consuming checkpoints made by the earlier format. The getter alone does not prove execution or resolve an unknown admitted operation. Durable pre/post-operation persistence and full private-program recovery remain separate requirements; an older final-result Python counter is not their replacement. SemanticLearningPhaseRecipe(*, source, target, phase_index, completed_updates_index, views) describes a cold learning-phase boundary. The supported pairs are alignment to fast, fast to consolidation, and consolidation to fast; construction grants no scientific acceptance. views must cover every actual model/learning view in native (role, index) order, exactly once. Each row is (role, index, operation, source_role, source_index). Operations are preserve, zero, master-from-effective, and phase. Unused source coordinates are zero. The producer supplies the actual phase and cumulative-progress scalar indices, not assumed ordinals. phase addresses that role-23 I64 scalar. zero is restricted to optimizer, gradient, and gradient-delivery views; it cannot reset cumulative progress, masks, rates, loss scale, effective weights, replay cursor, RNG, or budgets. master-from-effective names a matching BF16 effective view and its FP32 role-21 master. Native code converts its exact finite value without rounding. Full backing allocations, padding, strides, and shared storage are retained; conflicting effects on aliases refuse the entire private candidate. controller.prepare_learning_phase_transition(task_use, *, parent, recipe, source_checkpoint, consumer_streams, snapshot, scientific_owner, source_model, execute_phase_instruction, resource_observer, feedback_interventions, learning_grant_ref, checkpoint_destination, snapshot_model_state, restore_model, snapshot_restored_model, retire_restored_model, refresh_snapshot, phase_record_owner, phase_record_limits, frozen_program_bytes) takes the exact builtin full source_checkpoint bytes already used by the original scientific owner. It quiesces the successful recompute parent and verifies the supplied native state, task capsule, original Proposal expense, Session configuration, initial prefill and complete live model against that source. The existing checkpoint decoder and task encoder enforce these bindings. Fresh authority must advance both the saved source snapshot and the current task snapshot; it does not replace the source bytes or their digest. No omitted-source or newly serialized-source fallback is provided. learning_grant_ref selects an original durable-slow-consolidation grant covering the complete dependency closure; fast-adaptation permission alone cannot authorize this durable full checkpoint. Fresh publication, data-use, retention and revocation checks remain independent of scientific acceptance. The pending owner retains the exact original source_model, callable execute_phase_instruction, named xlog.resource_observer.v1 capsule and feedback_interventions tuple before entering the original issuer/store callbacks. The tuple contains exactly two builtin bytes objects in real/control order, each b"xlog.learning-feedback-intervention.v1\0" followed by one byte: 0 for real identity, 1 for control positive zero. Both original materials enter the signed admission; native does not decode the scientific program to reconstruct them. Admission of this transport does not by itself select a private branch, apply a numerical intervention, certify observer coverage or establish final acceptance. The installed C header is pyxlog/include/xlog_resource_observer.h. Native captures the original capsule table, context and interval functions before external callbacks. Source preparation uses the original first lifecycle entry from scientific_owner.next_operation, checked by the same store’s decode_phase_instruction; no native program JSON decoder is added. Its nonce is minted after durable admission, before source work. The original serializer return is retained before validation; the interval ends once after actual source joins, before cold trace delivery. Explicit continue_preparation() may read that same finished interval without repeating the serializer, joins or finish. physical_peak_bytes is usable only with PHYSICAL_PEAK_VALID and ALLOCATOR_COMPLETED, complete requested/enabled physical coverage without an unsupported gap, valid original boundaries/nonce/ordinal and no process loss/error/overflow. It is the absolute simultaneously occupied physical GPU peak of the entire original process, including the initial occupied baseline, driver backing and physical managed residency, not a reservation or delta. Trace completion alone does not establish physical memory. Unknown physical completion retains the original pending, capsule, interval, model and returned source bytes, without release, a numeric observation or subsequent execution. The header and consumer do not supply a physical producer, whole-operation work accounting or the complete private-trajectory executor. The private candidate uses the actual source Session’s allocation provider and unchanged memory_bytes limit. Its graph and native state allocations share the source’s live resource stack and serial stream; a clone does not receive a second native allocation budget or consume another permanent stream-pool slot. Its execution-domain marker and native graph owner remain distinct. This owner is acquired only after exact durable readback of the original signed admission. This native allocation bound is not whole-phase work, model allocation or cold-continuation qualification. The trusted original scientific owner must implement accept_learning_phase(parent, recipe, source_checkpoint, original_grant, destination, snapshot, actual_final_parent, actual_final_model, verified_full_final_checkpoint, frozen_program_bytes, original_history_bytes) -> bytes. Its nonempty exact builtin bytes must contain the original frozen criteria, result, model lineage and destination acceptance. The owner must verify those inputs against its genuine scientific result; this privileged boundary is not a cryptographic issuer verifier and never interprets an arbitrary acceptance flag as that result. Native captures that original callable before issuer/store callbacks. It does not call it before private restoration or with the former six-argument preparation interface. Only the original complete measured prefix, with delivery as its next operation, permits the eleven-argument comparison. A candidate checkpoint alone is insufficient. The result or exception is retained before later validation; explicit cold continuation cannot repeat an entered comparison. Private copy/reset and folding instead consume the exact signed Admission after its original issuer pin and durable readback. The canonical restore checks the original pending, full source bytes, complete recipe and that same Admission before allocation. Native phase lineage retains those original Admission bytes, not an early assertion of scientific success. Native publication material now uses format 3; format 2 is not reinterpreted as admission-bearing material. The signed preparation outcome holds three length-prefixed materials: the unchanged full final checkpoint, its original scientific history, and the acceptance bytes. Acceptance stays outside the checkpoint whose bytes it binds. This boundary migration does not implement the still-required native schedule, whole-operation work/call measurements, private feedback binding or cold-process recovery; refusal of an incomplete prefix is not completion of that executor. Destination acceptance must be derived from the original durable-training permission’s resolved scope, never merely from the caller’s destination string, grant reference, or purpose. The original owner checks controller/tenant, repository, confidentiality, purpose, retention/deletion, cross-request reuse, and revocation restrictions for the full checkpoint and all derived weights; sealed-evaluation material is excluded. Returning serialized callback inputs does not establish this acceptance. Alignment acceptance concerns the actual jointly trained generation. Entering consolidation authorizes that phase, not its future result. Returning from consolidation requires an already accepted slow generation with absorbed residual, neutral adapters, retained accepted heads and a complete cache of that generation. Copy/reset neither performs nor proves absorption or the scientific retention and causality criteria. Restoration uses the same native Session allocation and task admission path as ordinary restore. For this transition only, the trusted factory is called once as restore_model(controller, task_use, parent, None): reconstruct from the actual private native bank, including its new phase and reset optimizer values, not from the old model bytes. snapshot_restored_model(returned_model) -> bytes must serialize that complete real model, including physical aliases, optimizer and phase state. snapshot_model_state() verifies the unchanged source model against the supplied checkpoint; it does not create a replacement source. refresh_snapshot() supplies strictly newer original authority snapshots. These callbacks execute cold, on the creator thread, outside native/task locks. Both model snapshots and native banks are checked again before any handoff. retire_restored_model(returned_model) -> None is the original model/runtime owner’s mandatory retirement callback, retained before the factory is entered. Abandonment calls it once with the exact factory model while source use is still private. It requires already canonically retired segment/evaluation consumers, then releases the original held residency and its native/model ownership. Native then positively verifies the original lease has no reader, retained step, unfinished evaluation or captured executable; abort, reader-only release and a failed identity lookup do not establish retirement. Only after known retirement does native drop its candidate owners and resume the unchanged source, without refunding expense. The full candidate checkpoint remains available as cold material. A callback failure or uncertain native release retains the same pending and candidate with unknown status; neither the callback nor a factory is repeated. If retirement is known but later source authority validation fails, resolution may finish that source validation without repeating retirement or the durable resolver. This is not fresh-process or frozen-program recovery. The source retains the native pending owner before its first serializer, scientific callback or restoration operation. During preparation its status is preparing; completion methods cannot be nested from those callbacks. Canonical restore retains the actual private Session before native phase writes, then its Controller, task use, parent and returned model as they are constructed. A callback, restore or verification exception leaves that same pending owner unknown, with the original callbacks and all constructed owners retained. It does not abort the private bank, repeat a callback, refund the shared original Proposal expense or resume the source merely because the source still matches its checkpoint. The application must recover the same object through controller.pending_learning_phase_transition() after an exception and retain its original model/runtime lifetime even when no complete candidate exists yet. checkpoint_sha256 requires a complete candidate checkpoint; no synthetic digest is provided for an incomplete preparation. The resolver never treats absence of a final checkpoint as proof that private operations did not execute. abandon_prepared() cannot resolve an unknown preparation. The original application must supply phase_record_owner before preparation, with the explicit tuple phase_record_limits=(max_record_bytes, max_total_record_bytes, max_records) from its original run document. There are no default limits. Native requires 0 < max_record_bytes <= max_total_record_bytes <= sys.maxsize and 0 < max_records <= sys.maxsize, checks the complete initial record before copying its payload, and reserves a complete bounded preparation-outcome slot before admission. These storage bounds are not checkpoint-source limits, learning rights, or whole-execution resource accounting. Native binds the original complete source checkpoint, frozen_program_bytes, full five-field recipe, original grant, destination, both original feedback intervention materials and exact storage limits to a deterministic 32-byte pending.phase_id. The same inputs identify the same attempt. frozen_program_bytes must exactly match the scientific owner’s program_bytes before the signed admission is issued; native neither synthesizes a program nor interprets a shorter program as equivalent. pending.issuer_anchor exposes only its independently generated Ed25519 public key; the private key remains in native memory and is never exported in a phase record. pending.phase_record_limits returns the exact original tuple. The same trusted store must implement:
  • pin_phase_issuer(destination, phase_id, native_pending) -> bytes: independently pin the public key read from the actual native-issued pending under the original exclusive lock, and return the exact durably read-back 32-byte public key. Enforce the original run/attempt mapping; a new namespace cannot evade an unresolved attempt.
  • resolve_phase_issuer(destination, phase_id) -> bytes | None: read that same original pin without repeating its write. A key taken only from submitted signed bytes is not an independent anchor.
  • commit_phase_record(destination, phase_id, ordinal, complete_native_bytes, sha256) -> bytes: single-attempt append-only storage and exact durable readback.
  • resolve_phase_record(destination, phase_id, ordinal, expected_sha256) -> bytes | None | False: read the original attempt, never rewrite or replay it.
The native owner captures these original callables once, before model or preparation callbacks. Before entering preparation it requires the independent issuer readback and exact readback of ordinal 0, the full signed admission. Ordinal 1 is the signed preparation outcome containing the actual complete candidate checkpoint and the scientific owner’s full history_bytes. Each record binds its phase ID, issuer, ordinal, predecessor digest, cumulative serialized bytes and immutable limits. The signature covers the complete versioned record, not merely its payload digest. Exact readback is verified against the original native key before progress advances. An issuer/store exception or nonexact readback keeps that same pending unknown. Use pending.resolve_checkpoint() for readback: native selects early preparation-record readback or attempted final-checkpoint resolution from its retained state. The application must not guess the stage from status == "unknown", try two resolvers, or select a fallback from an exception. Early readback uses only the captured issuer/record resolvers. It never repeats a pin, write, serializer, factory or restoration. Resolving admission alone cannot prove completion of preparation or resume the source. Only exact readback of the retained full preparation outcome can restore prepared; None remains unknown and early False never proves nonexecution. The public pending owner has no separate phase-record resolver. The store’s resolve_phase_record callback is unchanged. For the same live pending, pending.continue_preparation() is a separate deliberate action, not part of resolution. Native permits it only after exact readback of the original issuer pin, with no unresolved record write. Before native/model entry it writes the initial Admission only if that original write was never attempted; a confirmed Admission is not written again. Both initial preparation and continuation use the same captured inputs, callbacks, immutable limits, store and shared irreversible expense. Fresh original data-use rights are checked before the remaining Admission or any native/model preparation. Successful continuation returns the identical pending in prepared, not a runnable checkpoint; publication remains a separate action. After entry, continuation is allowed only when the canonical save returned the complete original candidate and native retained its immutable checkpoint/history outcome before a late cold verification failure. It repeats fresh source/candidate verification and issues that original outcome once, without invoking the restore factory or scientific acceptance again. An already attempted outcome write must instead use the existing readback resolver; continuation cannot sign or send a replacement. Partial restoration, unknown model execution, or failure before the complete outcome was retained stays unknown and cannot be continued by this path. This live-owner path does not implement recovery after process loss or prove absence of earlier GPU work. The canonical resolver returns these distinct result/status pairs: prepared permits a separate deliberate initial commit_checkpoint with the same original early store, or abandon_prepared before any final write attempt. Reading resolution must not choose either action. After a final write attempt, native never returns to prepared or permits another commit or explicit abandonment. If the original call raised before returning a candidate/model, those actual owners remain private in the same native pending; only durable activation returns their complete SemanticTransitionRestoredCheckpoint. Never reconstruct them from a digest or manufacture a replacement candidate. Exceptions preserve the original pending and owners, and inconsistent result/status pairs do not authorize cleanup. Final checkpoint publication must use this same original phase_record_owner. This interface supplies signed admission/outcome custody for preparation, not a complete frozen-program operation journal, cold continuation, issuer rotation, or a full private learning executor. It does not establish whole-chain freshness, executor/GPU fencing, all-branch storage capacity or whole-execution resource usage. The supported scientific acceptance callback takes exactly eleven arguments. Checking its complete scientific prefix does not implement the private executor or establish full GPU execution or cold recovery. The returned native-issued SemanticLearningPhaseTransition retains both owners privately. A fast successor resumes inference; consolidation resumes training, subject to its original rights. A later fast training segment requires current-request-fast-adaptation grants, while consolidation requires durable-slow-consolidation grants. Phase adoption does not issue those rights or make the resident fast Update a durable consolidation commit. The original Session blocks further import, execution and abort until resolution. controller.pending_learning_phase_transition() recovers that same retained object if the application loses its reference. Call pending.commit_checkpoint(durable_owner) exactly once. That original owner implements commit(destination, complete_checkpoint, sha256) -> bytes and resolve(destination, sha256) -> bytes | None | False. commit must atomically persist the full checkpoint in the accepted destination and return its exact durable readback. Only after readback and fresh revalidation does native code revoke the source task use and return the single runnable SemanticTransitionRestoredCheckpoint. Any uncertain completion leaves pending.status == "unknown", retains both owners and forbids another commit. For an attempted final publication, pending.resolve_checkpoint() only calls the same retained final resolver: None remains unknown; exact checkpoint bytes allow activation; False must prove the write did not commit and permits revalidated source continuation only if native code has never observed exact durable readback. Once either callback has returned the complete original checkpoint bytes, that knowledge is retained before fresh authority and model/native revalidation. A later False is a contradiction: it leaves the same owners unknown, cannot authorize abandonment or source continuation, and does not refund expense or repeat the write. pending.abandon_prepared() is allowed only before a commit attempt. Ordinary save/restore and resident Update preserve this phase history and cumulative progress. The embedded native publication material uses version 2; older native material is not accepted by this phase-owning contract. For actor replay, retain the complete original pre-action checkpoint with its application owner and call SemanticTransitionSession.checkpoint_referent(checkpoint) to obtain the compact native record. Store the exact record bytes as the episode’s downstream-snapshot material. inspect_checkpoint_referent(record) exposes its checkpoint SHA-256 and length, original publication, model and task identities, content and scoring identities, authority scope, the native authority_root, and saved phase. This root is the original task goal witness’s domain-separated authority identity, not a raw hash of authority bytes. Referent records use version 2; version 1 records are rejected. The record is not a replacement for the complete checkpoint. The owner must check a retrieved source with verify_checkpoint_referent(record, checkpoint); restore_checkpoint accepts referent=record and performs that same exact check before allocating a new owner. The complete checkpoint uses version 3 and the compact referent remains version 2; the embedded task capsule uses version 5 to retain the original training domain, whole-task nominal Proposal expense and checkpoint-source registration. Older complete checkpoints, task capsules and version 1 referent sources are not accepted by this contract. Initial and recovered starts keep their original initial_prefill child and initial-prefill-bound referent (phase 2). For a completed late Proposal whose completed_action_projection(consumer_streams=...).initial_prefill is None, call step.save_checkpoint(consumer_streams=..., snapshot_model_state=callback). The callback is the original model owner’s historical serializer for that same retained step; it returns exact builtin bytes, not a snapshot of the current model or a new forward. The native checkpoint contains that step’s complete pre-action publication, model allocations, random state, semantic arena and task capsule. The full task capsule retains its original segment operation; its compact record uses the distinct pre-action-bound phase 3 with the same version 2 field order and extent. It contains no initial-prefill section payload. Read step.completed_action_projection(...) again after saving. Its pre_action_checkpoint is the same action’s native-owned completed child, kind pre-action-checkpoint, ordinal zero, with the compact record as its payload. This child and initial_prefill are mutually exclusive. Keep the returned full checkpoint bytes and persist them through the application’s immutable source owner before retiring the step. Full checkpoint bytes do not enter the episode’s material closure. An actor episode’s downstream-snapshot must be exactly this child’s payload, not the first step’s referent. Rebinding one action to another checkpoint is rejected. Saving the checkpoint does not publish an actor episode or perform the actor’s same-publication atomic append. When importing actor replay, pass one resolve_checkpoint(digest_bytes, item_limit) -> bytes callback plus max_checkpoint_bytes and max_total_checkpoint_bytes to SemanticTransitionController.import_task. The native importer checks both limits before calling the resolver, validates the complete source against the record and the episode’s original predecessor, then calls the existing refresh_snapshot() callback after each external read. That fresh snapshot must advance and still permit training. The callback and retained source are cold-path responsibilities; neither runs inside the resident segment. Missing or mismatched sources and revoked rights reject actor admission. This also applies when no replay row is selected: actor rows cannot enter the training arena until every resolved checkpoint has passed the fresh training-rights check. A fresh import may also preregister the complete resolver, both finite positive byte limits and callable authority refresh before any draw. An empty checkpoint-dependent row set validates this group without reading a source or executing any callback. The same group remains available to a later arena admission; it does not grant replay, restoration or execution rights. Native retains the original registration, including its absence. Same-Session re-import, arena admission and phase restoration cannot replace or drop its resolver, change either byte limit, or add a previously absent group. These checks precede source reads and candidate-model construction; they do not rely on the caller’s retained argument dictionary. The same resolver also checks late children on non-actor rows: the resolved native section must equal that replay’s full predecessor, including its original task identity and epoch. Late source rights use the original segment operation in that verified full task capsule, not an operation inferred from the Proposal opcode; actor rows additionally require current training rights. admit_training_arena and prepare_learning_phase_transition accept the same three resolver/limit arguments and use their existing refresh_snapshot callback. SemanticTransitionSession.restore_checkpoint accepts all four arguments to verify checkpoint-dependent rows retained inside the task capsule before creating a new CUDA owner. Pass the originally registered resolver and unchanged limits; these boundaries use the same checkpoint parser. With no referenced checkpoint sources, the complete preregistered group is allowed, or all source resolver/limit arguments may be absent. Partial groups are rejected. An already registered group remains required even when no source is referenced. Task capsule format 5 records the original registration and both byte limits along with nominal Proposal expense. Cold process restoration takes the trusted original file-owner callback with exactly those saved limits, validates them before any source read or CUDA-owner construction, and retains that callback for continuation. Python callables are not serialized. Earlier task capsules are not assigned new limits or rewritten implicitly; source, wheel and persisted-artifact migration must be coherent. The outer full-checkpoint format and compact referent format do not change. The checkpoint carries content, not current permissions. With refresh_snapshot, restore obtains another advancing authority snapshot after the model factory returns and checks the reconstructed native publication before activation. A callback error or final revalidation failure poisons the new native owner and returns no reachable carrier. Checkpoint creation and restoration are cold-path operations; they are not evidence of device-resident execution, training, or update publication.

Acquired model numerical identity

The existing acquired parent’s model_context(consumer_stream=...) returns (parent_identity, descriptor_digest, semantic_root, prefix, ring, generations, contracts, capacities, numerical_state). Its final pair contains two 32-byte identities from the same acquisition: complete model memory geometry, and the device-sealed numerical state. Geometry includes allocation extents, independent storage objects, view-to-storage offsets and all model tensor layouts. The numerical identity folds that geometry with the typed-view and full-backing seals of model roles 18 through 25, including optimizer and gradient-presence state. It is independent of physical bank addresses. Cold restoration checks the stored geometry against the complete saved map and compares the device-recomputed numerical identity with the original saved value; it does not replace the saved identity to accept a different state. The numerical fold excludes the enclosing parent identity and ModelContract; the full ModelContract remains covered by the parent identity. Consequently this pair is not a replacement for model architecture, names, executable attributes, Tensor-object/version-counter classes, or their complete restoration contract. It confers no independent authority. This is a cold acquired-context projection, not a host read permitted inside the measured resident loop. For cold execution reconstruction, the same acquired parent also exposes terminal_state(consumer_stream=...): a single-consumer CUDA DLPack capsule of dtype U64, shape (1,) and element stride (1,). It contains the original acquired terminal flag (0 active, 1 requires Drain), not a value inferred from tokens or supplied by the model. It shares the native private step-input owner used by source() and model_context(); it does not acquire another parent or change the nine-field context tuple. Keep the imported tensor alive until final use, treat its bytes as read-only and name all final consumer streams when releasing the parent. The existing current-task, live-reader and consumer stream checks apply. This export enables the existing model reconstruction consumer; it does not establish complete restoration or Update execution. Initial bind_parent metadata also requires model_contract_layout, the six byte coordinates (S_begin, S_bytes, S_digest_offset, generation_offset, N_offset, identity_offset) in the single producer-owned ModelContract (role 44). S contains the complete immutable model schema; XLOG does not parse its format. The schema span and four output spans must be nonempty, disjoint and bounded by the actual record length. Digests occupy 32 raw bytes, and the generation is an eight-byte little-endian integer; no alignment is required. The layout is retained in the existing native runtime contract and publication material. For a fresh parent, task_use.initial_source_layout(prefix_capacity=..., feedback_capacity=..., max_position=..., pad_token=..., provenance_capacity_records=...) returns (source, prefix, prefill_tokens, ring_head). The source and prefix contain the eight-field rows accepted by bind_parent; prefill_tokens is the exact ordered admitted portion preceding the 32-row active ring. The task’s observed payload and source mapping, not a second caller token list, determine these rows. Missing tokens, invalid mapping, capacity and text geometry are rejected. Use ring_head in initial metadata. When prefill_tokens is nonempty, instead issue the one-use task_use.begin_initial_prefill(...) with the same geometry and the actual model_generation; its source_layout() supplies the same tuple. At the original model wrapper boundary, call stage.bind_model_content(tensors=..., model_allocations=..., consumer_stream=...) before the forward and retain its model-content witness. The first rows are the exact role 18–25 model tensors, followed by role-zero backing allocations in the same order later passed to bind_parent. After model binding and before the forward, stage.capture_tensor_content(tensors=..., consumer_stream=...) may seal the original pre-call boundary and execution inputs. Their witnesses may be verified immediately, but do not replace input registration or authorize the forward. At the model’s actual pre-hook, call stage.record_input(token_tensor=..., consumer_stream=...) on the contiguous I64 [1, len(prefill_tokens)] tensor passed to the model. Native checks its admitted token values on device and completes that check before returning. Verify the pre-call witnesses again before the first model embedding. During that one original forward, use stage.capture_tensor_content(tensors=..., consumer_stream=...) to seal each transient or autograd-saved roster at its original production point and retain the returned content witness. Before each dependent read, call witness.verify(tensors=..., consumer_stream=...) with the same original tensor storage, layout and interval and the actual dependent consumer stream; native events join it to the guard even when it differs from the prefill stream. The model witness verifies the complete pre-forward model roster; no witness may establish a new baseline after output recording. Execute the original model prefill once on that stream; at the final post-detach hook, call stage.record_outputs(tensors=..., consumer_stream=...) with all original cache, recurrent and accumulator tensors (roles 4–13). It returns a SemanticInitialPrefillReceipt. Pass that exact receipt as prefill_receipt=... to controller.bind_parent for the same task and Session, along with the original tensor and allocation rosters. The receipt is consumed once; a changed source, generation, tensor owner or sealed content is rejected. The native receipt authenticates the tensors observed at these wrapper hooks; it does not independently execute or audit the external model forward. A nonempty prefix cannot bind without it. An empty prefix needs no receipt. The one-use stage retires after the completed parent copy and publication join, but the Session retains the original model and transient seals for verification through the dependent backward reads. Retain the Python witnesses, stage, original producers and saved autograd graph until creator-thread cleanup. After sealing the numerical model ranges, native initialization computes S_digest = SHA256(S) and identity = SHA256(b"xlog.semantic.model-identity.v1\0" + S_digest + LE64(actual_model_generation) + N). It fills those four output fields before sealing the complete ModelContract and parent. There is no separate record serial. The sampler’s u32 generation must equal the actual native model generation; conversion cannot truncate or wrap. Each publication bank owns a separate record-44 destination. The existing prepared-input producer copies the acquired record into private fixed storage, which prepared_record exports and the retained content guard checks. Ordinary model-preserving continuations copy the original record into the unpublished bank before the same finalizer and sole publication. Cold restore instead compares all saved fields against the device-recomputed values without rewriting them. The separate Update execution, producer restoration and subsequent forward still require their connected runtime implementation and full GPU evidence.

Original model work recording

controller.build_segment requires the frozen tensor_content_capacity, model_work_capacity, segment_capacity_bytes, and other_external_cuda_bytes alongside the transition schedule and producer. The last two values bound the producer’s CUDA slab and all later external CUDA allocation classes, respectively. Native compares their combined declared claim with free CUDA memory before the first segment allocation, then retains the logical reservation through segment retirement. The snapshot is a necessary cold refusal check. The instantiated graph is uploaded and completion-fenced on the original stream before installation; upload failure refuses the segment before draw. CUDA may later remap graph memory, so this does not establish a permanent graph-memory upper bound, bound external-library allocations, or prove that the full peak fits. Numerical domain payload and scratch are not included in this interface yet, so this claim alone does not admit a complete numerical execution. The prepared producer records logical work on its original SemanticPreparedStep. After the original memory_scope.__enter__ succeeds and before graph capture, the builder calls prepared.prepare_unpublished_successors() exactly once on the creator thread. It must return None and create every unpublished Update successor and its retained learning inputs inside that same private allocation scope; preparing them before the scope would put their backing outside the required slab. A callback failure exits the scope through the original cleanup path and aborts the segment; the builder never retries it. Call step.reserve_tensor_content(tensor_content_capacity) and step.reserve_model_work(model_work_capacity) during prepare_segment, before capture. Each positive capacity must exactly match its frozen segment value. Native allocates both buffers from the retained segment claim, not separate late reservations. The work capacity bounds event occurrences, including every saved tensor; native does not run the model to discover a capacity. During the original enqueue_step, call step.record_model_work(kind, dimensions) for each executed operator occurrence. kind and every dimension must be exact unsigned integers, excluding booleans. The geometry contains at most eight factors; their checked product is the number of units. An empty geometry describes one scalar unit; any zero factor gives zero. For each original autograd save clone, call step.record_saved_tensor_work(witness) with that occurrence’s original native content witness. Native derives shape and element width from its retained tensors. Different save occurrences count independently even when storage is shared; the same witness/tensor occurrence cannot be charged twice. Kind 15 is reserved for this witness-bound method and cannot be submitted as an ordinary operation. Views are not copies; masks do not discount executed dense geometry. Do not count an opaque operation and its assumed internal decomposition together. When enqueue_step returns, the native transition freezes the roster and checks its exact, nonzero model-work sum. An empty or entirely zero-work recording is not a valid model body. The immutable geometry is uploaded after capture, before submission, and consumed once inside the original executed IF body. Skipped bodies incur no model charge; later numerical refusal does not erase the forward. Counter overflow returns work_counter_overflow without publishing a successor. The original step retains the roster and allocation through completion and retirement. This port excludes cold preparation and later Update. This checked model-work sum is not the full attempt bound. Complete native-event accounting, its proved total bound, the full return consumer, and path-specific GPU evidence remain unfinished. The model receiver does not establish those gates.

Completed-step model inputs

Before retiring a completed segment, call its original step’s completed_model_binding(consumer_streams=[...]) to authenticate that step’s retained model inputs. With the semantic-policy feature enabled, the result is (predecessor_identity, successor_identity_or_None, model_generation, model_geometry_digest, model_numerical_digest, carrier_or_None). Each identity has the four fields returned by PublishedParent.identity(). The generation and both 32-byte digests belong to the acquired predecessor, not the final segment bank. For a successfully published Proposal, carrier_or_None is a frozen SemanticCompletedModelCarrier. Its numerical_realization, logits, and random_state properties are three separate immutable (native_owner_identity, canonical_bytes) pairs. The first pair binds the completed step and action realization to the CUDA execution and typed launch identities that produced it. The second carries the retained conditional edit FP32 distributions and their placement from that execution; it never recomputes logits or runs a second forward. The third carries the invocation and training RNG header followed by the ordered native draw-receipt records. The carrier is a cold observation owned by the original completed step. It retains that step’s native owners only until retirement; it neither reacquires a publication nor grants execution or publication authority. Native code validates the same completed binding before and after serializing the carrier. Recompute, Update, Drain, and refused steps return None for the carrier; a completed numerical refusal also has no successor. A skipped step, an uncompleted segment, or uncertain execution cannot return a binding. When the semantic-policy feature is disabled, the method raises its typed feature-required error instead of returning a six-field tuple with a fabricated None carrier.

Original Update measurements

Before retirement, read step.update_measurements on the original completed step, between the original and repeated completed_model_binding checks. It returns a host-only dictionary for both accepted and refused Updates. A completed non-Update returns None; skipped, uncompleted, unknown, retired, or custody-invalid steps raise an error. This observation joins the original registered consumer streams and never reacquires a publication, runs a forward, recomputes a metric, or grants candidate execution authority. The dictionary contains schema=1, predecessor, successor, bank, the acquired model_generation, model_geometry_digest, model_numerical_digest, the original native transition_status, selection, canaries, forward_receipts, and canary_refusal. The identities and acquired model fields must equal the same completed binding; a refused Update has no successor. selection retains the native status, roster/selected-row coordinates, identities, training RNG and complete original replay-origin record. canaries is the original five-check tuple, in native kind order: symbolic utility, retained behavior, logit drift, goal chain, resource limits. Each check retains evaluator_abi, kind, row_ordinal, identity, row_identity, row_content_identity, task_identity, exact lower_bound_bits and upper_bound_bits, integer memory_used, memory_limit, work_used, work_limit, obligation_positions, protected_member_offset, protected_member_count, and protected_positions. These are original frozen inputs and original device counters, not phase acceptance thresholds. Each check’s device-authored availability is "not_executed", "incomplete", or "obtained". measurement_bits is an unsigned integer containing the original FP64 bits only for "obtained"; otherwise it is None, never an initial zero or a partial accumulator. An obtained measurement survives bounds, protected-retention, goal-chain, and resource refusals. The check’s independent native reason is 0 for admissible, 1 non-finite, 2 outside bounds, 3 memory limit, 4 work limit, 5 incomplete operands, 6 protected retention lost, 7 invalid goal witness, 8 work overflow, or 9 generation mismatch. canary_refusal separately retains the prioritized native reason, identities, bounds and counters, with the same availability and optional measurement_bits as its original check. It does not expose a partial accumulator as a complete measurement. transition_status and the original execute_segment refusal remain independent of individual availability; the existing segment-row format is unchanged. forward_receipts contains the original baseline and candidate records in that order. Each has role (0 or 1), the same availability vocabulary, and material (None if no complete receipt was obtained). Obtained material contains role, the actual receipt’s generation, work_sequence, scalar_type, dimensions, strides_bytes, model_geometry_digest, model_numerical_digest, logits_digest, identity, and canonical_bytes. Native code validates the original logits owner/layout, receipt SHA-256, baseline binding, receipt order, and published candidate binding when present. The candidate generation is read from its receipt, not reconstructed as acquired generation plus one. Canonical bytes are the exact 232-byte little-endian private receipt: ten scalar words, three stride words, then four 32-byte digests (geometry, numerics, logits, identity). The last digest seals the preceding 200 bytes. The original pointer word is inert evidence, not a CUDA alias or a replay capability. All returned dictionaries, tuples and bytes are host-owned and may outlive retirement; the getter itself may not. This port does not change the six native execute_segment row fields, objective reductions, canary algorithms, scientific acceptance, or the lifetime required for an unpublished multi-Update candidate. controller.resolve_completed_segment(task_use, *, refresh_snapshot) retrieves the original final parent and complete six-field ordered rows retained by the same execute_segment. Native retains them before refreshing authority for handoff. If that callback fails, the task remains in a private pending-result phase: it cannot submit another segment, publish, save an ordinary checkpoint, or retire its producers. execute_segment raises the native SemanticCompletedSegmentPending exception with the original exception as its cause. The owning Runtime must retain its producers and result-collection state instead of using its permanent abort path for this exception. Constructing that exception in Python grants no result or execution rights. A further authority callback or snapshot failure while resolving that same pending handoff raises the same native exception and leaves the original task pending. Wrong-owner, missing-roster and cold getter failures do not gain this continuation signal. Resolution refreshes original rights and returns those same owners; it does not launch, collect another device outcome, invoke model producers, reserve nominal Proposals, or reconstruct missing results. Returned refusal dictionaries are checked against the retained native evidence again. Only successful reconciliation of the entire original roster, work evidence, publication lineage and reader release establishes known completion. An integrity failure after a completed CUDA stream remains unknown and cannot use this resolver. These retained in-process results are not durable admission, private-program recovery, scientific acceptance, or process-restart evidence. Use the same step’s record(44, 0, consumer_stream=...), tensor(...) and tensor_allocation(...) exports to serialize the original model contract and complete model backings. Their private storage is preallocated before capture and populated by that step’s recorded acquisition. The observation does not acquire an old publication bank or allocate another history of model states. Treat every export as read-only, retain it until final use, and include every original consumer stream in the binding call. Recheck the binding after serialization to authenticate the same original seals. Input corruption or unknown completion prevents further use of the session. This method joins consumers and reads device metadata on the host. Call it only outside capture and the measured resident loop, before retire_segment. It does not finish policy use or retire the step. Its returned identities are not live readers, complete predecessor/successor replay materials, or authorization for a new model invocation.

Completed-step action projection

After capture finishes and before the first execute_segment, every original Proposal step exposes completed_material_capacity. It returns ((count, largest_bytes, total_bytes), (numerical_bytes, logits_bytes, random_bytes), attempt_receipt_bytes, publication_evidence_bytes), with every size in bytes. The first triple conservatively covers all projection children, the three raw model carriers, every raw feedback, statement, and provenance record, the complete predecessor publication, and its native reconstruction provenance. It counts possible materials before digest deduplication, NULL choices, lane refusals, or winner selection, including every possible structural delta and its evidence, task ground, scoring law, edit solution, and physical certificate. Both frozen input banks and their original recorded policies are inspected; future values, selected results, and device tensor bytes are not read. The carrier lengths use the order numerical realization, structural logits, random state. An external codec must additionally bound its real wrappers and model-owned materials. The AttemptReceipt child is already in the first triple; its separate size bounds consumers that retain that child. Replay-kit evidence must instead use full publication replay evidence, reported separately, using all six native records’ complete allocation capacities. Neither evidence value substitutes for the other. Combine this native capacity with all external materials, record lines, views, anchors, and indexes under the same writer caps before allowing any submit. This getter is unavailable during capture or after submission and does not execute a forward, authorize replay, or enable an actor.

Replay materials from completed proposals

Use step.completed_replay_materials(consumer_streams=[...]) when you need to replay an earlier published Proposal after later steps have reused its device banks. Call it outside capture and the measured resident loop, after known completion and before retiring the original segment:
The tuple has the same format as Controller.export_replay_materials for held adjacent readers. These identities and bytes belong to this exact step, not the first parent or final successor of the segment. Native custody copies the full semantic arena and the selected model backings before reuse. It retains the successor’s original records inside its publisher before publication. The full allocation roster is reserved before capture and its byte copies are charged as native work. An unpublished refusal returns None; unknown completion does not return material. A scheduled Proposal that actually publishes another transition must be handled using its observed transition, not exported as a Proposal. For every saved published Proposal, retain all five tuple parts using your existing replay format. Pass full_p, native_provenance, and publication_evidence to the corresponding replay roots. Neither the compact logical-state child nor an AttemptReceipt replaces any of them. This export does not issue a use grant, invoke a model, or retire the original step. Use SemanticTransitionSession.inspect_publication_material(full_p) to inspect the same complete native material without creating a CUDA session or introducing another parser. The host-only dictionary has schema=1, the original publication=(instance, word, logical_digest, state_digest), semantic_root, model_generation, model_geometry_digest, model_numerical_digest, neural_generation, cache_generation, prefix_extent, ring_head, and prefix_identity. Identities are exact 32-byte values, not hexadecimal strings. prefix contains exactly the committed extent and source the 32 physical ring slots. Each row is (token, logical_position, kind, provenance, valid, committed, recomputed, provenance_record). token_provenance preserves the complete native ledger in original order. Its dictionaries retain source_slot, logical_position, token, base_word, proposal, ordinal, authority_generation, action_receipt_digest, origin_logical_digest, authority_closure_digest, and record_digest. Filled rows must resolve to their original token and position. SOURCE and GENERATED remain distinct; generated origins are not rewritten as admitted observations. cache is the complete original role/index roster for cache and running-state roles 4–13. Each dictionary retains role, index, capacity_bytes, logical_begin, logical_end, identity, layout, and logical_bytes. layout is (role, index, element_bytes, scalar_type, rank, logical_axis, dimensions, strides_bytes), with both final fields four-element tuples. logical_bytes traverses logical coordinates, excluding padding and unused capacity, and is checked against the original CUDA tensor seal. The model owner must compare its actual source/cache operands and entry-boundary generations against this projection through its existing model codec. Historical origin records still require the original execution closure and rights checks; this inspection neither proves past publication nor enables an actor. Initial-prefill evidence remains separate and must not be discarded or fabricated. After known completion, the existing tensor and tensor_allocation getters read that step’s private acquired model backings, not the reused publication banks. They preserve the original allocation-sharing partition, complete backing capacity, typed layout, and byte offsets. The supplied bank must be the branch that actually executed. Prefix and cache aliases use the same custody in cold exports and input guards. During original construction these getters still expose the declared resident bank operands for capture. Content verification continues to require the exact original tensor owners, layouts, and intervals; for authentic native model aliases it checks the saved bytes against the original publication seals. Separately owned operands continue to verify their own retained bytes. No new baseline is sealed during cold observation. Original input custody belongs to every prepared Proposal, Recompute, and Update, not just exported Proposals. Later transitions cannot replace an earlier step’s completed binding or Update measurement basis. Admission reserves all of these step owners before capture, including their full backing and semantic arena copies; actual device copies count as native work. For a successfully published Proposal, call completed_action_projection(consumer_streams=[...]) before consuming the original policy tape or retiring the segment. Completion moves the executed policy and receipts into that tape; the projection follows its original publication bank, not the independently advancing model-weight bank. This export authenticates the retained completed schedule; it does not reopen the construction-only transition API or permit another capture. The typed cold projection returns the authenticated predecessor and successor, logical-state materials, proposal/stream/family/action-law/model coordinates, native winner and pre-draw actor eligibility, exact signed-return bits, the device-sealed batch receipt and root, the retained task-result material, and distinct action RNG base and successor materials. It exposes the native predecessor semantic-root digest but does not turn an application’s source-text identity into that digest. schema_generation contains the exact retained admission-schema preimage; its SHA-256 is the native schema-generation identity. action_law is the generated catalogue’s canonical declaration bytes with its SHA-256 catalogue identity. edit_codebook is the exact native codebook byte image used by this admission and completed action, with its admission-derived binding identity. roster is the canonical little-endian encoding of the 136 actual components, including their admission-derived cardinalities and offsets; its bytes bind the codebook identity and hash to the roster identity. All four use the same byte-backed material interface as the other projection children (identity, bytes, and bytes_sha256). The codebook image is opaque: consumers bind its bytes and identity but must not decode private device offsets from it. Neither material claims that target, behavior, CDF, Philox, and hard-decode laws have separate native identities. world_root is the canonical native root material decoded from this original step’s private pre-mutation semantic arena. Every saved published Proposal has its actual acquired root, including after Update, Recompute, and later requests; its identity equals predecessor_semantic_root_digest. The same decoder produces the graph in completed_replay_materials’ full parent. This is not the initial admission’s theory relabeled as a later predecessor. predecessor_semantic_root_digest always names the root in the authenticated predecessor publication header; it is distinct from the logical-state digest and from an application’s source-text theory identity. initial_prefill is a completed-action child material for the original cold parent’s nonempty model prefix. It is None for an empty prefix, a later predecessor, or a restored parent; none of those cases gains an inferred prefill witness. The native owner retains the prefill stage consumed by bind_parent, including its original model and output tensor seals. The material is bound to this completed action’s owner and authenticates the same predecessor, semantic-root digest, model generation, task use, and three task-content identities. Its byte-backed interface is identity, bytes, and bytes_sha256; consumers must retain all three and reference the material from their durable episode record, not merely check it during live export. The child envelope uses XLOG-COMPLETED-ACTION-CHILD\0, version 1, owner identity, the length-prefixed initial-prefill kind, ordinal zero, and a length-prefixed payload. The payload is little-endian XLOG-INITIAL-PREFILL\0, version 1, the 104-byte predecessor publication identity (instance, word, logical digest, state digest), semantic-root digest, model generation, task-use identity and epoch, and query, theory-program, and result content identities. It then contains a token count and that many u64 prefill tokens, a mapping count and that many (UTF-8 source identity length, source identity bytes, token offset, logical position) rows, followed by two tensor-seal groups: original model content and original output content. Each group has a count and (role, index, four u64 digest words) rows. Mapping rows are ordered by logical position, and the native parent join checks the full source-provenance ledger in addition to the original device input and tensor owners. This is a cold export; it adds no host transfer to the resident step. estimator_bound is the original pre-draw physical certificate for an actor-eligible Proposal with a physical parameter roster. It uses the same immutable identity, bytes, and bytes_sha256 interface. Non-actor actions and models without that roster return None; refused or skipped Proposals and failed preflight do not produce a projection. A learned-lane refusal after a successful preflight does not remove this certificate. The native owner reads the retained FP64 snapshot, never the mutable producer tensor or a selected backward bound. The existing device preflight checks all six values per block for finiteness and nonnegativity and checks upward-rounded 100 * ordinary <= budget separately for actor and full-step areas. A zero budget requires zero ordinary error; the separate underflow value is not forced to zero. Export neither changes that law nor enables actor eligibility. The completed-action child envelope has kind estimator-bound and ordinal zero. Its native identity binds the original completed-action owner and that kind; bytes_sha256 independently hashes the complete envelope. The payload is little-endian XLOG-ESTIMATOR-BOUND\0, followed by u64 format version 1, preflight-law version 1, multiplier 100, and original publication bank. Next come the 104-byte predecessor publication identity, model generation (u64), 32-byte model geometry, model numerics, and native training-domain identities; then scoring-law and batch-receipt references, each consisting of its 32-byte native identity and 32-byte SHA-256 of its exact bytes. The batch receipt binds the original task content and successful completed-action preflight. The next two 32-byte fields are the original model-contract schema digest and model identity. Six u64 model-contract layout fields follow in this order: schema begin, schema byte length, schema-digest offset, generation offset, numerical-digest offset, and identity offset. Two length-prefixed byte strings contain the complete original model-contract record and native runtime-contract profile. Finally, a u64 physical-block count precedes rows in the original sorted physical-parameter order. Each row contains a u64 UTF-8 name length, name bytes, and six original FP64 bit patterns encoded as u64: actor budget, ordinary error, underflow, then full-step budget, ordinary error, underflow. The block count and names are checked against the same frozen native model roster, including physical parameters but not buffers. Consumers retain the whole certificate in their episode or replay material, not just its identity. Its complete encoded size is 632 + M + P + 56*N + sum(name_utf8_bytes), where M and P are the original model-contract and runtime-profile byte lengths and N is the physical-parameter count. This is the certificate size, not a capacity bound for all other Proposal materials. theory_deltas contains one entry for each actual native insertion attempt, ordered by learned lane then edit slot. No-edit choices and actions stopped before insertion produce no entry. A duplicate graph support attachment is still an attempted edit. Each entry has delta (canonical owner-bound decoded insertion bytes), verdict (accepted or rejected for the whole lane), and evidence (the native hard-decode, lane-admission, and selected component receipts). lane and slot retain the native source coordinates. A losing admitted lane remains accepted; a later lane refusal does not erase an insertion attempt. For an editable positive binary program, INSERT_RULE decodes one range-restricted rule with exactly two binary body atoms. Its kind="rule" delta binds all ten rule fields and any preceding rule edit in that lane; theory_generation binds the admitted program and preceding rule. The task’s three queries are evaluated against the resulting program closure on device. Graph support insertion is not offered in this mode because it does not alter that program closure. For a graph-support edit, kind="fact" and theory_generation names the acquired predecessor semantic-root digest. Neither mode currently emits a forest delta. The invocation’s result also contains program_rules: two lanes of two optional flat ten-field rule tuples, including attempted rules in a lane rejected by a hard constraint. These are distinct from graph-support edit results. Its truths and correct entries each contain three ordered triples: base, first candidate, and second candidate, with all three task queries in every triple. The current semantic engine generation is the catalogue generation, so engine_generation returns the same material as action_law. observation returns the same native four-valued task-result material as result_material; its bytes bind the observer query and program identities, actual result bytes, and three expected truth values. These aliases do not mint new identities. The projection’s task_ground is the one typed semantic ground retained by the same completed Proposal owner. It binds acquired base B, ordered query identity Q, expected observer results, the three actual base/no-op truth codes, and the bound goal-authority closure. Its two ordered lane outcomes are tagged EXECUTED, with the three actual lane truth codes, or REFUSED, with the canonical native refusal kind. task_ground.facts retains all three ordered truth-code vectors together with native agreement, eligibility, value, and work facts; a refused lane’s terminal tag remains authoritative. Candidate program-closure exhaustion is typed as fact-capacity or fuel-exhausted; it does not discard the completed base or turn a native infrastructure failure into a task refusal. task_ground.scoring_law is a canonical native material bound to this completed owner, original task identity, and versioned structural-cost law. Its six scoring_weights are, in order, correct-query, all-correct, measured-work, improvement, refusal, and spent-work coefficients. For each candidate, measured work is the count of actual edit commands plus added supports (new materialized facts for an editable program) plus defined-truth changes; candidate value is correct_weight * correct_count + all_correct_weight * solves_expected_truths - work_weight * measured_work. Eligible candidates are selected by ordered task-priority progress, then exact query agreement, then lower measured work, retaining the earlier slot on ties. Value measures the return; it does not override this selection order. The final return is improvement_weight * (selected_value - base_value) - refusal_weight * refused_lanes - spent_weight * spent_work, where spent work is the executed query count plus edit-command and defined-truth-change counts for discarded lanes. return_bound is the admitted symmetric signed bound for this task return, using the resident fact capacity for an editable program instead of the smaller graph-support cap. It is not a bound for an external training objective. Cold projection checks these equations against the retained native facts and work counters before exposing the material. controller.structural_cost_descriptor returns (unit_identity_bytes, cap) before import_task: the native versioned structural-work law for this Session’s graph-only or editable-program mode. The graph-only cap is 6; the editable-program cap is 4101, including at most 4096 new materialized facts. The unit is independent of task identity and sampled outcomes. Use unit_identity_bytes.hex() and cap as the objective’s cost pair. Native cold binding rejects any different pair for the actual bound task. The completed task_ground.structural_cost_descriptor exposes the same pair for checking the terminal ground; slot-zero work is zero, while candidate raw work is exactly edit_commands + added_supports + defined_truth_changes. Learned attempted requests are not duplicated in the ground. Read them from the same projection’s lane decoded_text_actions and decoded_edit_actions. The optional edit_solution is present only for the lowest-numbered admitted lane with a nonempty effective support delta that satisfies the complete bound goal while the base does not. It binds that lane’s complete original 68-component prefix, both native decode receipts, sealed resulting-state receipt, actual truths, catalogue, and device-forward component materials. Its half-open edit component interval is 32..68 for lane 1 or 100..136 for lane 2; policy winner and score do not determine membership. For an authenticated episode row in the supervised-edit objective, temporal_update_vjp(...) derives that same minimal decisive lane on device from the original completed Proposal state and bound goal closure. Its direct edit cotangent covers only the selected lane’s 36 edit components. Text components and the other lane receive zero direct edit cotangent; the native backward still walks the complete original edit recurrence and original categorical choices, masks, catalogue, and decode-bound tape. It does not resample, consume a historical score array, or invoke the model a second time. The projection also contains the admitted slot-zero record, learned lane slots 1 and 2 with their native admission or refusal, exact non-NULL text actions and hard-decoded edits, and exactly 136 ordered component objects. Each component retains its selected category, legal and active counts, exact 192-bit probability and cumulative-mass values, CDF interval, draw, rank, Philox address, and byte-backed final-mask, PWL-cell, active-set, selected-score VJP, component-receipt, and applicable hard-decode receipt materials. PWL-cell and VJP bytes are original device-forward records, not host reconstructions. The successor publication’s native AttemptReceipt remains a separate evidence material. The method joins declared consumers and reads retained device storage outside the resident loop. Its opaque owner is the original retained completed-step carrier; typed children do not reinterpret that private ABI. It never runs a second forward or reacquires a current publication. Native rechecks the same completed binding after serialization; a changed owner, non-Proposal step, refusal, unknown completion, or malformed retained law closes the call.

Completed-step raw feedback

Call the same retained step’s completed_feedback_records(consumer_streams=[...]) outside capture and the measured resident loop. It returns (predecessor_identity, successor_identity_or_None, records), using the same identity and completion rules as completed_model_binding. The native input guard brackets the readback and joins the declared consumers. The call neither acquires another bank nor runs a new encoder or semantic query. Records are ordered by (role, index). Each tuple contains (role, index, generation, capacity_bytes, logical_begin, logical_end, native_identity, raw_bytes). Role 15 contains the complete fixed-capacity raw feedback slots, including validity and native query receipts; its logical extent retains the original occupied interval. Role 16 contains each original statement’s bytes. Role 17 contains the original provenance records. These records are copied by the original device acquisition into preallocated private step storage and remain available until step retirement, independently of publication-bank reuse. Keep each native identity: it is the original role/index/extent/content seal, not SHA-256 of raw_bytes alone. A canonical carrier stores the byte hash separately. Do not replace the model identity from record 44 with a new hash of these records. The six tensors returned by feedback() are separate encoded features and projected service values, not this raw feedback material. This raw-feedback export does not supply the canonical logits or complete RNG state carried by completed_model_binding, does not supply complete predecessor/successor replay material, and grants no new execution authority.

Package Details

API Reference

LogicProgram (Deterministic)

Compiles and runs an ordinary (non-probabilistic) Datalog program on the GPU and returns each query’s answer as DLPack columns (one tensor per column).

Supplying Input Relations (DLPack)

CompiledLogicProgram.evaluate(dlpack_inputs=...) accepts a dict mapping relation name to a sequence of DLPack columns.

Persistent Named Relations (DLPack)

For repeated evaluation with long-lived GPU relations, create a persistent session instead of re-supplying dlpack_inputs on every call.
The persistent session path is additive:
  • evaluate(dlpack_inputs=...) remains the stateless one-shot API
  • session() exposes a mutable named relation store with schema-checked DLPack import/export

Persistent Relation Deltas

A delta is an incremental change to a relation — rows to add or remove — applied without recomputing the whole program. Persistent sessions accept DLPack-backed deltas so a caller can update its data in a loop. insert_relation(...), delete_relation(...), and apply_relation_delta(...) update the session’s stored relations through the runtime’s incremental-recompute path (RelationDelta / apply_deltas_and_recompute). Updates that only insert rows into a mutually-recursive relation group (a strongly connected component, or SCC) reuse previously computed output where the plan allows. Updates that delete rows clear and recompute the affected groups so results stay correct.
The delta stats dictionary contains changed_relations, insert_rows, delete_rows, affected_sccs, recomputed_sccs, incremental_sccs, input_delta_count, coalesced_insert_rows, coalesced_delete_rows, and canceled_rows. Delta debug output also includes changed_relation_names, equivalent_to_full_recompute, debug_trace, and nested planner_telemetry. Planner telemetry reports cache_reused, fallback_decision, affected/recomputed/incremental SCC counts, estimated_delta_speedup, measured_delta_speedup, and planner_advice. equivalent_to_full_recompute is None unless the caller opts into check_equivalence=True. Batch updates merge repeated changes to the same relation before the runtime recomputes, using device-resident set operations. Update dictionaries reject unknown keys before any relation is changed. A batch whose inserts and deletes cancel completely is a semantic no-op: changed_relations is 0, no runtime version or callback generation advances, and no callback fires for the canceled relation. Callback and diagnostic code must not copy relation rows down to the host. If a delta operation fails before commit but after preparation takes ownership of cached derived state, the authoritative relation rows and evidence remain unchanged, but XLOG discards the derived cache and retained runtime. The next evaluate() rebuilds them. This makes failure recovery safe at the cost of losing cache-hit and incremental-planner continuity for that attempt. Calling put_relation, remove_relation, or clear_relations directly invalidates the cached runtime store. The next evaluate() then does a full plan run before later deltas can reuse it. Persistent sessions keep their runtime executor across evaluate() and delta recompute calls, so persistent hash indexes can be reused through public pyxlog mutation loops. session.join_index_cache_stats() returns the retained executor’s lookups, hits, misses, builds, invalidation counters, background-build counters, entries, and total_bytes. session.wcoj_dispatch_stats() separates successful specialized dispatches from actual fallbacks. Its wcoj_fallback object reports total and the chain, dedicated_multiway, free_join, planned_hash, factorized_delta, and groupby_fusion routes. wcoj_error_decline_count is narrower: it counts pipeline errors that declined to a fallback, not ordinary eligibility or cost decisions.

Relation Change Callbacks

Persistent sessions expose opt-in metadata callbacks for relation delta commits:
Callbacks fire only after a delta commit succeeds. A failed or rolled-back delta does not invoke registered callbacks. The callback payload is a metadata-only dictionary with relation, generation, input_delta_count, insert_rows, delete_rows, has_deletes, coalesced_insert_rows, coalesced_delete_rows, canceled_rows, affected_sccs, recomputed_sccs, incremental_sccs, and nested telemetry. Callbacks are invoked synchronously while the pyxlog method holds the Python GIL. Registration order is callback order, and relation events are emitted in the caller’s update order after duplicate relation names are coalesced. This ordering is deterministic: a regression fixture confirms that 100 repeated runs produce identical callback sequences. Relations whose net batch update cancels completely are omitted from mixed-batch callback sequences and do not consume a generation number. Building a callback payload does not export DLPack tensors or download relation data rows; use explicit evaluate() or export_relation() when you actually need the rows materialized.

Rule, proof, temporal, and relation provenance

Compiled logic/probabilistic programs and sessions can report where their results came from (provenance):
rule_provenance() returns stable rule_id, source_kind, generation_trace_hash, support_relation_ids, and counterexample_relation_ids fields. proof_traces() returns each query’s answer relation, deriving rule ids, source facts, and rejected alternatives. Temporal stream loads can keep provenance metadata next to the relation:
The temporal metadata shape preserves timestamp_column, dataset_id, row_hashes, field_hashes, uncertainty, stream_id, source, order_column, process_boundary, and temporal_order. Temporal metadata is a Python helper facility; it is separate from the native whole-fact evidence API below. Native relation evidence binds ordered semantic roles and provenance records to complete facts of any positive arity. The role order must match the compiled predicate argument order, and each fact is identified by all of its cells:
Breaking in 0.12.0. Five relation APIs changed shape, so code written against 0.11.0 needs edits:
  • put_relation_with_provenance is now native and requires keyword-only roles= and facts=. The old source_path, source_hash, row_hashes, accepted_count, and decision_counts keyword arguments are gone, and the call returns a native snapshot instead of that flat sidecar dictionary.
  • evidence() returns {program_hash, relations} and raises KeyError for a name it does not hold, instead of returning {}.
  • relation(name) returns a frozen native RelationEvidence and raises KeyError for an unstored relation, instead of a wrapper whose provenance() returned {}.
  • RelationEvidence is an immutable native class. The old RelationEvidence(session, name) constructor no longer exists.
  • apply_relation_delta_batch and apply_relation_delta_debug reject unknown keys in an update dictionary that they previously ignored. Both route through the same parser, so both raise ValueError where 0.11.0 silently dropped the extra key.
See Migrating from Python-side relation evidence for the field-by-field mapping.
Each role input requires name; optional sort and type fields, when present, must match the compiled schema. Returned snapshots resolve all three fields. Source-named predicate arguments require their compiled names. Positional arguments accept application-defined role names on the first metadata-bearing load, then enforce that role contract on later metadata-bearing replacements and manifest imports. A plain put_relation, a manifest import with metadata_present=False, remove_relation, or clear_relations removes that positional role contract. A later metadata-bearing load may then register new positional names. Source-named arguments always remain bound to their compiled names. A fact supplies exactly one of tuple or cells. tuple is the convenient Python representation. cells is a sequence of exact {"type": ..., "hex": ...} values for bit-preserving values such as NaNs and signed zero. Provenance records may contain source, document, span, content_hash, kind, and polarity; at least one field must be non-null. Two different records for the same complete tuple remain distinct. Replacement rows retain their stored multiplicity, so row_count includes duplicate rows. Evidence is keyed by the distinct complete typed tuple rather than by row offset: duplicate stored rows share one fact entry, repeated fact entries merge their records, exact duplicate records collapse, and facts and records are returned in deterministic canonical order. Evidence follows the native relation lifecycle atomically:
  • put_relation_with_provenance replaces both rows and evidence;
  • plain put_relation replaces the rows and clears old evidence and any positional role contract;
  • insert_relation(..., facts=...) and apply_relation_delta(..., insert_facts=...) add evidence for inserted facts;
  • batch and debug updates accept insert_facts in each update dictionary;
  • deleting a complete fact deletes its evidence, and coalesced or canceled batch updates cannot leave stale evidence; and
  • metadata-free manifest replacement, remove_relation, and clear_relations remove the matching evidence and positional role contract.
Insert evidence has stricter preconditions than a metadata-free insert. facts or insert_facts requires insert columns for the same update, a positive-arity relation, and a role contract previously registered by a metadata-bearing replacement or manifest import. Every annotated fact must occur in that specific insert buffer; being present only in the session’s existing rows is not enough. Passing an empty facts=[] or insert_facts=[] still opts into these contract checks, although it performs no membership-mask transfer. Duplicate or already stored inserted rows may add distinct provenance records when the annotated fact is present in the insert buffer. All role, type, arity, evidence-tuple membership, and batch validation completes before mutation. A validation or pre-commit preparation failure leaves relation rows, evidence, delta statistics, callback generations, and callbacks unchanged. Nullary relations support plain put_relation, metadata-free inserts, deletes, deltas, evaluation, relation, and evidence. They reject put_relation_with_provenance, every put_relation_from_manifest and export_relation_with_provenance call, and any insert, combined delta, batch, or debug update that supplies facts or insert_facts (including an empty list). Persistent replacement methods take a device-to-device snapshot of imported DLPack columns before committing them. Mutating a retained producer tensor after put_relation, put_relation_with_provenance, or put_relation_from_manifest therefore cannot change stored rows behind the session’s versions, callbacks, or evidence. This is an owned GPU snapshot, not a zero-copy persistent import. Transient evaluate(dlpack_inputs=...) inputs and the handoff of query-result buffers to a DLPack consumer remain zero-copy. For each tensor-like input, XLOG calls __dlpack_device__() exactly once. Only CUDA device memory (kDLCUDA) is accepted; another device raises BufferError before XLOG requests or consumes a capsule. XLOG then calls __dlpack__(stream=1) exactly once so the CUDA producer orders pending work before consumption on the legacy default stream. If the producer rejects that stream argument, the exception propagates; XLOG does not retry without a stream. Raw capsules bypass both protocol calls. The caller must create each capsule for stream 1 or synchronize its producer first, and must pass it to only one consumer. The native importer still validates the capsule’s device header. Breaking in 0.12.0. This device gate is new, and it applies to every pyxlog entry point that accepts a __dlpack__ object. A CPU tensor that previously travelled some distance into XLOG before failing now raises BufferError immediately, at import. Membership is checked as complete tuples on the GPU. For a non-empty evidence set, the runtime downloads one boolean membership mask in a single transfer—one byte per distinct fact—not the relation rows. Role-only metadata and metadata-free manifests need no membership transfer. Use set_strict_deterministic_d2h(True) to reject even this deterministic mask transfer; deterministic_d2h_violation_count() reports rejected attempts, and a rejection is atomic. relation(name) returns a frozen native RelationEvidence captured at that call. Later session changes do not alter it, and each provenance() call returns fresh Python dictionaries and lists whose mutation cannot change the captured snapshot. evidence(name=None) returns a deterministic program_hash and a relations mapping of packed snapshot dictionaries. With evidence(name), XLOG still computes program_hash over every stored relation in the session and only then filters the returned relations mapping to name; named and unfiltered reads therefore share the same hash at the same session state. These are native session snapshots, not Python package sidecar records. Invalid role, whole-fact provenance, insert-evidence, or manifest input raises pyxlog.RelationMetadataError, a ValueError subclass. Looking up an unstored relation with relation(name) or evidence(name) raises KeyError. Without pyxlog._native, the package-level RelationMetadataError and RelationEvidence names remain importable. The fallback metadata error still subclasses ValueError; constructing the fallback RelationEvidence raises RuntimeError because it has no native snapshot. Native evidence instances and all session operations require the extension. Source builds that compile the extension expose this API; for a packaged build, determine availability from that release’s notes.

Provenance manifest round trips

Use the paired DLPack-and-manifest API to reconstruct rows and native evidence in another compatible session:
The manifest is the exact xlog.relation-provenance version 1 shape. Its required top-level fields are format, version, predicate, row_count, metadata_present, roles, and facts. predicate contains name, arity, and the compiled schema_sha256. Manifest facts contain identity, exact cells, and fixed-shape provenance records; they intentionally omit the friendly tuple. Every dictionary level rejects missing or unknown fields. version, arity, and row_count must be non-negative Python integers, not booleans, and metadata_present must be an actual Python bool. If it is false, both roles and facts must be empty and importing the manifest resets any registered positional role contract. Each exact cell contains only type and hex. Its type must match the compiled column, and its lowercase hexadecimal value must encode exactly that scalar type’s little-endian byte width; a boolean cell is exactly 00 or 01. Manifest provenance records contain all six fields (source, document, span, content_hash, kind, and polarity), using None for absent values. A non-null span contains exactly start and end; both are non-negative Python integers rather than booleans, both must be representable as u64, and start <= end. The schema fingerprint includes the predicate name and arity plus every compiled column’s name, scalar type, and optional source-domain sort. A fact identity includes the predicate name and arity plus each cell’s type code, byte length, and exact bytes; it does not include provenance records. Both hashes use domain-separated SHA-256 inputs. Fact identity is independent of row position and role labels, and import recomputes both hashes instead of trusting the supplied strings. Import validates static manifest structure, compiled schema identity, and column count before consuming a DLPack capsule. Once column import starts, all supplied capsules are consumed before dtype, equal-column-length, manifest row-count, and whole-fact membership validation completes. A failure at any of those stages still leaves the target relation and evidence unchanged, but the spent source capsules cannot be reused. Successful import consumes every column once. Fact and record order and exact duplicates are normalized deterministically. The DLPack columns are process-local, single-consumer ownership objects; the manifest may be serialized as data, but it does not contain the relation columns and is not a cross-process persistence format. Keep the exported columns alive until import consumes them. For portable storage, use a host-serialized relation format and treat native manifest reconstruction as a separate in-process operation.

Migrating from Python-side relation evidence

The native API replaces the earlier Python helper contract. The old relation_schema, source_path, source_hash, row_hashes, field_hashes, accepted_count, rejected_count, output_path, output_hash, and decision_counts keyword arguments are not accepted by put_relation_with_provenance. Pass ordered roles and complete facts instead. A per-fact source location can map to a record’s source or document, and a per-fact source digest can map to content_hash; XLOG derives exact fact identities from the typed cells. Row/field hash collections, output locations and hashes, and aggregate acceptance, rejection, and decision counters remain application-level data rather than native whole-fact evidence. The returned snapshot and evidence() payload contain native roles and facts, and unknown named reads raise KeyError instead of returning an empty sidecar record.

Runtime controls and diagnostics

Long-running callers can submit logic or probabilistic evaluations to a background Python worker with evaluate_async(...). The returned AsyncEvaluation is awaitable and also exposes done(), cancel(), exception(), and result(timeout=None) for synchronous orchestration.
Large logic outputs can be consumed as DLPack-compatible CUDA tensor chunks:
The same chunking is available from an already materialized result:
Per-call memory_mb is accepted by CompiledLogicProgram.evaluate, LogicRelationSession.evaluate, CompiledProgram.evaluate, and CompiledProgram.evaluate_device. A zero limit raises ValueError; a limit below the provider’s current tracked allocation raises MemoryError before the evaluation starts. The provider-level compile-time budget remains the hard GPU allocator budget. Runtime progress and diagnostics are exposed as stable dictionaries:
memory_stats() reports allocated_bytes, memory_limit_bytes, peak_memory_bytes, and status. peak_memory_bytes is the high-water mark of successful reservations recorded by the shared provider’s memory manager. It spans the provider lifetime (or the window after an explicit quiescent reset), so it is not reset between evaluations or executors that reuse that provider. It is not physical/NVML usage, and direct CUDA allocations that bypass the manager are not included. CUDA Graph stats report csm_cuda_graph_captures, csm_cuda_graph_launches, csm_cuda_graph_fallbacks, and csm_cuda_graph_cache_hits. If an environment cannot supply a given diagnostic, it reports an explicit unavailable status or error rather than a fabricated zero. neural_hot_loop_diagnostics() is the single audit surface for the neural inner loop. (nn/4 is a neural predicate declared with four arguments — the classification form, which carries a list of output labels.) It reports post_load_dtoh_bytes, post_load_htod_bytes, control_plane_bytes_per_iteration, scalar_sync_checks, nested cuda_graph, and nested circuit_cache diagnostics from the same runtime API. When this runtime cannot yet provide a separate control-plane or scalar-sync counter, the corresponding value is None and a *_status field explains why. The top-level pyxlog wrapper also carries nn/4 training lineage:
The lineage payload contains checkpoint_hash, split_hashes, calibration_metrics, and cuda_device. Its influence_audit object keeps registration metadata under registration and copied event snapshots under records; each event includes the changed_acceptance evidence recorded through record_nn4_influence(...). Reading named lineage or recording an event for an unregistered network is an error. register_network also accepts three keyword-only arguments that record and check the network’s typed signature. All three are available since 0.11.0:
  • arity= is the declared argument count. XLOG validates it against every nn/4 declaration bound to that network name in the program rather than trusting it; a disagreement raises ValueError.
  • arg_sorts= is a sequence of integer sort ids, one per argument. It requires arity and must have exactly that length. A bool element is rejected outright — Python’s bool is an int subclass, so True would otherwise be read as sort id 1.
  • artifact_hash= records the checkpoint identity.
program.network_metadata("mnist_net") reads that back, together with what the program itself declares:
It covers classification networks only. A name declared as an embedding is refused, because embeddings carry no registration metadata. Available since 0.11.0.

Epistemic evidence -> exact probability

CompiledLogicProgram.evaluate_conditioned(prob_source) runs a compiled epistemic program (know / possible) on the GPU and conditions an exact probabilistic query on its accepted world view. Only facts declared in the epistemic program’s own source feed that world view. Limitation: unlike evaluate, this method does not accept dlpack_inputs. Caller-supplied input relations are NOT consulted. If the epistemic program depends on a relation that would normally be supplied at call time via evaluate(dlpack_inputs=...), that relation is empty here, no world view is accepted, and evaluate_conditioned raises RuntimeError:
It does not fall back to the unconditioned prior. This is fail-closed by design: a conditioned query that silently became unconditioned would return a plausible number with nothing in the result marking it as unconditioned, which is exactly the failure the trace counters exist to prevent. To probe for the state without catching an exception, call epistemic_evidence() first — it reports accepted_world_views == 0 and does not raise.
When the circuit structure stays fixed and only independent fact priors change, prepare it once and reuse the returned handle:
gpu_conditioned_circuit_preparation_compiles counts actual GPU circuit compiler invocations owned by that prepared handle: it is 1 after a fresh compile and 0 after a verified disk-cache restoration or GPU-cache hit. The three origin counters above are mutually exclusive for the handle’s single materialization. Every later evaluation reports zero source/program compile deltas. For an independent lifetime-reuse check, compare the gpu_conditioned_circuit_generation and gpu_conditioned_circuit_cache_slot trace pair across evaluations. It is derived from the retained exact state and cache handle and must remain unchanged as priors are updated. The generation is opaque and process-local; do not persist it or compare it between processes. These reuse keys are present on every EpistemicEvalResult. Results returned directly by evaluate_conditioned() carry zeroes for the reuse fields; gpu_conditioned_circuit_generation == 0 is the sentinel that no prepared circuit identity is attached. A prepared evaluation always reports a positive generation. set_fact_probabilities() validates the complete mapping before one serialized state change. Each variable id is its prob_var_map() list index. Index 0 is unused padding with kind == "other", not a mutable variable id, and only entries with kind == "fact" are mutable. Annotated-disjunction choices, compiler-introduced entries, Monte Carlo and count-lift programs, non-finite probabilities, and values outside [0, 1] are rejected without a partial update. When a mutable fact is itself fixed by accepted evidence, changing its prior keeps the evidence assignment fixed and changes the evidence likelihood (log_z_e), matching a fresh conditioned compilation at the new prior. Evaluations and setters on the same prepared handle serialize. Coordinate access at a higher level when an application needs an update and its following evaluation to behave as one larger transaction. Waiting for the prepared handle’s shared native state, including in prob_var_map(), releases the Python GIL. The Python dictionaries are constructed only after the native metadata snapshot completes. A failed device write is rolled back before set_fact_probabilities() returns. If that rollback also fails, the prepared circuit is permanently invalidated: every later evaluation, gradient, metadata read, or setter through that handle or any clone fails closed rather than using potentially partial device weights. On a CUDA device, conditioning 0.6::fact(). query(fact()). on know fact() raises P(fact()) from the unconditioned 0.6 to the exact 1.0: the epistemic layer already accepted fact() into its world view before the probabilistic query ran, so result.log_z_e is ln(0.6), not 0.0. result.log_z_e is log P(evidence): the exact log-probability of the conditioned evidence under the probabilistic program’s distribution, computed by weighted model counting over the compiled circuit, not the log-evidence of the whole query program. Query probabilities are exp(log_z_eq - log_z_e). When the conditioned atoms are independent root facts it coincides with the log of the product of their priors — measured on GPU, one known atom at prior 0.6 gives log_z_e == ln(0.6) and two known atoms each at prior 0.5 give log_z_e == ln(0.25) — but that is the independent-root special case, not the definition. Evidence on a derived atom (0.6::a(). b() :- a(). with know b() gives ln(0.6) though b has no prior), on atoms sharing an ancestor, or negated evidence all depart from the product form. Trace invariant. Conditioning reached the GPU exact path when gpu_conditioned_evidence_facts — the total the engine itself validates — is non-zero. A direct result also requires the GPU exact, PIR/CNF, and knowledge-compilation event counters to be positive; a prepared result instead requires the prepared-circuit reuse counter to be positive, reports one materialization, and identifies exactly one compile/cache origin. The four evidence classes (gpu_conditioned_know_evidence_facts, gpu_conditioned_possible_evidence_facts, gpu_conditioned_not_known_evidence_facts, gpu_conditioned_not_possible_evidence_facts) decompose that total; a possible-only or negated-evidence program conditions correctly with the know class at 0, so check the total rather than the know class alone. EpistemicEvalResult carries atoms, prob and log_prob (DLPack capsules, like EvalResult), log_z_e, and trace. CompiledLogicProgram.epistemic_evidence() runs the same epistemic program and returns an EpistemicEvidence with the accepted-world-view counters alone (epistemic_mode, know_operator_count, possible_operator_count, accepted_candidates, rejected_candidates, accepted_world_views, final_output_rows), without touching the probabilistic tier. Like evaluate_conditioned, it only ever sees facts declared in the program’s own source — but a program that depends on a caller-supplied relation reports accepted_world_views == 0 (with accepted_candidates and final_output_rows at 0) here rather than raising. know_operator_count and possible_operator_count are plan-level censuses and stay non-zero even then, so the state to check is the accepted/consumed family, not “every counter”. Which plans are accepted. Only single-component epistemic plans are supported; split, stratified and WFS plans raise instead of being silently reduced. Both epistemic modes reach this surface: FAEEL programs and non-recursive #pragma epistemic_mode = g91 programs both lower to a single-component epistemic plan and condition normally. epistemic_evidence().epistemic_mode names the mode, and the trace’s accepted_faeel_world_view_evidence_consumed / accepted_g91_world_view_evidence_consumed pair says which one supplied the evidence. Only the recursive G91 shapes — positive possible cycles that need tuple-level compatibility — compile to a dedicated G91-compatibility plan and are rejected at planning. Rejection also covers one case that reads like a false negative: an admissible recursive modal program such as reach(X, Z) :- reach(X, Y), know link(Y, Z). is reduced to ordinary recursion at compile time (the ordinary_recursive_modal_reduction provenance class). The reduction erases the world-view machinery, so there is no accepted world view left to condition on and the program is rejected as “ordinary” despite being full of know. That is deliberate, not a compiler bug. A real CUDA device is required.

Program (Probabilistic)

Compiles and runs a probabilistic Datalog program — facts and rules annotated with probabilities, such as 0.3::rain — and computes query probabilities. The prob_engine argument selects the inference method: "exact_ddnnf" compiles the program to a Boolean circuit (a deterministic, decomposable negation normal form, or d-DNNF) that makes exact probability and gradient computation tractable, while "mc" estimates probabilities by Monte Carlo sampling.

Host Outputs (Requires host-io)

When built with --features host-io, you can call CompiledProgram.evaluate(...) to get host-derived probability outputs as device tensors (DLPack):

Monte Carlo Inference (Device-Only)

For device-result workflows, prefer CompiledProgram.evaluate_device(...): result buffers stay on the GPU, while the runtime may still observe its bounded terminal status and convergence summary. The optional samples, seed, confidence, sampling_method, and max_nonmonotone_iterations arguments override the corresponding source pragmas only when supplied. When an argument is omitted, Python uses #pragma prob_samples, prob_seed, prob_confidence, prob_method, or prob_max_nonmonotone_iterations; if neither layer supplies a value, the documented language default applies. This is the same source-then-explicit-override precedence as the CLI. Supplying any of these Monte Carlo options, or allow_cpu_oracle=True, to an exact-engine evaluate(...) call is an error; the binding does not silently ignore them.

Monte Carlo Inference (Host Outputs, Requires host-io)

When built with --features host-io, CompiledProgram.evaluate(...) computes probabilities and confidence intervals and uploads them as device tensors (DLPack):

Experimental Arrow C Device Interop (Feature arrow-device-import)

These helpers bridge XLOG’s DLPack columns to Apache Arrow’s C Device interface — a standard for sharing columnar data that already lives on a device (GPU) — without host copies. When built with --features arrow-device-import, pyxlog exposes:
  • pyxlog.export_arrow_device(...) -> PyCapsule (name arrow_device_array)
  • pyxlog.import_arrow_device(...) -> (dlpack_tensors, names, num_rows)
This is experimental and currently rejects nulls; import does not yet support bit-packed Bool.

JointConstraintCarrier (joint constraint solving)

pyxlog.JointConstraintCarrier picks one label per entity subject to pairwise constraints, entirely on the GPU. Its result buffers are handed back as DLPack capsules, so a caller never copies them to the host. Available since 0.11.0.
  • register_schema(catalog_sha, solver_identity) must be given pyxlog.SOLVER_ABI_IDENTITY — a module-level string naming the solver ABI this build speaks — so a carrier cannot be driven by a mismatched client.
  • bind_signatures(head_masks, tail_masks) must run before any solve.
  • note_producer_stream(...) and note_consumer_stream(...) perform the CUDA stream handoff when the buffers cross into or out of another library’s stream.
  • fuel_spent is a read-only property reporting how much of fuel_limit the solves consumed.
Every precondition is enforced, not assumed: a violated one raises pyxlog.CarrierRefused, and running out of fuel_limit raises pyxlog.SolverResourceExhausted. See Error Handling.

Term Embeddings

The register_embedding / forward_embedding API enables explicit PyTorch-side embedding training through the logic program. Embedding predicates use the label-free nn/3 declaration form (a neural predicate declared with three arguments and no output label list).

Embedding Registration

Forward Lookup

Cross-Registration Validation

  • Embedding declarations (nn/3, no labels) reject register_network() — error directs to register_embedding()
  • Classification declarations (nn/4, with labels) reject register_embedding() — error directs to register_network()
  • Same network name as both embedding and classification → compile-time error

Constraints

  • trainable=True requires nn.Embedding; raw torch.Tensor with trainable=True raises ValueError
  • Raw tensors with requires_grad=True are detached at registration (frozen contract enforced)
  • Integer IDs only (symbol/string lookup keys deferred)
  • Optimizer ownership is user-managed; classification-network optimizer helpers do not cover embeddings
  • Inference through rules (dot/cosine evaluation, grounded query API) is deferred to future embedding-rule integration

Training Loop API (Neural-Symbolic)

For neural-symbolic training with neural predicates (nn/k — a predicate backed by a neural network), Program exposes loss computation, optimizer stepping, gradient clipping, learning-rate control, and batched training loops, in addition to the single-query forward_backward* helpers.

Loss computation

The nll_loss* helpers compute a negative log-likelihood (NLL) loss — the standard training objective that penalizes low predicted probability for the target answer.

External Consumer Bridge Helpers

These helpers keep a four-valued evidence model (Belnap logic) in the Python/ML layer: each candidate carries separate pro (evidence for), contra (evidence against), and quarantine (held-out) scores. The GPU structural kernels never see those channels. The helper surfaces operate on PyTorch tensors and preserve autograd unless the caller explicitly detaches inputs.
deterministic_topk(...) resolves ties by lower input index. neural_cache_stats() reports circuit-cache size, hit/miss counters, template compile count, query-signature cache size, and registered-network cache/top-k/deterministic configuration. belnap_loss(...) returns a dictionary containing loss, pro_reward, contra_penalty, quarantine_penalty, cfr_regret_proxy, and the formula string. Registered-network output modes reuse the existing register_network(..., k=N, det=True) configuration. forward_backward_tensor(...), forward_backward(...), and batched neural-query training apply the configured stable top-k or deterministic top-1 mode before NLL loss and cached circuit probability import. Deterministic mode picks a single hard top-1 value on the forward pass, then routes gradients straight through the selected probability on the backward pass (a straight-through estimator, so the discrete choice still trains).

Optimizer and scheduler control

Batched training epoch

Profiling


ILP Training (dILP Beta)

The pyxlog.ilp subpackage provides differentiable ILP (Inductive Logic Programming): it learns Datalog rules from labeled positive and negative examples by gradient descent, treating rule choice as something that can be optimized like neural weights. This surface is beta.

Training API

Artifact Persistence

TrainConfig Fields

The temperature fields (tau_start, tau_floor) control a Gumbel-softmax relaxation — a technique that makes discrete rule choices differentiable so they can be trained by gradient descent. Training starts hot (tau_start, soft and exploratory) and anneals toward tau_floor (sharp, near-discrete).

Result Types

External Consumer Diagnostics

These helpers package the ILP audit surface for downstream applications (“external consumers”) as reusable pyxlog utilities:
A NeuralBodySpec carries an opt-in train_phi_gradient flag, default False. Left at the default, the entity features φ(x) are detached on the training upload, and the uploaded values are byte-identical to earlier releases. Set it to True to leave φ(x) attached, so gradients flow back into whatever produced the features (backbone coupling). Only the autograd linkage changes — never the values that are uploaded. Available since 0.12.0. train_and_promote(...) accepts transfer-audit metadata through training_fold, held_out_domains, base_kernel_checksum_before, and base_kernel_checksum_after. The returned PromotionResult.rule_inventory records those values with selected and rejected clauses, scores, and gate outcomes.

Device Query APIs

For GPU-native ILP workflows, CompiledIlpProgram exposes device-resident query helpers (results stay on the GPU) alongside the existing host-returning helpers:
Contract notes (CSR here means compressed sparse row — a compact layout that stores per-row offsets plus a flat list of entries):
  • batch_fact_membership() and batch_tagged_credit() remain available for host-materialized Python outputs
  • batch_fact_membership_device() returns a DLPack bool tensor on CUDA
  • batch_tagged_credit_device() returns CSR-style device outputs: fact_row_offsets, entry_indices, entry_i, entry_j, entry_k
  • The device query path avoids semantic-loop device-to-host transfers; inspect host_transfer_stats() / reset_host_transfer_stats() when enforcing that contract in tests
  • Unsigned metadata/count tensors are exported as DLPack int32 for broad framework compatibility

Bounded Exact Induction API

Bounded exact induction searches a bounded space of candidate rules and scores each one exactly on the GPU, rather than optimizing rule choice by gradient descent. pyxlog.ilp.induce_exact(..., backend="native") is the GPU-native scorer. The public entry point returns an ExactInductionResult containing ScoredCandidate rows grouped by join shape (topology): chain, star, fanout, then fanin.
The native backend scores each topology independently in one batched CUDA pass. The Python reference can be used for parity checks with backend="python", strict_per_topology=True; leaving strict_per_topology at its default preserves legacy prototype behavior and is not semantically equivalent to native scoring. Exact induction accepts pair relations whose two columns share one of these scalar types: u64, u32, or symbol. Generated ilp_exact.portable.ptx and .cubin files are packaged build artifacts, not checked-in source files.

Sparse Mask APIs

A rule mask selects which candidate rules are active during training. Sparse mask setters let you pass only the selected candidates instead of a full dense vector. CompiledIlpProgram exposes two:
  • set_rule_mask_sparse(name, candidate_ids, soft_probs, budget, allow_recursive=False) is the legacy compatibility path. Rust receives the full candidate soft-probability vector and ranks it internally.
  • set_rule_mask_sparse_selected(name, selected_candidate_ids, selected_soft_probs, allow_recursive=False) is the preferred inner-loop path. Python/Torch performs ranking on CUDA, then Rust consumes only the selected subset and preserves that order as the sparse active-rule list.
Prefer the selected-candidate path when you need zero device-to-host transfer on the provider side during mask setup.

GPU-Native Contract

For Python consumers that need an auditable GPU-native ILP inner loop, the intended contract is:
  • Zero provider-tracked device-to-host transfer inside the semantic loop: set_rule_mask_sparse_selected(...), batch_fact_membership_device(...), batch_tagged_credit_device(...), and compute_ilp_loss_grad_gpu(...)
  • Metadata/control-plane reads may still occur behind public runtime/provider helpers such as cached row-count access; these are not relation-column materializations
  • Compatibility paths that are not suitable for a strict GPU-native inner loop: set_rule_mask_sparse(...), batch_fact_membership(...), batch_tagged_credit(...), and any host-output API gated behind host-io
  • Use host_transfer_stats() / reset_host_transfer_stats() to audit the provider-tracked transfer behavior of the chosen path

DLPack integration

XLOG accepts CUDA-backed DLPack producer objects and returns single-consumer capsules. Transient input imports and framework wrapping of query-result capsules are zero-copy. Persistent relation replacement takes an owned device-to-device snapshot, and stored-relation export leaves a device-to-device clone in the session before transferring the exported allocation. Compatible CUDA-backed producers include:
  • PyTorch
  • CuPy
  • JAX
  • TensorFlow
  • Any CUDA-backed DLPack-compatible library

Input via DLPack

Your producer’s __dlpack_device__() must return an exact two-item tuple (device_type, device_id). The device type accepts an integer enum such as PyTorch’s CUDA tag, or another int instance with value 2; bool is rejected. The device id must be an exact Python int in 0..=2147483647 and match the selected native device. XLOG reads the integer payload without calling custom __int__ or __index__ methods. This device-specific reader does not relax general metadata validation or replace validation of the actual DLPack capsule.

Output via DLPack

dlpack_roundtrip helper

dlpack_roundtrip sends a CUDA tensor into XLOG and returns it as a fresh DLPack capsule — a quick way to verify zero-copy interop end to end. All three arguments are required:

Compile Options

LogicProgram.compile()

Program.compile() (Probabilistic)

Result Objects

Deterministic Results

Probabilistic Results

Device-Only MC Results

Error Handling

Python exceptions are raised for errors:
relation(name) and evidence(name) use KeyError for an unstored relation. export_relation_with_provenance(name) uses ValueError for the same condition. The joint-constraint carrier raises two exceptions of its own, both RuntimeError subclasses, available since 0.11.0:
  • pyxlog.CarrierRefused — every typed refusal: a schema or ABI mismatch, a zero-capacity dimension, use before register_schema, rebinding already-bound signature masks, a signature-mask shape mismatch, solving before bind_signatures, running the top-two stage before feasibility has solved, a malformed component plan, an out-of-range abstain label index, or an unavailable joint-solve kernel.
  • pyxlog.SolverResourceExhausted — a solve used up its fuel_limit, with the message solver fuel exhausted: spent {fuel_spent} of {fuel_limit} node expansions. The fuel meter lives in the carrier session, so a retry reproduces the identical refusal instead of making partial progress; build a new carrier with a larger fuel_limit.

Memory Management

  • An unconsumed output capsule owns its exported GPU buffer and releases it when garbage collected.
  • A framework conversion such as torch.from_dlpack(capsule) consumes that capsule exactly once and takes over its managed-tensor ownership.
  • Passing a CUDA tensor object lets XLOG request a fresh capsule through the producer protocol. Do not manufacture or reuse raw input capsules unless you also satisfy the stream-ordering and single-consumer contract.
  • Persistent relation replacement and stored-relation export use owned device-to-device copies rather than mutable views of session storage.

Thread Safety

  • compile() is thread-safe
  • evaluate() is NOT thread-safe on the same program instance
  • Use separate program instances for concurrent execution

Examples

Integration with PyTorch

Batch Processing

Read-only semantic model evaluation

The semantic-policy native build provides SemanticTransitionController.prepare_model_evaluation(task_use, parent, model_work_capacity=..., cohort=None) for an already admitted training operation and its genuine acquired parent. It does not construct an Update, run backward, draw randomness, update an optimizer, or publish a successor. The returned SemanticModelEvaluation owns one recording. Export its training_view(consumer_stream=...) and model_work_buffer(consumer_stream=...) before begin(), then use the existing model content scope on the same parent and the existing numerical/work registrar on the evaluation’s consumer_stream. record_model_work(kind, dimensions) and record_model_device_work(kind, upper_dimensions) use the same native recording, geometry rules, scratch layout, and device work consumer as prepared execution. Saved-autograd-copy recording is intentionally unavailable: this invocation is forward/objective-only. Retain evaluation.cohort separately for later observations. Passing that native object as cohort= reuses the exact original seventeen ports and their private content seals, including across full checkpoint restoration; no final-model RNG selects another group. The cohort does not retain a Python Runtime or model. The trusted phase owner must still bind the original source checkpoint and all private branches; this API alone is not scientific phase acceptance. finish(output_witness, consumer_streams=...) consumes the original parent’s native content witness containing exactly three tensor rows, with role zero and indices zero, one, and two: contiguous FP32 [6] losses in the numerical producer’s canonical order, Bool [1] numerical admissibility, and the original contiguous UInt8 slab backing. Outputs must lie in that actual CUDA allocation, which must not alias publication storage. Native joins all consumers, validates the original cohort and witness, consumes actual work, and rechecks the complete original publication, including model allocations and RNG. It then returns status, six loss_bits, model_work, operation_count, work_bound, and retained_allocation_bytes. Status zero means the cohort, numerical outputs, and work recording passed their native checks. Status one is a completed refusal: the original selection refused, numerical admissibility is false, or at least one loss is nonfinite. Status two means work overflow, an incomplete event count, or a work-bound mismatch; it takes precedence over numerical refusal and does not certify complete work. Neither nonzero status is a loss certificate. Do not record status two as an ordinary numerical refusal or retry the invocation. Operation count is registrar occurrences, not model forward calls. These statuses exist only when completion returns its result. A witness validation, consumer join, content-integrity, or full-publication recheck exception is not a returned refusal status and does not establish reusable state or known completion. It retains the existing cancellation or quarantine requirements. finish accepts an output witness exactly once and retains the complete original consumer-stream roster before submitting the result kernel. If a late cold completion or observation-allocation failure occurs while that original native submission remains unpoisoned, it raises SemanticModelEvaluationPending, with the original exception as its cause. Keep the original Runtime, cold objective, evaluation, output witness, tensors and content owners alive. Do not execute another forward, call finish again, replace streams, cancel that submission, or prepare a replacement evaluation. Use evaluation.resolve_completion() without arguments to complete that same submitted report through its retained streams and full-publication check. It never begins recording or launches the numerical result kernel again. Once the immutable observation is issued, resolution returns that identical Python object; a late delivery failure cannot manufacture a replacement observation. Further resolvable cold failure raises the same pending exception. CUDA/context failure still poisons and retains the original Session; this resolver does not clear quarantine or make that failure recoverable. Known incomplete work never issues a completed observation or the resolvable pending signal. Memory accounting includes the live native managers and the queried slab backing allocation. It is not a time-sampled whole-process peak and does not cover unrelated external allocations. The consumer’s original bounded memory scope and full-system peak measurement remain necessary. Preparation and completion export full native material and metadata cold; neither belongs inside a claimed zero-transfer loop. Successful completion restores the original task phase without changing its authority snapshot. Before output submission, cancel(consumer_streams=...) does so only after joining actual consumers and revalidating the original full publication. Cancellation is forbidden after output submission. Unknown completion keeps native buffers retained and blocks mutation/release. Dropping an unfinished owner quarantines its Session; it is not cancellation or proof of completion. Keep all Python producers and aliases alive until known completion and creator-thread cleanup.

Limitations

  • Linux x86_64 + CUDA only
  • Source builds that compile the PyO3 extension expose the documented native API. For prebuilt packages, use the release notes for that package version to determine which surfaces it contains.
  • Pure-Python helper modules can import without pyxlog._native, but native-backed compile, evaluate, and session APIs still require the extension. RelationEvidence and RelationMetadataError remain importable for annotations and exception handling in source-only mode. The fallback RelationMetadataError is a ValueError subclass, while constructing fallback RelationEvidence raises RuntimeError because its state is native-owned.

See Also