dau_core.fixed_point

Integer models of the fixed-point transcendental datapaths.

Two layers, and the distinction is the whole accuracy contract:

  • exp_reference() is the MATHEMATICAL answer – true exp correctly rounded to the fixed-point format. It is what “right” means.

  • ExpDatapath is an ALGORITHM: a specific range reduction, table and polynomial at a specific internal precision. The HDL mirrors it exactly, so the cocotb benches compare against it with zero tolerance, like every other tile in the package.

An operating point that deviates from the reference does so by a MEASURED amount, swept exhaustively over every representable input and recorded in the registry beside the resource envelope. Accuracy is a measured fact here, not a claim – sampling reported one configuration as flawless when an exhaustive sweep found three wrong inputs, so nothing is believed without the full sweep.

See dau-docs-private/design/transcendental-tiles.md for how the operating points were chosen.

Attributes

_ORACLE_PRECISION

EXP_FRACTIONAL_BITS

LN_FRACTIONAL_BITS

NORMAL_CDF_FRACTIONAL_BITS

Classes

ExpDatapath

One operating point of the exp datapath.

LnDatapath

One operating point of the ln datapath.

NormalCdfDatapath

One operating point of the normal-CDF datapath.

Functions

_decimal_context() → None

exp_reference(x, *[, fractional_bits]) → int

True exp of a Q(32-f).f value, correctly rounded to Q(32-f).f.

exp_domain(*[, fractional_bits]) → tuple[int, int]

The inputs where exp is neither 0 nor an overflow, inclusive.

_exp_constants(internal_bits, table_bits, degree, k_bits) → dict

ln_reference(x, *[, fractional_bits]) → int

True ln of a Q(32-f).f value, correctly rounded to Q(32-f).f.

_ln_constants(internal_bits, table_bits) → dict

normal_cdf_reference(x, *[, fractional_bits]) → int

The standard normal CDF of a Q(32-f).f value, correctly rounded.

_erfc(z) → decimal.Decimal

Complementary error function to the working precision.

_pi() → decimal.Decimal

Pi to the working precision (Chudnovsky is overkill; Machin suffices).

normal_cdf_domain(*[, fractional_bits]) → tuple[int, int]

The inputs where the CDF is neither 0 nor 1 to the last bit.

_normal_cdf_constants(internal_bits, table_bits, ...) → dict

Module Contents

dau_core.fixed_point._ORACLE_PRECISION = 80
dau_core.fixed_point.EXP_FRACTIONAL_BITS = 16
dau_core.fixed_point._decimal_context() → None[source]
dau_core.fixed_point.exp_reference(x: int, *, fractional_bits: int = EXP_FRACTIONAL_BITS) → int[source]

True exp of a Q(32-f).f value, correctly rounded to Q(32-f).f.

The mathematical contract. Computed in 80 digits, so the half-way cases that double precision cannot decide are decided here.

Raises when the result does not fit int32; the tile reports RANGE.

dau_core.fixed_point.exp_domain(*, fractional_bits: int = EXP_FRACTIONAL_BITS) → tuple[int, int][source]

The inputs where exp is neither 0 nor an overflow, inclusive.

Below the low bound the correctly rounded answer is 0; above the high bound it does not fit int32. Small enough to sweep exhaustively, which is what makes a correctness claim about this tile a proof rather than a sample.

class dau_core.fixed_point.ExpDatapath(/, **data: Any)[source]

Bases: dau_core.models.CheckedModel

One operating point of the exp datapath.

x is reduced as k/2**table_bits units of ln2 plus a residue, the table supplies 2**(j/2**table_bits), and a Horner polynomial of degree evaluates exp(residue) – all in internal_bits fractional bits.

round_steps rounds each Horner step instead of truncating it, which costs an adder per step in hardware. It buys accuracy only where the internal precision is the binding constraint; past that the polynomial degree binds instead and rounding changes nothing.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

internal_bits: int = 52
table_bits: int = 8
degree: int = 4
round_steps: bool = False
fractional_bits: int = 16
k_bits: int = 12
model_post_init(context) → None[source]

Override this method to perform additional initialization after __init__ and model_construct. This is useful if you want to do some validation that requires the entire model to be initialized.

property multiplies: int

the Horner chain, the table, and the two in the range reduction. This is what the DSP cost tracks.

Type:

Multiplies on the datapath

constants() → dict[source]
evaluate(x: int) → int[source]

The integer datapath, bit for bit as the HDL runs it.

dau_core.fixed_point._exp_constants(internal_bits: int, table_bits: int, degree: int, k_bits: int) → dict[source]
dau_core.fixed_point.LN_FRACTIONAL_BITS = 16
dau_core.fixed_point.ln_reference(x: int, *, fractional_bits: int = LN_FRACTIONAL_BITS) → int[source]

True ln of a Q(32-f).f value, correctly rounded to Q(32-f).f.

The mathematical contract, like exp_reference(). Computed in 80 digits so the half-way cases double precision cannot decide are decided here.

Raises on a non-positive input; the tile reports DOMAIN, the same position the sqrt tile takes – ln of zero or a negative has no value and a substituted one would be a wrong number that looks right.

class dau_core.fixed_point.LnDatapath(/, **data: Any)[source]

Bases: dau_core.models.CheckedModel

One operating point of the ln datapath.

x is normalised to m * 2**e with m in [1, 2), so ln(x) = e*ln2 + ln(m). The mantissa’s top table_bits index two tables – ln(1 + j/N) and the reciprocal 1/(1 + j/N) – reducing the residue to |v| <= 1/N before a short Horner series for ln(1+v). The reciprocal table is what keeps the polynomial degree low without a divider: a direct series on [0,1) would need many more terms.

The exponent term is EXACT integer work: e is a small signed integer and ln2 a constant, so the only approximation is the mantissa series.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

internal_bits: int = 52
table_bits: int = 8
degree: int = 3
round_steps: bool = False
fractional_bits: int = 16
model_post_init(context) → None[source]

Override this method to perform additional initialization after __init__ and model_construct. This is useful if you want to do some validation that requires the entire model to be initialized.

property multiplies: int

The reciprocal multiply, the Horner chain, and e*ln2.

constants() → dict[source]
evaluate(x: int) → int[source]

The integer datapath, bit for bit as the HDL will run it.

dau_core.fixed_point._ln_constants(internal_bits: int, table_bits: int) → dict[source]
dau_core.fixed_point.NORMAL_CDF_FRACTIONAL_BITS = 16
dau_core.fixed_point.normal_cdf_reference(x: int, *, fractional_bits: int = NORMAL_CDF_FRACTIONAL_BITS) → int[source]

The standard normal CDF of a Q(32-f).f value, correctly rounded.

TOTAL: every int32 input has an answer, so unlike ln and exp there is no error path. Beyond about +/-4.33 the result saturates to 0 or 1 exactly, which IS the correctly rounded answer rather than a clamp.

dau_core.fixed_point._erfc(z: decimal.Decimal) → decimal.Decimal[source]

Complementary error function to the working precision.

dau_core.fixed_point._pi() → decimal.Decimal[source]

Pi to the working precision (Chudnovsky is overkill; Machin suffices).

dau_core.fixed_point.normal_cdf_domain(*, fractional_bits: int = NORMAL_CDF_FRACTIONAL_BITS) → tuple[int, int][source]

The inputs where the CDF is neither 0 nor 1 to the last bit.

Small enough to sweep EXHAUSTIVELY, which is what makes a correctness claim about this tile a proof rather than a sample.

class dau_core.fixed_point.NormalCdfDatapath(/, **data: Any)[source]

Bases: dau_core.models.CheckedModel

One operating point of the normal-CDF datapath.

Folds on symmetry (the CDF of -x is one minus the CDF of x), so only the positive half is approximated, then evaluates a PER-SEGMENT Taylor polynomial: the magnitude’s top table_bits select a segment and the coefficients are the CDF’s derivatives at that segment, which have closed forms in the density. Piecewise rather than one polynomial because the CDF has no useful global series.

Saturation is exact rather than a clamp: beyond the domain the correctly rounded answer IS 0 or 1, so this tile is TOTAL and needs no error path – the one transcendental here that does not.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

internal_bits: int = 52
table_bits: int = 10
degree: int = 3
fractional_bits: int = 16
span_bits: int = 19
model_post_init(context) → None[source]

Override this method to perform additional initialization after __init__ and model_construct. This is useful if you want to do some validation that requires the entire model to be initialized.

property multiplies: int

The Horner chain; the symmetry fold and saturation are free.

constants() → dict[source]
evaluate(x: int) → int[source]

The integer datapath, bit for bit as the HDL will run it.

dau_core.fixed_point._normal_cdf_constants(internal_bits: int, table_bits: int, degree: int, fractional_bits: int, span_bits: int) → dict[source]