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)
¶
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.