Time now compiles to tensors — and it’s differentiable.
Today we released TensorLogic 0.1.1 — a follow-up to our first stable that makes Linear Temporal Logic operators exact, adds tape-based GPU autodiff on OxiCUDA, brings probabilistic execution (Monte Carlo + variational inference) into the runtime, and lets you query knowledge graphs as tensor contractions. It builds directly on the first stable 0.1.0 (released 2026-04-27); if you know TensorLogic, this post is about what’s new.
No C. No Fortran. No FFI shims. TensorLogic 0.1.1 is Pure Rust end to end: the SciRS2 backend (now 0.5.0) executes the einsum graphs, the OxiCUDA backend (0.1.8) runs the GPU path, and OxiARC (oxiarc-deflate 0.3.3) handles compression. It ships as a single static binary, with Python bindings (pytensorlogic) via PyO3. Rust 1.90+, Apache-2.0.
Why 0.1.1 matters
The headline is temporal logic. In earlier releases the LTL temporal operators were either unimplemented (they returned errors) or, worse, used mathematically-incorrect single-step approximations. 0.1.1 closes that gap:
- Exact LTL, finally.
Next (X)andUntil (U)are no longer stubs that return an unimplemented error — they emit real tensor nodes.Release (R),WeakUntil (W), andStrongRelease (M)replace three wrong single-step approximations with exact finite-trace backward-scan recurrences. - Temporal constraints are differentiable. Every temporal scan ships a VJP (
temporal_binary_scan_vjp), so you can backpropagate through LTL constraints and train models to satisfy them. - GPU gradients. A tape-based autodiff implementation for the OxiCUDA executor records the forward pass and replays it in reverse for
backward. - Probabilistic reasoning in the runtime. Monte Carlo samplers, a
MonteCarloEstimatorwith credible intervals, epistemic-uncertainty estimates, and mean-field variational inference are now first-class. - Knowledge graphs as tensors. A new SPARQL BGP evaluator runs conjunctive queries as boolean tensor contractions over an einsum graph.
Test coverage grew from 6,407 to 7,178 (+771 new tests, 100% pass rate; 37 GPU-only tests skipped in CI).
Technical Deep Dive
The temporal layer. A new temporal_ops.rs module in tensorlogic-scirs-backend is the foundation. It exposes shift_next / shift_prev for the unary time shifts, and a unified temporal_binary_scan / temporal_binary_scan_vjp pair for the binary operators. The scan is parametrised over TemporalBinaryForm (Until / WeakUntil / Release / StrongRelease) crossed with UntilSemantics (MaxMin / ProbSumProduct), with a boundary_val ∈ {0.0, 1.0} distinguishing the strong and weak variants. This generalises the earlier Round-6 until_scan into one exact, reusable recurrence (+16 tests in this module alone).
On the compiler side, tensorlogic-compiler now does the right thing: compile_next emits a temporal_next:<axis> node dispatched through tensorlogic_scirs_backend::temporal_ops::shift_next, and compile_until emits a temporal_until:<tag>:<axis> binary node. Because the backend scans expose VJPs, those temporal nodes are differentiable — temporal logic is now a fully gradient-aware part of the einsum graph.
The GPU layer. tensorlogic-oxicuda-backend gains a TlAutodiff implementation for OxiCudaExecutor that records a tape during forward and replays it in reverse for backward, covering Matmul2D, BatchedMatmul3D, Identity, Unary, Binary, and Reduce ops. The OxiCudaTape exposes gradients: HashMap<usize, OxiCudaTensor> so you can inspect intermediate gradients, and an optional native-broadcast feature routes gradient broadcasts through GPU kernels. Alongside it: tensorlogic-oxicuda-solver adds generic f64 LU/Cholesky/QR-lstsq/CG solvers, a preconditioned CG (pcg_solve) supporting Precond::Jacobi and Precond::IncompleteCholesky, and a tridiagonal solve_tridiagonal via the Thomas algorithm. tensorlogic-oxicuda-sparse generalises SparseCsr<T> over T: Float (f32 stays the default for backward compatibility) and adds SparseCsc<T>, with transpose / to_csc / to_csr / from_dense and f64 SpMV/SpMM plus batched SpMV. tensorlogic-oxicuda-rng adds uniform_f64 / normal_f64 (52-bit mantissa + Box-Muller) and streaming chunked fills.
The probabilistic layer. A new probabilistic/ sub-module in tensorlogic-scirs-backend brings Monte Carlo samplers (sample_bernoulli, sample_uniform, sample_normal, sample_categorical via Gumbel-max) and mc_integrate. A MonteCarloEstimator reports mean / variance / percentile credible intervals; predictive_entropy and bald_epistemic_uncertainty quantify uncertainty. And VariationalInference::fit implements mean-field Gaussian VI with the reparameterization trick + Adam, maximising the ELBO.
Knowledge-graph + ML. tensorlogic-oxirs-bridge adds an InternedGraph (O(1) term dictionary, predicate-indexed adjacency, parallel N-Triples bulk loading) and a TensorBgpEvaluator that evaluates conjunctive SELECT/BGP SPARQL queries as boolean tensor contractions over an EinsumGraph + Scirs2Exec::forward, decoding non-zero entries back to variable bindings. On the learning side: tensorlogic-train gains a nas/ module (ArchSearchSpace / Architecture / LayerSpec, an ArchSampler with 4-operator mutation, RegularizedEvolution with aging evolution + tournament selection and an ask/tell API, plus a RandomArchSearch baseline). tensorlogic-sklears-kernels adds an svm/ module — SvcModel (C-SVM, binary + one-vs-rest multiclass) and SvrModel (ε-SVR) implementing Platt’s SMO with Keerthi two-loop heuristics, all built on Arc<dyn Kernel> — plus a multi_output/ module (MultiOutputKernel trait, ICM, LMC, and VVGP vector-valued GP regression). Finally, tensorlogic-quantrs-hooks adds a VariationalGaussianMixture implementing VBEM with Dirichlet-prior mixing proportions and ELBO monitoring.
Getting Started
cargo add tensorlogic
Here’s the new temporal backend in action — “eventually grant” expressed as Until(true, grant) over a finite trace, computed with the exact backward recurrence instead of a single-step approximation:
use tensorlogic_scirs_backend::temporal_ops::{
shift_next, temporal_binary_scan, TemporalBinaryForm, UntilSemantics,
};
// "eventually grant": Until(true, grant) over a finite trace along the time axis.
// `cond` is a [batch, time] truth tensor; `temporal_binary_scan` runs the exact
// finite-trace backward recurrence (no single-step approximation).
let satisfied = temporal_binary_scan(
&antecedent, // left operand (e.g. all-ones for "eventually")
&grant, // right operand
/* time_axis = */ 1,
TemporalBinaryForm::Until,
UntilSemantics::MaxMin,
/* boundary_val = */ 0.0,
)?;
The higher-level path is the same idea: define an LTL rule and compile it, and the compiler emits the matching temporal_until / temporal_next nodes for you — backed by the very same exact recurrences.
What’s New in 0.1.1
Temporal / LTL
- Exact
Next (X)operator —compile_nextemits atemporal_next:<axis>node (was an unimplemented error). - Exact
Until (U)operator —compile_untilemits atemporal_until:<tag>:<axis>binary node (was unimplemented). - Exact
Release (R),WeakUntil (W), andStrongRelease (M)— replaced incorrect single-step approximations with exact finite-trace backward-scan recurrences. - New
temporal_opsbackend module:shift_next,shift_prev,temporal_binary_scan,temporal_binary_scan_vjp,UntilSemantics,TemporalBinaryForm.
GPU autodiff + solvers
- Tape-based
TlAutodiffforOxiCudaExecutorwith an inspectableOxiCudaTape; optionalnative-broadcastfeature. - f64 LU/Cholesky/QR/CG solvers, PCG (Jacobi / IncompleteCholesky), and Thomas-algorithm tridiagonal solves.
- Generic
SparseCsr<T>+ newSparseCsc<T>, f64 SpMV/SpMM, batched SpMV. - f64 RNG (
uniform_f64/normal_f64) with streaming chunked fills.
Probabilistic execution
probabilistic/module: Monte Carlo samplers,MonteCarloEstimatorwith credible intervals,predictive_entropy,bald_epistemic_uncertainty, andVariationalInference::fit(mean-field Gaussian VI + Adam, ELBO).
SPARQL tensor evaluation
InternedGraph+TensorBgpEvaluator: conjunctive SELECT/BGP queries as boolean tensor contractions, decoded back to variable bindings.
ML (NAS / SVM / GPs / VB-GMM)
- NAS:
RegularizedEvolution(aging evolution, ask/tell) +RandomArchSearchbaseline. - SVM via SMO:
SvcModel(C-SVM, multiclass) andSvrModel(ε-SVR) onArc<dyn Kernel>. - Multi-output GPs: ICM, LMC, and VVGP.
VariationalGaussianMixture(VBEM, Dirichlet prior, ELBO monitoring).
Dependency upgrades
- SciRS2 0.5.0, OxiRS 0.3.1, QuantrS2 0.2.0, SkleaRS 0.1.1, OxiCUDA 0.1.8, OxiARC (oxiarc-deflate) 0.3.3, oxicode 0.2.4.
Tips
- Pick your temporal semantics. Use
UntilSemantics::MaxMinfor hard or fuzzy Boolean reasoning; switch toUntilSemantics::ProbSumProductwhen you want probabilistic temporal semantics. - Backprop through time. The temporal scans ship VJPs (
temporal_binary_scan_vjp), so LTL constraints are just another differentiable node — train your network to satisfy them. - Route GPU broadcasts on-device. Enable the
native-broadcastfeature so gradient broadcasts inTlAutodiffgo through OxiCUDA kernels instead of bouncing through the host. - Query RDF as tensors. Load a graph into an
InternedGraphand run conjunctive SPARQL withTensorBgpEvaluator— the BGP becomes a boolean contraction, and non-zero entries decode straight back to bindings. - Quantify what your model doesn’t know. Use
bald_epistemic_uncertaintytogether withMonteCarloEstimatorfor credible intervals on predictions. - Fit kernels with SMO.
SvcModel/SvrModelimplement Platt’s SMO (Keerthi two-loop) onArc<dyn Kernel>, so they compose with the same kernels as the GP code.
The neurosymbolic foundation
0.1.1 deepens TensorLogic’s role as the neurosymbolic glue of the COOLJAPAN stack — and every integration here is real and shipped. Logic, time, and probability all compile down to SciRS2 tensors (0.5.0). OxiRS knowledge graphs (0.3.1) are now queryable as tensors through the BGP evaluator. The OxiCUDA backend (0.1.8) gives you GPU autodiff, f64 solvers, generic sparse, and f64 RNG. SkleaRS (0.1.1) contributes SVM and multi-output GP kernels, and QuantrS2 (0.2.0) hooks supply the variational Gaussian mixture. All Pure Rust. All in one tensor graph.
Repository: https://github.com/cool-japan/tensorlogic
Star the repo if you want temporal logic that’s both exact and differentiable — running on the GPU, in Pure Rust.
Time, logic, and gradients now live in the same tensor graph.
— KitaSan at COOLJAPAN OÜ June 9, 2026