Metadata-Version: 2.4
Name: aadc
Version: 2.18.1
Summary: Accelerate numerical Python by recording and compiling it — with optional exact derivatives (AAD)
Author-email: Matlogica <info@matlogica.com>
License-Expression: LicenseRef-Matlogica-EULA
Project-URL: Homepage, https://matlogica.com
Project-URL: Documentation, https://matlogica.com/docs
Keywords: automatic-differentiation,algorithmic-differentiation,adjoint,AAD,derivatives,gradients,greeks,quantitative-finance,risk,jit
Classifier: Development Status :: 5 - Production/Stable
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: C++
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Science/Research
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE.txt
License-File: THIRD-PARTY-NOTICES.txt
Requires-Dist: numpy>=1.21.0
Requires-Dist: scipy>=1.11.0
Dynamic: license-file

# aadc

**Run the same numerical code many times, much faster.**

AADC records your calculation once — as you already wrote it, in ordinary Python
arithmetic — and turns that recording into machine code. Replaying it skips the
interpreter entirely, and replays several scenarios at once on the CPU's vector
units.

```python
import aadc

f = aadc.Functions()
f.start_recording()
x  = aadc.idouble(1.0);  xa = x.mark_as_input()
y  = my_pricer(x)                  # your existing code, unchanged
ya = y.mark_as_output()
f.stop_recording()

f.compile()                        # -> machine code, once
```

From then on every replay runs compiled. Typical speedups are several times the
interpreted path — about **4x** on one customer's FX pricer — and more when you
replay a batch of scenarios, because the vector backends evaluate four (AVX2) or
eight (AVX-512) at a time.

## Where to look first

```python
import aadc
help(aadc)                 # the three ideas, and a quickstart that runs
aadc.llms_txt()            # the same, written for a coding agent
```

`dir(aadc)` is the declared API (`aadc.__all__`), and every headline type
carries a docstring — `help(aadc.Functions)`, `help(aadc.idouble)`,
`help(aadc.Workspace)`.

**The one trap.** `if x > k:` on an active value forces a plain `bool`, which
freezes that comparison as it came out *while recording* — a later replay with
different inputs still follows the recorded branch. Use `aadc.iif(x > k, a, b)`
when the condition depends on an input. Every such conversion is counted, so
this is detectable rather than silent:

```python
kernel.num_passive_warnings()      # 0 is the goal
kernel.passive_warnings()          # kind and location of each
```

**Derivatives are optional.** If you want them, the same recording gives you
exact first-order sensitivities of every output with respect to every input — no
bumping, no re-running the pricer per input, and exact rather than
finite-difference approximations. If you do not want them, AADC is simply a
compiler for the hot loop you already have.

That is the whole idea: *record once, replay compiled — and take the derivatives
if they are useful to you.*

## Install

```sh
pip install aadc
```

Supports CPython 3.10 and later. One abi3 wheel serves every CPython >= 3.11 on
a given platform (the extension is built against the CPython Limited API); 3.10
is served by its own version-specific wheel.

### Supported platforms

Binary wheels only — there is no source distribution, so `pip install aadc` on
anything not listed here fails with *no matching distribution* rather than
attempting a build.

| | CPython 3.10 – 3.14 |
|---|---|
| Linux x86-64 (`manylinux_2_28`) | yes |
| Linux aarch64 (`manylinux_2_28`) | yes |
| macOS **arm64** (Apple Silicon, 11.0+) | yes |
| Windows x64 | yes |
| macOS x86-64 (Intel) | **no** |
| Alpine / musl | **no** |
| 32-bit Windows | **no** |

The macOS wheels are **Apple Silicon only**. An Intel Mac gets no matching
distribution — the `Operating System :: MacOS` classifier is as specific as
PyPI allows, so this table is the authority, not the sidebar.

The Linux wheels need glibc 2.28 or newer; the shipped library's own floor is
lower (it references no symbol above GLIBC 2.14 / GLIBCXX 3.4.22), so the tag
is the binding constraint rather than the binary.

### Upgrading from `aadc` 1.x

**2.x is a different engine, not a compatible successor.** The 1.x line ended
at 1.8.2; 2.x is a ground-up rewrite with its own recording API, and code
written against 1.x will not run unchanged. Notably `aadc.license()` no longer
exists — entitlement is resolved from the environment instead (see *Licence in
one paragraph* below).

If you depend on the 1.x behaviour, pin it explicitly:

```sh
pip install 'aadc<2'
```

Migrating is a port rather than an upgrade. Start from *Where to look first*
above; the recording shape — `start_recording()`, `mark_as_input()`,
`mark_as_output()`, a workspace per evaluation — is what changed most.

Version 2.2.0 renames the distribution from `aadc-ng` to `aadc`, continuing the
PyPI `aadc` 1.x line. The import (`import aadc`) and every binary name
(`_aadc_ng`, the `aadc_ng` runtime package, `libaadc-ng`) are unchanged.

**Upgrading from an environment that has `aadc-ng` installed:** uninstall it
FIRST (`pip uninstall -y aadc-ng`), then `pip install aadc`. pip treats the two
as unrelated distributions that own the same files, so installing `aadc` over
`aadc-ng` works — but a later `pip uninstall aadc-ng` would then delete the files
out from under `aadc`, leaving an install whose metadata says it is fine and
whose `import aadc` fails.

## Licence in one paragraph

Proprietary. The free **Community Edition** covers non-commercial and academic
use only — personal study, hobby projects, publicly available open-source
development, and research or teaching at a university or recognised research
institute. Everything else is Production Use and needs a commercial licence,
including evaluation by a company and any calculation whose output reaches a
book, a client report or a regulator.

**The Community Edition is fast.** On x86-64 with AVX2, and on Apple Silicon, it
runs the interpreter and every **JIT** backend at full speed, and `compile()`
selects a JIT by default — so on those hosts the ordinary path needs no licence
and no C compiler on the box. Two hosts are the exception: on **non-Apple 64-bit
ARM** the vector backends `compile()` picks there need a licence with the
`arm64` feature, and on **pre-AVX2 x86-64** there is no scalar JIT, so
`compile('scalar')` falls back to the C emitter and needs `codegen_c`. Both
report the feature they want, and neither changes your numbers.

What a licence adds is the ahead-of-time code generators (which write a
translation unit you keep, inspect and ship), AVX-512 in any form, and
double-precision GPU.

Ask for a licensed backend without the licence and those segments replay on the
interpreter: **identical results, more slowly**, with the reason reported in
`compile()`'s `unavailable` field. Nothing expires and nothing contacts a server.
Every numerical answer is the same in every tier — **the licence buys speed,
never correctness.**

Full terms ship inside the wheel at
`<site-packages>/aadc-<version>.dist-info/licenses/LICENSE.txt`, with
`THIRD-PARTY-NOTICES.txt` beside it. Commercial and free full-performance
academic licences: <info@matlogica.com>.

## Which version am I running?

Two version strings, because there are two things to version:

```python
import aadc

aadc.__version__          # e.g. '2.9.2' -- the WHEEL you pip-installed
aadc.__engine_version__   # e.g. '2.9.0' -- the aadc-ng C++ engine inside it
```

`__version__` is the distribution version, identical to
`importlib.metadata.version("aadc")` and to what `pip freeze` reports.
`__engine_version__` is the version of the compiled aadc-ng engine the wheel
carries, recorded at build time from the SDK.

**They are allowed to differ**, and comparing them is not a health check: a
Python-only fix ships a new wheel against an unchanged engine. Record
`__version__` next to your risk numbers and `__engine_version__` when you need
to say which arithmetic produced them. Both are also available as
`aadc_ng.__version__` / `aadc_ng.__engine_version__`.

For what the loaded library says about itself — a third, independent source —
use `aadc.license_status()["build_version"]`; disagreeing with
`__engine_version__` means the wheel is carrying a different image than its
metadata claims.

## Check your entitlement in your own CI

```python
import aadc

s = aadc.license_status()
assert s["state"] == "licensed", s["detail"]
```

`state` is `unlicensed`, `licensed` or `expired`. **`expired` means the token
is genuine but this build is newer than its version ceiling** — the
entitlement is to older builds, which keep running at full speed. Compare
`version_max` against `build_version` to see the gap.

Token location, in order: `$AADC_NG_LICENSE`, `./aadc-ng.lic`,
`~/.aadc-ng/license`.

## One shared library per process

`libaadc-ng` carries thread-local recording state, so a second copy in one
process gives correct values and **silently zero gradients**. This wheel is the
only one that ships it; anything else linking it must resolve it by RPATH.
Assert it:

```python
import aadc_ng
aadc_ng.check_single_image()   # raises if more than one is mapped
```
