XLOG is a GPU-native logic programming language: you declare typed facts and rules (in the Datalog tradition), and XLOG compiles them into GPU execution paths that run the joins, negation, aggregation, and inference. This page is the complete reference for the language — every statement form, data type, operator, built-in, pragma, and grammar rule. Use it to look up an exact detail; each section is meant to be read on its own.

Scope and release context

This reference describes XLOG’s implemented language surface. It has three layers:
  • The core language — facts, rules, recursion, deterministic negation, aggregation, and arithmetic.
  • The language-completeness contract — finite terms and lists, safe meta-predicates, deterministic negation, magic sets, probabilistic aggregates, approximate inference, and CLI inspectability.
  • The epistemic surface — the modal operators know, possible, not know, and not possible, plus #pragma epistemic_mode = faeel|g91. “Modal” here means the operator talks about the models (the possible “worlds”) a program admits, not about a single fact.
Accepted epistemic programs are compiled into the EIR — the epistemic intermediate representation, the compiler’s internal form for know/possible programs — and run through the high-level xlog run GPU epistemic runtime. An unsupported epistemic form must fail with a typed diagnostic rather than silently falling back to CPU evaluation. Within the epistemic surface, the executor is complete for:
  • enumerating candidate worlds from the EIR;
  • checking modal membership at the value level, one tuple key at a time;
  • FAEEL foundedness per tuple key — FAEEL is XLOG’s default epistemic semantics, and a tuple is “founded” when it has a non-circular justification;
  • ground epistemic constraints, safe splitting of independent components, and solving several epistemic components jointly.
Its semantic rules are also complete for:
  • global and per-row modal gates, which combine with logical AND;
  • finite nested modal chains (such as know possible fact()), which normalize to a single operator by parity/duality;
  • predicates that share a name but differ in arity, told apart by their arity-qualified tuple sources;
  • recursion — covering determined stratification (ordered evaluation after a modal target is materialized), positive FAEEL dependency cycles through an ordinary founded least fixpoint, supported positive exact-tuple Gelfond-1991 possible cycles through a greatest compatibility fixpoint, stratified negated modals, and cyclic negated modals. Supported cycles through negation use the xlog-gpu GPU-backed Well-Founded Semantics (WFS) plan — a fixpoint that assigns each atom true, false, or undefined — which replaces the older xlog_prob host-side WFS solver. A positive compatibility component does not itself use WFS, although an unrelated nonmonotone component may use WFS within the same upper-bound or refinement pass.
Programs outside the executor’s finite-key, relation-identity, tuple-shape, or materialization contracts fail closed with a diagnostic instead of being executed. The concrete boundaries are listed under Epistemic support and boundaries.

Table of Contents

  1. Overview
  2. Basic Syntax
  3. Data Types
  4. Language Completeness Contract
  5. Predicates and Declarations
  6. Facts
  7. Rules
  8. Queries
  9. Constraints
  10. Variables and Wildcards
  11. Comparisons
  12. Arithmetic Expressions
  13. Negation
  14. Aggregations
  15. Lists
  16. Safe Meta-Predicates
  17. Magic Sets
  18. User-Defined Functions
  19. Modules
  20. Symbols
  21. Probabilistic Logic
  22. Epistemic Logic
  23. Approximate Inference
  24. Neural Predicates
  25. Learnable Rules
  26. Term Embeddings
  27. GPU ILP Configuration
  28. Pragmas and Directives
  29. CLI Developer Experience
  30. Float Predicates and IEEE 754 Semantics
  31. Comments
  32. Complete Grammar Reference

Overview

XLOG is a GPU-native logic programming language that compiles typed, modular logic programs into backend-specific GPU execution paths. It supports:
  • Datalog fundamentals: Facts, rules, recursive queries, and stratified negation
  • Arithmetic operations: Comparisons, computed values via is, and built-in functions
  • Aggregations: count, sum, min, max, and logsumexp
  • Probabilistic reasoning: Probabilistic facts, annotated disjunctions, evidence, and queries
  • Epistemic reasoning: know/possible modal body literals, FAEEL default semantics, and Gelfond 1991 compatibility mode through explicit EIR/GPU execution paths
  • Language-completeness contract: Finite lists, safe meta-predicates, explicit negation contracts, magic-set planning, probabilistic aggregate semantics, approximate inference configuration, incremental parsing, and CLI inspectability
  • GPU execution: All core operations (joins, sorts, aggregations) run on the GPU

Hello World Example

Running this program:
Output:

Basic Syntax

Program Structure

An XLOG program consists of a sequence of statements. Each statement ends with a period (.).

Statement Types

Identifiers

  • Predicates and atoms: Start with a lowercase letter, followed by alphanumerics or underscores (edge, reach_from, node2)
  • Variables: Start with an uppercase letter, followed by alphanumerics or underscores (X, Node, Value1)
  • Anonymous variable: A single underscore (_) matches any value without binding

Data Types

XLOG supports the following scalar types:

Literals

Type Inference

Predicate declarations set column types explicitly. Without a declaration, compilation infers a schema from available facts and rule information. When different source programs—either the entry program and a selected import, or two selected imports—contribute clauses for the same undeclared predicate name and arity, module resolution compares inferred head-column types before merging them. Head constants, head variables typed by ordinary body atoms or built-in arithmetic bindings, and aggregate result types supply evidence, including through rule chains; unanchored variables do not. Conflicts produce error[E0412]. Invalid built-in arithmetic or aggregate evidence produces error[E0413] with its source module and file; user-defined function calls are expanded after imports merge and validated during compilation.
Within a single program, compilation also rejects a rule whose variables draw incompatible column types from the available schemas of the predicates they touch — whether the conflict is between two body atoms or between a body atom and the head. Schemas inferred from facts and rules participate in the same check as explicit declarations. The error names the rule, the variable, both types, and where each came from:
An explicit pred declaration makes the expected schema stable and visible at the source boundary. Without one, compilation infers the predicate schema from the available program evidence and still rejects conflicting type flows.

Language-Completeness Type Forms

The language-completeness contract extends the type model with finite language-level terms that must lower to typed relation layouts before execution: Unsupported type forms must fail during parsing or semantic analysis before runtime execution starts.

Language Completeness Contract

The language-completeness contract is a language-surface expansion over the core runtime. New syntax is accepted only when it has a typed normalization and lowering path for every execution route that supports it. Deterministic programs flow through RIR (XLOG’s internal relational intermediate representation), the optimizer, and runtime; probabilistic and epistemic constructs use their own typed IR and execution routes. CLI commands report the route that was actually analyzed rather than implying that every construct traverses every subsystem.

Execution Responsibilities

Accepted language-completeness and epistemic features must not execute by constructing an arbitrary CPU Prolog term heap, running dynamic CPU predicate calls, or using a hidden CPU-only fallback for a GPU-claimed path.

Feature Coverage Matrix

Diagnostic Contract

Unsupported language-completeness and epistemic forms must report:
  • the feature area, such as list, meta, naf, magic_sets, prob_aggregate, or epistemic;
  • the source span when available;
  • the reason the form is unsafe, unbounded, or not GPU-lowerable;
  • a remediation, such as adding a positive binder, a finite cap, or a static predicate reference.
Diagnostic examples:

Epistemic support and boundaries

The source language accepts the epistemic literals and the epistemic_mode pragma listed here, and routes accepted epistemic examples through xlog run. The epistemic executor supports:
  • candidate enumeration derived from the EIR;
  • value-level modal membership;
  • per-tuple-key FAEEL foundedness;
  • ground and variable-keyed epistemic integrity constraints;
  • safe-split equivalence;
  • same-name, multi-arity disambiguation;
  • finite nested-chain normalization;
  • determined-head stratification;
  • positive FAEEL modal dependency cycles through founded least-fixpoint reduction;
  • supported positive exact-tuple Gelfond-1991 possible cycles through a greatest compatibility fixpoint;
  • stratified negated-modal recursion;
  • cyclic negated-modal recursion through the xlog-gpu GPU-backed WFS plan, which replaces the old xlog_prob host-side WFS solver.
The remaining unsupported epistemic forms are:
  • direct raw RIR lowering;
  • genuinely unbounded or untyped modal tuple keys;
  • unsafe unbound negated epistemic variables;
  • CPU-only world-view scans;
  • cyclic WFS shapes outside the xlog-gpu GPU-backed negated-modal WFS plan;
  • recursive epistemic programs that also carry modal integrity constraints;
  • Gelfond-1991 compatibility components with recursive negation or aggregation;
  • one derived epistemic predicate name authored at multiple source arities (pure extensional predicates may still share a name across arities);
  • sibling clauses for an augmented epistemic head that lower to different hidden tuple shapes;
  • queries over an augmented epistemic head whose arguments are not a tuple of distinct named variables; and
  • single-pass clause unions whose modal conjunctions differ and are not proven redundant for every row of the combined relation.

Predicates and Declarations

Predicate Declarations

Predicate declarations specify the name and column types:

Visibility

By default, predicates are public and can be imported by other modules. Use private to hide internal predicates:
Private predicates may support other local definitions when the file is used as the entry file. An exported rule cannot depend on a private predicate because private definitions are not merged into importing programs; such an import is rejected with error[E0406].

Domain Declarations

Domain declarations provide semantic naming for types:

Facts

Facts are ground atoms (no variables) that assert tuples exist in a relation:
Facts can also be written on a single line:

Numeric Facts


Rules

Rules derive new facts from existing ones. A rule has a head (the derived fact) and a body (the conditions).

Simple Rules

Recursive Rules

Recursive rules derive facts that depend on themselves:
XLOG uses semi-naive evaluation for efficient recursive computation, processing only new (delta) tuples in each iteration.

Multi-Atom Bodies

Rules can have multiple atoms in the body, which are implicitly joined:

Variable Binding

Variables are bound by appearing in positive atoms. The same variable name in different atoms creates a join condition:

Queries

Queries specify which relations to output. Use ?- followed by an atom pattern:
Queries can include constants to filter results:

Constraints

Constraints assert that a condition must have no solutions. If any tuples satisfy the constraint body, execution fails:
Constraints are checked after all derivations complete. If violated, XLOG reports an error.

Variables and Wildcards

Named Variables

Variables start with an uppercase letter and are bound to values when matched:

Anonymous Wildcard

The underscore (_) matches any value without binding. Multiple underscores in a rule are independent:

Comparisons

XLOG supports comparison operators in rule bodies:

Examples

Explicit Equality

Use = to explicitly compare variables (in addition to implicit join):

Arithmetic Expressions

XLOG uses the is keyword for arithmetic computations. The left-hand side must be a fresh (unbound) variable.

Basic Syntax

Operators

Built-in Functions

Examples

Type Casting

Type casting is explicit in XLOG. Use cast(expr, type):

Division and Error Handling

To avoid division by zero errors, filter explicitly:

Negation

XLOG supports deterministic negation as failure using the not keyword. In deterministic programs, not atom(...) is closed-world, stratified negation. This is distinct from probabilistic nonmonotone negation, where exact inference may use Well-Founded Semantics (WFS) for accepted probabilistic programs.

Syntax

Examples

Stratification Requirements

Negation must be stratifiable: there cannot be a cycle where a predicate depends on its own negation. Valid (stratifiable):
Invalid (unstratifiable):
XLOG detects unstratifiable deterministic programs at compile time and reports a negation safety error. Low-level stratification analysis still reports the underlying dependency cycle for callers that use it directly.

Domain Safety

Named variables in negated atoms must be bound by a prior source-order binder in the same rule body. Positive atoms bind their named variables, and a deterministic is expression binds its target after the expression appears. Source order matters: a later positive atom does not make an earlier negated atom safe. Use _ for existential positions inside negated atoms.

Aggregations

Aggregations compute summary values over groups of tuples.

Supported Aggregates

Syntax

Aggregates appear in the rule head. Variables not in the aggregate form the grouping key:

Examples

Multi-Key Aggregation

Multiple Aggregates

Computing Averages

XLOG does not have a built-in avg aggregate. Compute it using sum and count:

Log-Sum-Exp Aggregation

For probabilistic and numerical applications, logsumexp computes log(sum(exp(values))) in a numerically stable way:

Lists

The language-completeness contract defines finite list syntax and list built-ins. Lists are accepted only when they are finite and typed; accepted programs lower to relation layouts and normal GPU-capable execution paths. Current finite-list implementation status: scalar, symbol, nested finite list, and finite meta term IDs are normalized to u64 list identifiers plus __xlog_list_* helper relations. list<T> predicate columns lower to scalar list-id columns while helper relations carry length, item, cons, append, sort, msort, and set facts. The current implementation accepts finite literals, declared list<T> columns, safe [Head | Tail] patterns in declared list columns, and the built-ins below when their list argument has a finite literal or a known list<T> type.

Syntax

Built-Ins

Pair helpers may be added as typed helpers when they lower to finite relation columns. Unsupported helpers must be rejected with a typed diagnostic rather than simulated by a CPU term evaluator. Current implementation limits:
  • append(A, B, C) accepts finite first and second list literals; split generation such as append(A, B, [1,2,3]) is rejected as unbounded;
  • sort, msort, and list_to_set require a finite input literal;
  • term, compound, and predref list elements lower to finite u64 meta term IDs when they appear in declared list<term>, list<compound>, or list<predref> contexts.

Examples

Unsupported Forms

The following forms are outside the finite-list contract:
  • unbounded list generation, such as asking append(A, B, [1,2,3]) to enumerate every split unless a finite split mode is explicitly implemented;
  • cyclic lists;
  • heterogeneous lists unless represented through a declared finite term domain;
  • list evaluation that requires an arbitrary CPU Prolog term heap.

Safe Meta-Predicates

Safe meta-predicates provide finite term inspection and static predicate mapping. They do not introduce an unrestricted Prolog dynamic database or unrestricted call/N. Current safe-meta implementation status: accepted safe meta forms normalize before stratification and lowering into ordinary helper relations such as __xlog_meta_functor, __xlog_meta_univ, __xlog_meta_findall_*, and __xlog_meta_maplist_*. Finite term, compound, and predref predicate columns lower to u64 term IDs; compound metadata and univ parts are stored in typed helper relations. findall and maplist currently accept finite source facts and finite list literals only; derived-goal collection and non-literal maplist inputs are explicitly rejected. Use head aggregates such as count and sum to fold data that rules produce.

Supported Meta-Predicates

Examples

Unsupported Forms

  • runtime-variable predicate names in maplist or any future call-like form;
  • assert, retract, dynamic database mutation, or IO predicates;
  • findall over derived goals or goals with variables that are neither bound before findall nor collected by the template;
  • non-literal maplist inputs in the current safe-meta subset;
  • constructing recursive or open compound terms with =..;
  • higher-arity maplist unless explicitly implemented and tested.

Magic Sets

Magic-set rewriting specializes bound deterministic recursive queries by adding derived magic predicates and adorned rules before optimization. The accepted accepted magic-set subset is a source-level rewrite over positive recursive rules that can be proven query-equivalent under the supported left-to-right SIPS (sideways-information-passing strategy — the order in which bound argument values flow through a rule).

Configuration

auto lets the compiler apply the rewrite only when it can prove that the transformed program preserves query output. on requests the rewrite and fails with a typed magic_sets error if the compiler cannot safely apply it. off disables the rewrite.

Example

The bound query argument 1 may seed a magic predicate so recursive evaluation does not materialize unreachable source components. xlog explain must show the adornment, generated magic predicates, and any declined-rewrite reason. The compiler currently emits helper predicates such as __xlog_magic_reach_bf, where b marks a bound argument and f marks a free argument. Accepted rules continue through the normal AST, stratification, lowering, optimizer, WCOJ, and runtime paths.

Supported Subset

  • deterministic programs with ?- queries over recursive predicates;
  • at least one scalar constant in the queried recursive atom;
  • positive recursive rules over the same head predicate;
  • source-order binding propagation through prior positive body atoms;
  • auto, on, and off source pragmas.

Declined Forms

auto leaves the program unchanged and records a decline reason. on fails with a typed diagnostic for:
  • unbound recursive calls under the supported SIPS;
  • rules with negation, aggregation, comparison, is, or unnormalized univ in the recursive target;
  • recursive rules that cross language-completeness list/meta helper predicates;
  • mutual-recursive SCCs in the current subset;
  • probabilistic profiles, which remain governed by the probabilistic engine.

Unsupported Forms

Magic-set rewriting must decline or fail before execution when negation, aggregates, list/meta constructs, or probabilistic rules would make equivalence uncertain. It must not create a runtime side engine.

User-Defined Functions

XLOG supports user-defined functions for reusable arithmetic and relational lookups.

Basic Functions

Conditional Functions

Use if-then-else for conditional logic:

Predicate-bodied functions

A predicate-bodied function derives its result through a relational body:
Predicate-bodied calls are supported in is expressions in ordinary rules and constraints. The relational literals are inserted immediately before the source is binding. Multiple calls within one expression are inserted from left to right. Each invocation receives fresh names for its non-parameter result and body-local variables, and nested calls are expanded recursively up to #pragma max_recursion_depth. The relational body may match zero, one, or many rows. Each match contributes a caller row; predicate-bodied function syntax does not enforce a unique scalar result. Ordinary set semantics deduplicates identical projected caller tuples even when multiple body witnesses derive them. When a parameter is used in a predicate-body term position, its argument must be a variable or numeric literal. Materialize a compound arithmetic expression with an earlier is binding before passing it. Predicate-bodied calls in conditional result branches are rejected because their relational literals cannot be hoisted without changing guarded semantics.

Recursive definitions

Production compilation registers function declarations in source order and expands only definitions reachable from ordinary rule or constraint calls. It rejects duplicate function declarations, but unused definitions with recursion, undefined callees, or a predicate-name conflict do not block this demand-driven path. Expansion is eager across both conditional branches rather than data-dependent. If it tries to enter another user-defined call while already at the depth configured by #pragma max_recursion_depth, it reports error[E0504]. A reachable cyclic call chain therefore reaches E0504 whether it is wholly unguarded or contains a conditional branch that looks like a base case. Function recursion is not a runtime looping construct. The Rust library also exposes strict whole-program validation through FunctionRegistry::from_program. It visits every definition in declaration order and callees in source order. A recursive strongly connected component (SCC) with no conditional-bodied member is rejected with error[E0502]; a conditional-bodied member lets the SCC pass that structural check. Because later expansion still visits both branches, a used guarded SCC can pass strict validation and then reach E0504.

Using Functions in Rules

Functions are called on the right side of an is expression:

Type Annotations

Optional parameter and return annotations are parsed and preserved as function metadata:
Executable types are inferred from predicate schemas and expressions. Function parameter and return annotations are not independently enforced as call signatures.

Private Functions

Functions can be marked private and used by other definitions in the same entry file:

Modules

XLOG supports organizing code into modules for reusability and encapsulation.

Creating Modules

Imported modules are .xlog files, and the filename without its extension becomes the module name. The entry file is loaded from the exact path supplied to the CLI and may use another extension:

Importing Modules

Use the use statement to import public deterministic definitions from modules:

Module Paths

Module paths use / as the separator:
A use in the entry file first resolves relative to the entry path’s directory. A nested use first resolves relative to the importing module’s canonical source directory, then through the configured module search paths. Resolution follows those importer-specific edges throughout validation and merging. Distinct files are not conflated merely because their imports use the same spelling, and aliases of one canonical source file do not duplicate its definitions. Public facts and rules for the same predicate are merged across resolved import branches into one relation. The same union semantics apply when selective imports name that predicate in multiple modules. Participating declarations are checked under error[E0408]. Without a participating declaration, inferred clause-head column types from different source programs—either the entry program and a selected import, or two selected imports—are compared by predicate name and arity. Constants, head variables typed by ordinary body atoms or built-in arithmetic bindings, and aggregate result types supply evidence; unanchored variables do not. Conflicting types for one signature produce error[E0412]. For invalid built-in arithmetic or aggregate evidence, resolution reports error[E0413] with the contributing module and source file. User-defined function calls are expanded after import resolution. For import schema validation, different arities remain distinct. Functions have one body: separate branches cannot define the same imported function, and a module or entry file cannot redefine an imported function. Those function conflicts produce error[E0402]. An imported module that defines an exported function name more than once is rejected with error[E0410]. Every declaration in the entry program and every public declaration selected by the resolved imports participates in declaration compatibility checking. Participating declarations for one predicate must have identical arity, column names, and resolved types; otherwise resolution reports error[E0408]. Private declarations in imported modules and public declarations omitted by a selective import are not merged and do not participate in that comparison. Domain-backed types are compared using the scalar type to which each local alias resolves. Reusing one domain alias name for different scalar types in the entry program or import closure is error[E0409]. Within an imported module, every declaration of one predicate must use the same visibility; mixing public and private declarations is rejected with error[E0411].

Visibility and Encapsulation

Predicates and functions are public by default. Use private to hide implementation details:
Private predicates are not exported. Naming one in a selective use fails with error[E0404]; it does not make the predicate visible to the importing program. An exported rule or function that depends on a private item, or on a public item omitted by a selective import, is rejected with error[E0406]. Include every public dependency in a selective import. Imports merge public deterministic predicates, functions, facts, and rules, together with domain aliases. Probabilistic facts, annotated disjunctions, evidence, integrity constraints, neural predicate declarations, and learnable rule templates belong in the entry file; an imported module containing one is rejected with error[E0405]. Queries, probabilistic queries, and pragmas are entry-file-scoped. Imported queries are not merged, and imported pragmas are ignored with warning[W0510].

Symbols

Symbols are interned strings, represented internally as integers for efficient comparison and storage.

Declaring Symbol Types

Symbol Literals

Symbol literals are written as identifiers (lowercase) or as string literals:

Querying Symbols

Symbols are displayed as their original string values in query output:
Output:

Symbol Comparison

Symbols can be compared for equality:

Reversible Symbols

Symbols are reversible: the original string value is preserved and displayed in query output. Symbols are stored internally as u32 IDs with a bidirectional mapping to strings.
Output displays original string values:

Probabilistic Logic

XLOG supports probabilistic Datalog for reasoning under uncertainty.

Probabilistic Facts

Probabilistic facts are Bernoulli random variables with a specified probability:

Annotated Disjunctions

Annotated disjunctions represent categorical distributions (mutually exclusive outcomes):
If probabilities sum to less than 1, the remaining mass represents an implicit “none” outcome.

Deterministic Rules with Probabilistic Facts

Rules can derive facts from probabilistic facts:
Arithmetic values bound by is in probabilistic rules must be representable by the probabilistic tuple value model. In particular, a u64 result greater than i64::MAX is rejected with a range error before it can be used by another literal or emitted in a derived tuple. Deterministic execution continues to use the full declared u64 range.

Evidence

Evidence constrains the probabilistic model to worlds consistent with observations:

Probabilistic Queries

Use query to compute the probability of an atom:

Complete Probabilistic Example

Running with exact inference:
Output:

Inference Engines

XLOG supports two inference engines: Exact inference supports:
  • Stratified deterministic negation: Automatic layer detection and two-valued evaluation for deterministic not atom programs
  • Probabilistic non-monotone negation: Well-Founded Semantics (WFS) for accepted cyclic probabilistic programs
  • Finite probabilistic aggregates: count, sum, min, max, and logsumexp aggregate outputs compiled through provenance/PIR when the exact finite domain cap is respected
  • Gradients: Correct gradient flow through negated literals
Probabilistic aggregates in the language-completeness contract: finite count, sum, min, max, and logsumexp aggregate programs are supported for exact and MC inference in the finite probabilistic-aggregate subset. Exact mode enumerates finite aggregate outcomes into provenance/PIR formulas, so query and evidence may reference aggregate output tuples. Exact enumeration is capped per group by the operators in the head: a head whose aggregate expressions are all count accepts up to 64 uncertain contributing rows per group, while sum, min, max, and logsumexp accept up to 16. Cap excess fails with a typed prob_aggregate error that recommends MC or reducing the finite domain. Monte Carlo supports probabilistic rules and non-monotone recursion. The production MC path is the GPU-resident megakernel engine, which rejects negation, aggregates, and other unbounded constructs with a typed ResidentRejection. Programs in that fragment (including non-monotone recursion and language-completeness probabilistic aggregates under MC) require an explicit opt-in to the labeled CPU oracle--allow-cpu-oracle on the CLI, allow_cpu_oracle=True in Python, McEvalConfig::allow_cpu_oracle_fallback in Rust — and their results carry mc_engine = "cpu-oracle". Without the opt-in, such programs fail closed with the typed rejection (corrected 2026-06-10; previously this fallback was silent and unlabeled):

Probabilistic Aggregates

Finite probabilistic aggregate programs may query or condition on aggregate outputs:
Exact mode compiles each supported finite aggregate outcome into a Boolean PIR formula over contributing row-presence formulas. Empty probabilistic groups do not materialize count(..., 0) tuples. MC mode samples worlds on the GPU and executes the shared deterministic aggregate path for each accepted sample batch.

Aggregate Lifting

Small-domain aggregate lifting is permitted only when the lifted computation is semantically identical to finite exact enumeration. For each successful lift, explain output reports the detected finite domain, cap, operator, and fired status. Unsupported operators and type, domain, or cap violations return typed compilation errors instead of aggregate-lifting report entries. In the aggregate-lifting subset, finite probabilistic count aggregate heads use exact cardinality dynamic programming when every aggregate expression in the head is count(...). The lift constructs formulas for exactly k contributing rows over the same row-presence formulas used by finite enumeration, so it does not change exact semantics. The count lift accepts up to 64 uncertain rows per group; larger count domains fail closed with a typed agg_lift error. sum, min, max, and logsumexp use factorized aggregate-state dynamic programming with a cap of 16 uncertain rows per group. Successful count and numeric lifts both report fired. xlog explain --format json emits an aggregate_lifting array containing the predicate, group key, operator, finite-domain source, domain size, cap, status, reason, naive outcome count, and dynamic-programming state count for those successful lifts.

Monte Carlo Sampling Methods

The Monte Carlo engine supports two sampling methods: Evidence clamping avoids wasted samples when rejection acceptance rates are low. If evidence is derived, deterministic, or otherwise not directly forceable, XLOG falls back to rejection sampling.

Negation in Probabilistic Programs

Probabilistic exact inference supports both stratified probabilistic negation and accepted non-monotone WFS profiles:

Probabilistic Recursion

Probabilistic facts can be used with recursive rules:

Pragma for Engine Selection

The inference engine can be specified in the source file:
For approximate inference with confidence intervals:

Epistemic Logic

Epistemic logic is part of the accepted source surface. Epistemic logic adds modal body literals for bounded knowledge and possibility reasoning. Modal literals are valid in rule bodies and use the same atom syntax as ordinary predicates. Finite nested modal chains such as know possible fact(), not know possible fact(), and know not possible fact() are accepted program forms. The parser normalizes them to a single epistemic literal by the semantic parity/duality rules: the operator adjacent to the atom determines the modal operator, while leading/interior/atom-adjacent not tokens dualize or negate the resulting literal according to their position.

Epistemic Mode Pragma

faeel is the default epistemic mode when no pragma is present. g91 is an explicit compatibility mode for programs that need Gelfond 1991 possibility behavior.

Runtime Boundary

Accepted epistemic programs enter EIR through xlog_logic::build_eir and run through the high-level xlog run / xlog_gpu::LogicProgram dispatch path. That path classifies dependencies before selecting a single-pass epistemic GPU plan, an ordinary FAEEL founded least-fixpoint reduction, a Gelfond-1991 positive exact-tuple possible greatest compatibility fixpoint, or a GPU-backed WFS alternating-fixpoint plan. Direct ordinary RIR lowering of raw epistemic body literals remains a rejection boundary and must report UnsupportedEpistemicConstruct. The Gelfond-1991 compatibility plan relaxes the selected gates once to compute a GPU upper bound. It then reevaluates the program from its extensional inputs, reading each selected modal target from a frozen copy of the preceding iteration. GPU set comparison over the intensional relations detects the greatest compatible tuple fixpoint. This is a tuple-level contract: a positive non-invariant possible literal qualifies when its key exactly matches its rule head and its head and target share a recursive dependency component. Predicate recursion without compatible concrete tuples is not sufficient. Ordinary integrity constraints are evaluated after tuple convergence. When a pass uses WFS, a positive constraint atom is true only in the lower extension, while a negated atom is true only when its tuple is absent from the upper extension; an undefined atom satisfies neither polarity. Modal integrity constraints in a recursive epistemic program remain unsupported. An unrelated negative cycle may use GPU-backed WFS inside a pass; recursive negation or aggregation within the compatibility component is unsupported because the descending operator would be non-monotone. Single-pass epistemic output materialization does not retain the defining clause that supplied each row. One active clause per predicate signature is always unambiguous. Multiple clauses are also accepted when their normalized modal conjunctions are identical relative to the output columns, because the shared filter distributes over the clause union. For an invariant modal target, know and possible normalize to the same fixed-extension test. A single epistemic clause may coexist with ordinary sibling clauses when each of its positive modal gates is proven to be a no-op for every union row. This includes ground atoms derived from explicit ordinary facts or rules, bijective all-variable exact-head tuples with independent founded support, and positive exact-head possible self-support in G91 compatibility mode. Repeated-variable, constant, or wildcard heads do not qualify as bijective tuple keys. Other mixed or differently filtered clause unions are rejected with epistemic rule-union materialization; applying every clause’s modal gate to the combined relation would otherwise lose valid sibling rows. Admissible recursive epistemic programs are not subject to this single-pass restriction. FAEEL positive-cycle literals resolve into ordinary rule bodies before least-fixpoint execution. Selected Gelfond-1991 gates instead become reads from frozen relation snapshots during descending refinement.

Examples

FAEEL default knowledge:
Gelfond 1991 compatibility:
Independent split components:
Run the shipped examples:

Approximate Inference

Approximate inference is the source and CLI contract for Monte Carlo probabilistic reasoning. It must be reproducible under a fixed seed and must report uncertainty, sample counts, evidence handling, and sampling method.

Source Pragmas

CLI flags override source pragmas when both are provided; the effective configuration must be visible in CLI output and explain JSON. In the approximate-inference subset, xlog prob uses source #pragma prob_engine = mc when --prob-engine is not supplied. CLI flags override source pragmas field-by-field: --samples, --seed, --confidence, --prob-method, and --prob-max-nonmonotone-iterations replace only their matching source values. --output json emits a machine-readable MC report with the same probability, standard-error, confidence-interval, sample-count, evidence-count, seed, confidence, and sampling-method fields as the pretty/csv batch output.

Output Contract

Approximate output formats must include:
  • probability estimate;
  • standard error;
  • confidence interval low/high;
  • sample count;
  • evidence count or acceptance/clamping count;
  • seed and method.
Invalid confidence ranges, unsupported methods, or ambiguous precedence must fail before sampling begins.

Neural Predicates

Neural predicate declarations embed a neural network directly into an XLOG program as a first-class predicate.

Declaration Forms

Classification (nn/4):
Embedding mode (nn/3):

Arguments

Lowering

For nn/4, the network output is treated as a label distribution and lowered to probabilistic facts that enter the inference pipeline. For nn/3, the network output is a dense tensor bound into the program and used through the embedding APIs.

Example: MNIST Classification

Python side:

Example: Embedding Mode

Python side:

Cross-Registration Validation

Classification declarations (nn/4) require register_network(). Embedding declarations (nn/3) require register_embedding(). Reusing the same name across both forms is rejected.

Learnable Rules

Learnable rules are the source-level surface for differentiable ILP. A rule is gated by a named mask tensor:
During training, candidate rules are softly weighted; at convergence, argmax selects the winning rule and the learned artifact serializes the concrete program.

Training API

Multiple Learnable Rules

Cross-References


Term Embeddings

Term embeddings associate dense vectors with discrete logic terms.

register_embedding()

forward_embedding()

The returned tensor stays on the same device as the registered embedding. For a trainable nn.Embedding, gradients remain attached; for frozen tensors they do not. Current scope: explicit training-side embedding registration and batched lookup are supported. Rule-level embedding inference remains deferred.

GPU ILP Configuration

The GPU-resident ILP path exposes runtime configuration and telemetry through Python.

coo_chunk_budget

Controls the per-chunk temporary allocation ceiling used during sparse COO construction on GPU:
The older coo_memory_cap naming is deprecated.

host_transfer_stats()

Training APIs expose transfer telemetry so strict zero device-to-host workflows can verify that the hot path stayed device-resident:

Pragmas and Directives

Pragmas configure compiler and runtime behavior. The current source grammar accepts prob_engine, prob_cache, epistemic_mode, max_recursion_depth, magic_sets, and the MC configuration pragmas below. The epistemic_mode pragma is accepted for the epistemic solver surface.

Syntax

Scoping

Pragmas are entry-file-scoped: only directives in the file passed to xlog run, xlog prob, or xlog explain configure the engine. A pragma declared in an imported module is dropped at merge time and reported with warning[W0510]. See Pragmas apply only in the entry file.

Available Pragmas

Examples


CLI Developer Experience

Developer-experience commands make the language inspectable and interactive without changing the runtime execution contract.

xlog explain

The CLI explain report includes parse-cache and AST counts, stratification status, relational compilation and optimizer status, WCOJ reporting availability, magic-set rewrite results, epistemic lowering status, aggregate lifting summaries, provenance, proof traces, and generated-rule diagnostics without requiring GPU execution. Unavailable analyses include a reason instead of a derived plan or decision. The JSON representation includes the corresponding status, counts, summaries, and diagnostic records and is deterministic for fixed input. JSON output must be deterministic for fixed input. Unknown formats are rejected before compilation.

Incremental Parsing

xlog_logic::ParserSession is the shared parser cache for explain, REPL, and watch workflows. It splits .xlog source into statement units, records stable text hashes and byte/line spans, and reparses only changed statements through the production parser. Cache statistics report hits, misses, invalidations, module invalidations, statement count, and an estimated statement-unit speedup. Module invalidation removes the changed module and cached sources that import that module name. Incremental parse diagnostics include the original line/column and byte span before forwarding the underlying parser error.

xlog repl

The command reads a multiline source session from standard input until EOF, parses it through the incremental parser/session cache, reports statement/rule/query counts, and exits. It does not compile or execute the session and therefore does not initialize GPU execution.

xlog watch

Watch mode re-reads and reparses the entry file at a fixed interval. Each pass prints parser statistics; --explain also builds a fresh explain report. Explain passes resolve sibling imports and imports from --module-path again after each re-read. --once runs one pass and exits, and --debounce-ms controls the interval between repeated passes. Unsupported REPL/watch mutation semantics, such as dynamic assert or retract, remain outside the language.

Float Predicates and IEEE 754 Semantics

XLOG uses hybrid semantics for floating-point comparisons to handle special values (NaN, Infinity, signed zero) correctly.

Comparison Semantics

Total Ordering

For ordering comparisons, XLOG uses a total ordering consistent with Rust’s f64::total_cmp:
This means:
  • NaN values are at the extremes (negative NaN is smallest, positive NaN is largest)
  • -0.0 and +0.0 are distinguishable in ordering (unlike IEEE equality)
  • All values are comparable (no undefined ordering)

Filtering NaN Values

Since NaN != NaN under IEEE 754, use equality to filter out NaN:

Detecting Special Values

Use ordering to detect special values:

Example: Data Cleaning Pipeline


Comments

XLOG supports single-line comments:
Block comments (/* ... */) are not supported.

Complete Grammar Reference

The following is a summary of the current XLOG grammar plus the language-completeness extensions and epistemic source surface in PEG-style notation.

Lexical Elements

Types

Language-completeness contract:

Terms and Atoms

Language-completeness contract:

Expressions

Aggregates

Rules and Facts

Probabilistic Constructs

Declarations

Modules

Pragmas

CLI Examples

Program Structure


See Also