Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Eqiora

Eqiora is a typed mathematical modeling and execution system backed by one canonical Rust implementation. Its Python SDK provides immutable native declarations, synchronous and awaitable execution, explicit NumPy/DLPack ownership, bounded first-order PyTorch and JAX adapters, and an optional Matplotlib Result adapter without reimplementing model meaning in Python.

Alpha — 0.1.0a2. The supported boundary is intentionally narrow. Consult the capability matrix before relying on a method, backend, or platform.

Install

Eqiora 0.1.0a2 supports ordinary-GIL CPython 3.11–3.14 on manylinux x86-64:

python -m pip install eqiora==0.1.0a2

Optional first-order framework adapters are explicit:

python -m pip install "eqiora[torch]==0.1.0a2"
python -m pip install "eqiora[jax]==0.1.0a2"
python -m pip install "eqiora[matplotlib]==0.1.0a2"
python -m pip install "eqiora[notebook]==0.1.0a2"

The base package imports none of these optional libraries. The PyTorch extra declares torch>=2.13,<2.14; this release verifies exactly PyTorch 2.13.0. It also verifies the exact JAX/JAXLIB 0.11.0 pair and Matplotlib 3.11.1 on CPython 3.13. The JAX extra requires Python 3.12 or newer.

The exact notebook extra installs anywidget 0.11.0 and keeps the complete private Three.js frontend inside the Eqiora wheel. In the verified Linux x86-64 CPython 3.13 profile, a bare exact accepted 50-chord circular-hole Mesh renders interactively in JupyterLab 4.6.2 and marimo 0.23.16. Other meshes and hosts retain deterministic text; this does not add Mesh selection, field display, saved widget state, a public viewer API, or Studio coupling.

Five-minute model and run

Build a decay relation from frozen native declarations and execute it through the shared native lifecycle:

import eqiora

state = eqiora.Field("state", initial=1.0)
rate = eqiora.Parameter(
    "rate",
    value=1.0,
    dimension=eqiora.Dimension(time=-1),
)
decay = eqiora.Relation(
    "decay",
    residual=eqiora.derivative(state) + rate * state,
)
model = eqiora.Model.define("decay", state, rate, decay)

result = eqiora.run(model, end_time=1.0, max_step=0.01)
time = result["state"].time.numpy(copy=False)
values = result["state"].values.numpy(copy=False)

print(eqiora.__version__)
print(model.digest)
print(time[-1], values[-1])

Field, Parameter, Relation, and Model are immutable handles over Rust-owned meaning. A relation declares a residual equal to zero; validation, typed lowering, atomic commit, execution, and artifact identity remain in Rust. Spatial authoring and the bounded FEM/FVM realization path are described in Modeling and realization.

One explicit locked Model Package can also be checked through the installed Python distribution:

from pathlib import Path

resolution_bytes = Path("resolution.canonical.json").read_bytes()
report = eqiora.check_package_conformance(
    "package-store",
    resolution_bytes,
    entry_model="Main",
    profile="eqiora.package.structural-conformance-v1",
)

The immutable in-process report states structural compatibility and exact package-compilation and current Model identity only. A deliberately false scientific claim in package documentation can still pass: the operation does not prove physics, well-posedness, realizability, numerical accuracy, convergence, performance, or execution support. It runs no package code or tests and creates no registry, installation, publishing, trust, badge, attestation, durable report wire, scientific-evidence decision, or Studio workflow. The precise boundary is documented under Modeling and realization.

The accepted exact-cylinder path now begins with explicit native-owned sketch composition:

base_sketch = eqiora.geometry.CadAuthoredSketch.rectangle_xy(
    x_bounds=(0.0, 2.2),
    y_bounds=(0.0, 0.41),
    plane_z=0.0,
    modeling_tolerance=1e-10,
)
base = base_sketch.extrude_positive_z(depth=1.0)
cut_sketch = eqiora.geometry.CadAuthoredSketch.circle_on_face(
    base.face_handle("end-cap"),
    center=(0.2, 0.2),
    radius=0.05,
)
graph = base.through_cut(cut_sketch, boolean_tolerance=1e-10)
geometry = graph.planar_circular_section(
    classification_tolerance=1e-12,
    region="fluid",
    x_lower="inlet",
    x_upper="outlet",
    y_lower="walls",
    y_upper="walls",
    hole="cylinder",
)
request = eqiora.meshing.MeshRequest(
    maximum_boundary_error=1e-4,
    minimum_mean_ratio=1e-5,
    maximum_boundary_facets=50,
)
plan = eqiora.meshing.resolve(geometry, request)
mesh = eqiora.meshing.generate(geometry, plan=plan)
print(geometry.digest, mesh.digest)
print(mesh.selection_entity_count("cylinder"))

The sketch wrappers retain native values and all dimensions and tolerances are coherent-SI metres. Existing CadAuthoredGraph.rectangle_extrusion and graph.circular_through_cut calls remain supported and reproduce the same canonical graph. The graph and its exact planar section have distinct identities. The section reproduces the accepted exact planar value byte-for-byte; depth and CAD tolerances cannot leak into its independently classified 2D meaning. This is not a generic Sketch, section, or Python Boolean implementation. Its matching meshing operation is one Rust-owned, error-controlled chordal reference path behind common request, plan, and mesh ownership boundaries, not a production mesher. The returned value retains exact source and correspondence identity within the live process; durable generated-realization replay, geometry-backed Model binding, solve, Result, and visualization are separate capabilities.

The accepted exact-cylinder Result can be presented as one bounded pressure still:

import eqiora.matplotlib as eqplot

# `result` is the common Result returned by the accepted fluid solve.
pressure = result.snapshots[0]
figure = eqplot.plot_scalar_field(result, field=pressure.field)
figure.savefig("exact-cylinder-pressure.png")

The adapter selects an exact Model-bound Field from the accepted Result rather than accepting raw arrays. It uses the Result's paired Mesh connectivity, vertex-associated P1 pressure, and Rust-owned full pressure range in pascals. This slice does not claim arbitrary fields, vectors, animation, media-publication, or visual validation.

The accepted mixed-boundary structural workflow is likewise an ordinary Python file:

python examples/python/mixed_boundary_elasticity.py \
  --displacement-png mixed-boundary-displacement.png --scale 1

It compiles the packaged source through the single current Model API, executes the shared Rust application result, and renders original and scaled-deformed canonical Q1 edges. It is one bounded verified case, not a general structural solver or deformation viewer.

The accepted fixed-reference FSI workflow follows the same rule. It resolves a fully explicit, immutable FixedMeshMonolithic intent before submitting the ordinary Run; both coupled time steps execute inside the shared Rust application service:

python examples/python/fixed_reference_fsi.py \
  --fsi-png fixed-reference-fsi.png --step 2 --displacement-scale 12

The common immutable Result exposes the ordered fields and lineage through its Trajectory, while fixed_mesh_monolithic_evidence(result) owns the accepted partition and FSI-specific solver/acceptance observations. The optional still uses only the general trajectory field adapters. This is one verified fixed-reference monolithic case, not general FSI, ALE or moving-mesh support, a Python time loop, or an animation surface.

Structured diagnostics

Failures expose stable categories and structured diagnostics:

try:
    eqiora.run(model, end_time=-1.0, max_step=0.01)
except eqiora.EqioraError as error:
    print(error.category)
    for diagnostic in error.diagnostics:
        print(diagnostic.code, diagnostic.severity, diagnostic.message)

Validation, compatibility, capability, execution, cancellation, and internal failures have distinct subclasses. Ordinary Python call-shape errors remain TypeError.

NumPy ownership and copies

Eqiora Array values own dense, rank-one CPU float64 storage:

array = result["state"].values
view = array.numpy(copy=False)
writable = array.numpy(copy=True)

assert not view.flags.writeable
assert writable.flags.writeable

copy=False and copy=None return the same lifetime-safe, read-only NumPy projection. If that contract cannot be honored, Eqiora fails instead of copying silently. copy=True returns an independent writable allocation. DLPack exports are fresh versioned CPU snapshots, not aliases of immutable result evidence. The complete contract is in Execution, diagnostics, and arrays.

Await, progress, and cancellation

run(...), submit(...).result(), and await submit(...) share one native state machine and one materialized result:

async def simulate(model):
    run = eqiora.submit(model, end_time=10.0, max_step=0.001)
    try:
        print(run.status, run.progress)
        return await run
    finally:
        if not run.done:
            run.cancel()

Cancelling the surrounding asyncio task or dropping a Run does not implicitly cancel native work. Call run.cancel() explicitly. Cancellation is cooperative at accepted execution boundaries and never publishes a partial result.

PyTorch and JAX

Both optional adapters consume the same accepted, opaque DifferentiableProgram. They do not define a second model. This complete example constructs the spatial model and its matching realization before compiling the differentiable program:

import numpy as np

model = eqiora.compile(
    """
    model differentiated_poisson {
      domain square = box(0, 1, 0, 1);
      domain x_lower = boundary(square, axis = 0, side = lower);
      domain x_upper = boundary(square, axis = 0, side = upper);
      domain y_lower = boundary(square, axis = 1, side = lower);
      domain y_upper = boundary(square, axis = 1, side = upper);
      representation scalar_space = continuum;
      field potential on square as scalar_space: 1 = 0;
      parameter diffusion: 1 = 1;
      parameter wave_number: 1 / m = 3.141592653589793;
      parameter source_scale: 1 / m ^ 2 = 19.739208802178716;
      parameter boundary_offset: 1 = 0;
      relation balance continuous on square {
        -div(diffusion * grad(potential))
          - source_scale * sin(wave_number * coordinate(0))
            * sin(wave_number * coordinate(1)) = 0;
      }
      relation x_lower_value continuous on x_lower {
        trace(potential) - boundary_offset = 0;
      }
      relation x_upper_value continuous on x_upper {
        trace(potential) - boundary_offset = 0;
      }
      relation y_lower_value continuous on y_lower {
        trace(potential) - boundary_offset = 0;
      }
      relation y_upper_value continuous on y_upper {
        trace(potential) - boundary_offset = 0;
      }
    }
    """
)
realization = eqiora.preview_realization(
    model,
    eqiora.ScalarElliptic(
        method=eqiora.ScalarEllipticMethod.FiniteElement,
        cells_per_axis=4,
    ),
)
program = eqiora.diff.compile(
    model,
    realization,
    inputs=(
        model.parameter("source_scale"),
        model.parameter("diffusion"),
        model.parameter("boundary_offset"),
    ),
    output=model.field("potential"),
)
point = np.array([19.739208802178716, 1.0, 0.0], dtype=np.float64)
evaluation = program.evaluate(point)
values = evaluation.primal().output.numpy(copy=False)

The current path is host-CPU, rank-one float64, generated-Cartesian scalar elliptic Q1 FEM or TPFA FVM.

PyTorch uses Eqiora's accepted VJP in backward:

import torch
import eqiora.torch as eqtorch

torch_program = eqtorch.bind(program)
theta = torch.tensor(point, dtype=torch.float64, requires_grad=True)
state = torch_program(theta)
state.square().sum().backward()

JAX uses typed native CPU FFI for primal, JVP, and VJP:

import jax
import jax.numpy as jnp
import eqiora.jax as eqjax

jax.config.update("jax_enable_x64", True)
jax_program = eqjax.bind(program)
theta = jnp.array(point, dtype=jnp.float64)
gradient = jax.grad(lambda point: jnp.sum(jax_program(point) ** 2))(theta)

Device transfer is never hidden. GPU execution, output sharding, higher-order differentiation, export/serialization, and general transformation support are not claimed. See Differentiation and framework adapters.

Compatibility and limitations

0.1.0a2 is an alpha prerelease. Public Python names and serialized contracts change only deliberately and are documented in release notes, but breaking changes may occur before 1.0. Corrections to a published artifact use a new version; an existing release is never overwritten.

This distribution does not support macOS, Windows, free-threaded CPython, GPU wheels, bundled MPI, or arbitrary user-defined native operators. It is not a complete physics library or a safety-certified engineering tool.

Links

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

eqiora-0.1.0a2.tar.gz (7.0 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

eqiora-0.1.0a2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ x86-64

eqiora-0.1.0a2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

eqiora-0.1.0a2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

eqiora-0.1.0a2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

File details

Details for the file eqiora-0.1.0a2.tar.gz.

File metadata

  • Download URL: eqiora-0.1.0a2.tar.gz
  • Upload date:
  • Size: 7.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for eqiora-0.1.0a2.tar.gz
Algorithm Hash digest
SHA256 8147a2a344cd729116dd1d6ac0250631a9e4473c4c8b45bc10c80aeffcdadd01
MD5 306807cb216eb782ad25ff4d30f2dd86
BLAKE2b-256 14554bab9d1899e2310e583a97815a7bc06f5d525e869ca61c9c027f7fd059bd

See more details on using hashes here.

Provenance

The following attestation bundles were made for eqiora-0.1.0a2.tar.gz:

Publisher: python-production-publish.yml on nkiyohara/eqiora

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file eqiora-0.1.0a2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for eqiora-0.1.0a2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 6ec7eaf5a03986bb79432943ca0327ae1e8d2934c9723ecf26566f0db4fcdd99
MD5 e0ee5291f9de82e8b7cc941ff4483895
BLAKE2b-256 cd987e3f0b87488f0c7d7181e762543078dd875ca57789b0194d84547310bc47

See more details on using hashes here.

Provenance

The following attestation bundles were made for eqiora-0.1.0a2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: python-production-publish.yml on nkiyohara/eqiora

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file eqiora-0.1.0a2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for eqiora-0.1.0a2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e2c2725202cd84795e92be0df6a7ddfc3323ac6fc6b86c5db64c8fc0299898af
MD5 ce5444a18b494fe3479d432795739b79
BLAKE2b-256 a5bc851e56ba752050095af7a6b6954780b22a2dd4bce60b5b60b62d3ae35b43

See more details on using hashes here.

Provenance

The following attestation bundles were made for eqiora-0.1.0a2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: python-production-publish.yml on nkiyohara/eqiora

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file eqiora-0.1.0a2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for eqiora-0.1.0a2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 ecf97bebe46226c0ee12f48430b0b10bb5b5cecf6bcbc76bdd205dbefbc2de8c
MD5 5752ca386355fd4e3e756f4f30379bb7
BLAKE2b-256 3a40aeed7f51b84583c48833c8a43875a1f4c41804004f00d02ed3784d2eec90

See more details on using hashes here.

Provenance

The following attestation bundles were made for eqiora-0.1.0a2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: python-production-publish.yml on nkiyohara/eqiora

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file eqiora-0.1.0a2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for eqiora-0.1.0a2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 88f6f55122e0ad89df86ab662a5a3fd9cec6bc7e485200400bd2ca8096dd52a7
MD5 bccf5f16459d1dd22cc73053c89093d4
BLAKE2b-256 468e8b6a3b5ca4d07a5b9de45c980aa948c84a16c71dc94b1157bffcb4c5727d

See more details on using hashes here.

Provenance

The following attestation bundles were made for eqiora-0.1.0a2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: python-production-publish.yml on nkiyohara/eqiora

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page