Skip to content

Polynomial

Generated API reference for the polynomial engine types. Start with Polynomial engine for the concepts behind these objects, or Execution pipeline for how lazy graphs become scheduled backend work.

RingSpec dataclass

is_quotient_of(other)

Check if this ring is a quotient of another ring, or equivalently whether this ring's modulus divides the other ring's modulus.

Poly

Bases: OpMixin

__init__(data, ring, device=None)

Create a Poly from coefficient data.

Parameters:

Name Type Description Default
data list[int] | ndarray

Coefficient list or array of length ring.n

required
ring RingSpec

The ring specification

required
device str | None

Target device (optional, defaults to current device)

None

uniform_on_interval(ring, *, bound, rng=None) classmethod

Random coefficients uniformly distributed in [-bound, bound].

uniform(ring, *, rng=None) classmethod

Random coefficients uniformly distributed modulo q.

ternary(ring, rng=None) classmethod

Ternary coefficients {-1, 0, 1} with probabilities (1/4, 1/2, 1/4).

sparse_ternary(ring, *, rng=None, h) classmethod

Sparse ternary coefficients {-1, 0, 1} with exactly h +-1 coefficients and n-h 0 coefficients. The locations of the nonzero coefficients are chosen as a uniformly random size-h subset of {0, ..., n-1}, and each coefficient in those locations is independently set to -1 or 1 with probability 1/2.

rand(dist, ring, *, rng=None) classmethod

Create a polynomial with coefficients drawn from the given distribution.

const(value, ring, device=None) classmethod

Create a constant (degree-0) polynomial.

monomial(c, k, ring, device=None) classmethod

Create a monomial c * X^k.

to_ring(new_ring, *, residue=Residue.BALANCED)

Convert polynomial to a new ring. Backend picks the algorithm based on the relationship between source and target rings, while preserving the requested residue system when arithmetic lifting is required.

scale_by_primes(scalars)

Multiply each RNS limb by a per-prime scalar.

Unlike polynomial multiplication, this scales all coefficients in limb i by scalars[i]. This is used for embedding masks in keyswitching where different RNS primes need different scaling factors.

Mathematically, this multiplies by the ring element specified in factored coordinates of the product ring Z_q ≅ ∏Z_{pi}. The tuple (s0, s1, ..., sk-1) represents the unique S ∈ Z_q where S ≡ si (mod pi).

rescale(target_moduli)

Rescale into a lower-modulus ring by approximately applying the modulus ratio between the source and destination bases.

rotate(k)

Multiplies a polynomial by X^k

clone(device=None)

Return a value-equivalent polynomial backed by fresh storage.

contiguous()

Return a contiguous poly.

This should be called before accessing .buffer when view ops may be present.

realize(*lst)

Realize this polynomial and any additional ones together.

Triggers computation if needed. After calling realize(), nodes are updated to buffer-backed versions via apply_map_to_polys.

Parameters:

Name Type Description Default
*lst Poly

Additional Polys to realize together (enables combined scheduling).

()

Returns:

Type Description
Poly

self

data()

Return a zero-copy memoryview over the raw RNS limb bytes.

The layout is little-endian coefficient limbs in prime-major order with shape (len(ring.moduli), ring.n). The returned view is tied to the polynomial's buffer — do not mutate it.

Raises :class:RuntimeError for constant polynomials (use :meth:tobytes instead, or serialize via the "const" encoding).

tobytes()

Return raw RNS limb bytes for this polynomial (always copies).

The layout is little-endian coefficient limbs in prime-major order with shape (len(ring.moduli), ring.n). This exports the polynomial in its current domain.

frombytes(data, *, ring, domain=Domain.COEFF, device=None) classmethod

Build a polynomial from raw RNS limb bytes.

Accepts any bytes-like object. The underlying buffer is referenced without copying, so callers must not mutate or resize mutable inputs (for example bytearray or writable memoryview) while the resulting polynomial is still alive.

__reduce__()

Pickle as a materialized poly: realize and store concrete bytes (or a const value) plus ring/domain, never the lazy IR graph.

to_centered_list()

Reconstruct coefficients as the balanced [-Q/2, Q/2) representative.

This is the native output of Op.FROM_RNS. Reconstruction runs on the source device's backend (the GPU for cuda polys); only the resulting limb buffer is copied to host for the final big-int combine. tolist() derives [0, Q) from this.

tolist()

Reconstruct integer coefficients in [0, Q), derived from to_centered_list().

equals(other)

Check if two polynomials are equal.

This returns True if and only if the two polynomials have the same RingSpec and the same coefficients. (In particular it does not care about which domain the polys are in.)

Node dataclass

Bases: OpMixin

realized property

Return the Buffer if allocated, else None.

view_offset cached property

Element offset for view ops. None if not a view.

has_buffer_identity cached property

True if this node is backed by an accessible buffer (directly or through views).

base property

Get the underlying storage/computation node, unwrapping view ops.

const(val, dtype=dtypes.default_int, device=None) staticmethod

Create a CONST node.

Parameters:

Name Type Description Default
val int

The constant value (arbitrary precision integer)

required
dtype DType

Element dtype (dtypes.index for indexing, dtypes.default_int for data)

default_int

Per-limb modular reduction (val % p_i) happens at codegen, not here.

from_buffer(buf, ring=None, domain=None) staticmethod

Create a BUFFER node wrapping an existing allocated Buffer, optionally with (ring, domain) semantics attached.

Each call creates a fresh node; because the BUFFER key embeds a unique node it can never be shared, so it is constructed uncached instead of paying the intern cache's lookup and registration.

as_ring(ring, domain)

Attach ring semantics without changing storage.

Parameters:

Name Type Description Default
ring RingSpec

The ring specification

required
domain Domain

Domain (COEFF or NTT)

required

If already AS_RING, updates metadata instead of nesting.

sink(*src) staticmethod

Create a SINK node aggregating one or more outputs.

from_rns()

Reconstruct integer coefficients from RNS limbs as raw uint32 limbs.

Output is the balanced [-Q/2, Q/2) representative, encoded as signed two's-complement packed little-endian into limb_count = ceil(bitlen(Q)/32) uint32 words -- the same signed-limb encoding TO_RNS consumes, so the two ops are exact structural inverses.

index(idx)

Create INDEX node: self[idx].

load()

Create LOAD from this INDEX.

store(value)

Create STORE: store value at this location.

is_realized()

Check if this node is backed by an allocated buffer.

replace(**kwargs)

Create a new node with selectively replaced properties.

Any property not specified in kwargs is preserved from self.

topovisit(visitor, cache)

Topological visit with memoized results.

Computes visitor(node) once all children have been processed, storing results in the provided cache.

key()

Content-based structural hash.

substitute(mapping)

Replace nodes according to mapping in a single bottom-up pass.

bufferize()

Create a BUFFERIZE node capturing this node's materialization metadata.

contiguous()

Ensure this node has contiguous buffer access.

after(*deps)

Create an AFTER node: use this value after dependencies complete.

AFTER(value, *deps) passes through value but establishes ordering dependencies on deps. Used for: - Buffer dependencies: buffer.after(store) means "use buffer after store" - Parallel execution: independent ops without AFTER can run in parallel - Correct scheduling ordering on toposort

format_trace(lines=None)

Return a multi-line computation trace showing the full DAG.

Op

Bases: IntEnum