When to use Tesseract¶
Tesseract runs a component in its own process and lets JAX and PyTorch differentiate through it as if it were native code. In exchange, calls usually cross a process boundary (see Performance trade-offs & optimization), and the component has to be written as a Tesseract: a folder with a tesseract_api.py, a tesseract_config.yaml, and a requirements file, with its inputs and outputs declared as schemas (see Creating Tesseracts). That structure suits a self-contained component, since it declares the component’s environment and interface in one place. It gets in the way when the component is one function inside a larger codebase that you are still changing, or when its natural interface has several entrypoints, which become several Tesseracts.
This page helps you decide when that trade pays off, and points to simpler answers where it doesn’t.
When it fits¶
Tesseract pays off most for a component that meets all four of these conditions:
Condition |
Typical reason |
If not |
|---|---|---|
A rewrite is off the table |
The code is validated, proprietary, decades in the making, or a model trained on data you don’t have |
Rewrite it in JAX or PyTorch. Textbook functions and small ODE systems are better as native code. |
You need gradients through it |
It sits inside an optimizer, a sampler, or a training loop |
A Tesseract without derivative endpoints still works, but a plain service or script may be all you need. |
It needs its own process |
Its dependencies, language runtime, global state, or autodiff framework clash with yours |
Wrap it in a |
Each call does real work |
A call costs a few milliseconds plus the time to move its arrays |
Batch calls, or load the Tesseract in-process with |
There are many reasons to use Tesseract
Tesseract also helps with:
Developing, testing, and shipping components in isolation. Each Tesseract declares its environment and interface in one place, so it can be built and tested on its own, handed to someone who never reads its source, and deployed unchanged. Schemas and generated API docs tell its users what it expects and returns.
Testing a hand-written gradient. A schema plus check-gradients verifies a VJP or adjoint against finite differences, even if the component later runs in-process.
Mixing differentiation strategies. One gradient can combine finite differences through a cheap CAD geometry with a handful of parameters and an exact adjoint through the expensive solver behind it. It can also combine forward-mode derivatives through a physics model with reverse-mode autodiff through a neural network, as in δSnowSac.
In general, don’t let the absence of one of the four main conditions discourage you from using Tesseract if you believe it’s the right tool for your problem.
What you get¶
The simplest way to call a component in another environment is to write its inputs to disk, run it with subprocess.run, and read its outputs back. Tesseract improves on this in many ways:
A persistent process. The component starts once and stays alive between calls, so imports, meshes, and loaded models are reused instead of rebuilt.
Typed interfaces. Pydantic schemas validate every input and output, including array shapes and dtypes, and generate an OpenAPI schema and API docs.
Native differentiation. Tesseract-JAX and Tesseract-Torch turn a Tesseract into a JAX primitive or a PyTorch operator, so
jax.grad,jax.jit, andtorch.autogradtreat it like any other operation. Its gradient can come from an adjoint solver, autodiff in another framework, or finite differences.Gradient checks.
check-gradientscompares every derivative endpoint against finite differences ofapply(see Checking gradients).Managed environments.
Tesseract.from_sourcebuilds an isolated uv or conda environment from the Tesseract’s requirements and reuses it on later runs.tesseract buildpackages the same folder as a container image.Efficient array transport. Arrays travel as base64 or as binary files, optionally compressed or on shared memory (see Fast same-machine runs with shared-memory binref). Arrays already on a GPU can skip the copy to the host with the experimental GPU transport (see Choosing the encoding per request).
Logs and diagnostics. Output from the component can stream back to the caller, metrics and artifacts go to files or MLflow, and built-in profiling, tracing, and debug mode help find slow or failing calls (see Debugging guide).
Composition across stacks. A Fortran solver, a Julia package, and a PyTorch model can share one gradient without sharing an interpreter (see Composing Tesseracts into pipelines).
One component, many deployments. The same
tesseract_api.pyruns in-process, as a local process, in a container, on an HPC cluster, or as a remote service (see Ways to run a Tesseract).
Ready to try it? The Get started tutorial writes and calls a Tesseract from scratch.