fhelium.engine.ckks_engine
CKKS operations for one configuration and local device.
CkksEngine
class View source
CkksEngine(ckks_config: CkksConfig | Preset | dict[str, object] | None=None, *, device: torch.device | str | None=None, allow_sk_gen: bool=True, galois_generator: int=3, ntt_backend: str | None=None, rng_seed: int | None=None, rng_nonce: int | None=None)CKKS execution facade for dense process-local values.
The engine fixes prime_ids; operations never infer hidden level or scale alignment.
ntt_backend is an exact engine execution-policy name. When omitted, CUDA engines select fhelium.DEFAULT_NTT_BACKEND and CPU engines select fhelium.DEFAULT_CPU_NTT_BACKEND. Engine construction does not benchmark hardware. A selected backend name must support both the device and ring dimension. The backend affects table layout and kernel execution, not CKKS context identity.
Construction creates one fhelium.rng.Csprng from the engine configuration and device. rng_seed and rng_nonce provide fixed stream material for controlled tests and benchmarks; applications should leave them unset so the generator obtains production entropy.
public_level_count
property
public_level_count: intNumber of ordinary public CKKS levels.
The count equals config.num_scale_primes. Public levels satisfy
final_public_level
property
final_public_level: intGreatest ordinary public CKKS level.
The value is public_level_count - 1. Its active Q basis contains the final scale prime and the structural base prime. Public next-level transitions require a source below this level. The bootstrap structural transition consumes a ciphertext at this level.
rng
property
rng: CsprngStable random-number generator shared by all engine components.
num_slots
property
num_slots: intSemantic CKKS slot count
create_secret_key
method
def create_secret_key(*, modulus_basis: ModulusBasis='QP') -> SecretKey: ...Sample a fresh secret polynomial without installing it.
Coefficients of {-1, 0, 1}, reduced over the complete level-zero Q or QP basis, and transformed to NTT-domain Montgomery residues. The result has layout [limb, ntt_index], engine integral dtype/device, final extent prime_ids. Generation allocates independent key storage and does not change the engine's installed key lifecycle.
create_public_key
method
def create_public_key(secret_key: SecretKey, *, modulus_basis: ModulusBasis='Q') -> PublicKey: ...Create a public encryption key for secret_key.
The two generated components satisfy
where [key_component=2, limb, ntt_index] in level-zero NTT-domain Montgomery form with engine integral dtype/device and exact prime_ids. The input secret key is not mutated and the public key is not installed on the engine.
create_relinearization_key
method
def create_relinearization_key(secret_key: SecretKey) -> RelinearizationKey: ...Create QP key-switch material from
The result replaces [key_digit, key_component=2, limb, ntt_index] in complete level-zero QP, NTT-domain Montgomery form on the engine device. It is returned without being installed; secret_key is not mutated.
create_rotation_key
method
def create_rotation_key(rotation_step: int, secret_key: SecretKey) -> RotationKey: ...Create QP key-switch material for one signed slot rotation.
For canonical step torch.roll(m, shifts=r). The result uses the full level-zero QP basis, NTT-domain Montgomery form, engine integral dtype/device, and independent storage; it is not installed automatically.
create_conjugation_key
method
def create_conjugation_key(secret_key: SecretKey) -> ConjugationKey: ...Create QP material from
The result has layout [key_digit, key_component=2, limb, ntt_index] in complete level-zero QP, NTT-domain Montgomery form with engine integral dtype/device and exact prime_ids. It enables semantic slot conjugation and is returned without installation; secret_key is not mutated.
create_key_switch_key
method
def create_key_switch_key(source_secret_key: SecretKey, destination_secret_key: SecretKey, *, uniform_component_by_key_digit: torch.Tensor | None=None) -> KeySwitchKey: ...Create QP material that switches source phases to destination phases.
The direction is
Both secret keys must be complete level-zero QP NTT/Montgomery values for this engine. The output layout is [key_digit, key_component=2, limb, ntt_index] with engine integral dtype/device and exact QP prime_ids. Optional uniform components are indexed by stable key_digit_index. Inputs are not mutated and the result is not installed.
secret_key
property
secret_key: SecretKeyReturn the installed secret key, generating one lazily if allowed.
set_secret_key
method
def set_secret_key(key: SecretKey) -> None: ...Install key and invalidate all dependent installed keys.
The key tensor is retained rather than cloned. Installed public, relinearization, and rotation keys are cleared because this value type does not carry a symbolic key-lineage identifier.
public_key
property
public_key: PublicKeyReturn or lazily generate the installed public encryption key.
set_public_key
method
def set_public_key(key: PublicKey) -> None: ...Validate and install key by reference without changing others.
relinearization_key
property
relinearization_key: RelinearizationKeyReturn or lazily generate installed
set_relinearization_key
method
def set_relinearization_key(key: RelinearizationKey) -> None: ...Validate and install QP relinearization material by reference.
rotation_keys
property
rotation_keys: RotationKeySetMutable installed mapping from canonical signed steps to keys.
rotation_key
method
def rotation_key(rotation_step: int) -> RotationKey: ...Return the matching installed key, generating it lazily if allowed.
set_rotation_key
method
def set_rotation_key(key: RotationKey) -> None: ...Install a key under its self-described canonical rotation step.
plaintext
method
def plaintext(message, *, level: int=0, scale=None) -> Plaintext: ...Create a lazy slots-only plaintext with an actual scale argument.
No encoding arithmetic is performed. The value stores semantic slots
Parameters
message: Tensor-like real or complex slot values.level: Public CKKS level at which a later encode will materialize the message.scale: Positive finite per-value scale. The context'sconfig.default_scaleis used only when this isNone.
Returns
A detached, cloned slots Plaintext on the engine device. It has no polynomial domain, modulus basis, residue form, or prime_ids and does not alias the caller's tensor.
Raises
InvalidScaleError: Ifscaleis not positive and finite.ValueError: If the requested level or message layout is invalid.
encode
method
def encode(message, *, level: int=0, scale=None) -> Plaintext: ...Encode slots at one programmer-selected level and actual scale.
For
Parameters
message: Tensor-like real or complex slot values.level: Public CKKS level for the encoded value.scale: Positive finite per-value scale, orNoneto useconfig.default_scale.
Returns
A new exact integer_coefficients plaintext with layout [*batch, coefficient], final extent prime_ids.
Raises
InvalidScaleError: Ifscaleis not positive and finite.ValueError: If the requested level or arithmetic state is invalid.
integer_coefficients_to_rns
method
def integer_coefficients_to_rns(plaintext: Plaintext, *, modulus_basis: ModulusBasis='Q') -> Plaintext: ...Reduce an integer polynomial to coefficient-domain standard RNS.
For every exact active prime_ids[i] with modulus
Input [*batch, coefficient] becomes [*batch, limb, coefficient] with engine integral dtype/device and final extent
The result has representation="rns", polynomial_domain="coefficient", and residue_representation="standard". Its level, scale, semantic polynomial, and exact active prime_ids are preserved. The input must be the integer_coefficients result of encode; this transition never performs encoding or CRT reconstruction implicitly.
prepare_plaintext_for_addition
method
def prepare_plaintext_for_addition(plaintext: Plaintext, *, modulus_basis: ModulusBasis='Q') -> Plaintext: ...Prepare coefficient-domain Montgomery RNS data for addition.
For each exact active prime_ids[i] with modulus
The result has layout [*batch, limb, coefficient], engine integral dtype/device, and final extent
The result's state is (representation="rns", polynomial_domain="coefficient", residue_representation="montgomery"). Level, scale, semantic polynomial, modulus basis, and exact prime_ids are preserved.
Semantically, this convenience operation is equivalent to
standard_residues_to_montgomery_residues( integer_coefficients_to_rns(plaintext)).
The implementation reuses its newly allocated intermediate storage.
prepare_plaintext_for_multiplication
method
def prepare_plaintext_for_multiplication(plaintext: Plaintext, *, modulus_basis: ModulusBasis='Q') -> Plaintext: ...Prepare NTT-domain Montgomery RNS data for multiplication.
For each exact active prime_ids[i] with modulus [*batch, coefficient] becomes [*batch, limb, ntt_index] with engine integral dtype/device and final extent
The result's state is (representation="rns", polynomial_domain="ntt", residue_representation="montgomery") with the selected Q or QP modulus_basis and its exact prime_ids.
Semantically, this convenience operation is equivalent to
coefficient_domain_to_ntt_domain( standard_residues_to_montgomery_residues( integer_coefficients_to_rns(plaintext))).
The implementation reuses its newly allocated intermediate storage.
decode
method
def decode(plaintext: Plaintext, *, is_real: bool=False): ...Decode a plaintext with its own actual per-value scale.
Slots input is encoded first at its stored scale. Otherwise the input is exact integral integer_coefficients or finite binary64 approximate_coefficients with layout [*batch, coefficient], final extent [*batch, slot] with extent is_real=True selects its real part.
encrypt
method
def encrypt(plaintext: Plaintext, public_key: PublicKey | None=None) -> Ciphertext: ...Encrypt slots or exact integer coefficients under a public key.
The output phase satisfies
Slots input is first encoded at its stored actual scale. Decode-only approximate_coefficients and RNS input are rejected. With configured encryption noise [component=2, *batch, limb, coefficient] ciphertext in coefficient domain and standard residues, over Q or QP according to the public key. It uses engine integral dtype/device, exact active prime_ids, unchanged level, and
decrypt
method
def decrypt(ct: Ciphertext, secret_key: SecretKey | None=None) -> Plaintext: ...Decrypt to bounded binary64 coefficients for decoding.
The RNS phase is
The current decrypt path reconstructs the centered class from the trailing Q-prime pair into a finite torch.float64[*batch, coefficient] tensor on the engine device. It is bounded approximate_coefficients for decoding, not an exact full-ct.level and decode. Inputs are unchanged and output storage is independent.
encrypt_message
method
def encrypt_message(message, public_key: PublicKey | None=None, *, level: int=0, scale=None) -> Ciphertext: ...Encode and encrypt slots at the requested level and actual scale.
First compute
then encrypt so that
Parameters
message: Tensor-like real or complex slot values.public_key: Compatible key, or the engine-installed/default key.level: Public CKKS level for the new ciphertext.scale: Positive finite per-value scale, orNoneto useconfig.default_scale.
Returns
A new two-component [component, *batch, limb, coefficient] ciphertext in coefficient domain and standard residues, with engine integral dtype/device, exact Q or QP prime_ids selected by the public key, unchanged level, and actual scale
Raises
InvalidScaleError: Ifscaleis not positive and finite.ValueError: If the message, level, key, or direct-encode range is invalid.
decrypt_message
method
def decrypt_message(ct: Ciphertext, secret_key: SecretKey | None=None, *, is_real: bool=False): ...Decrypt and decode using the ciphertext's actual scale.
Decryption uses the bounded approximate-coefficient reconstruction described by decrypt; it is not exact CRT. The result is CPU [*batch, slot] and is complex unless is_real=True. The ciphertext and secret key are unchanged.
coefficient_domain_to_ntt_domain
method
def coefficient_domain_to_ntt_domain(value: Ciphertext) -> Ciphertext: ...
def coefficient_domain_to_ntt_domain(value: Plaintext) -> Plaintext: ...2
Return an RNS value after its negacyclic forward NTT.
For every limb prime (coefficient, montgomery) to (ntt, montgomery). Ciphertext states are deliberately coupled: the transition is (coefficient, standard) to (ntt, montgomery) and includes standard-to-Montgomery conversion.
Layout [*batch, limb, N] for plaintext or [component, *batch, limb, N] for ciphertext, engine integral dtype/device, level, actual scale, component count, Q/QP basis, and exact prime_ids are preserved. The functional result has independent storage. Input already in NTT domain is rejected because this is a strict source-to-target transition. No CRT reconstruction occurs.
coefficient_domain_to_ntt_domain_
method
def coefficient_domain_to_ntt_domain_(value: Ciphertext) -> Ciphertext: ...
def coefficient_domain_to_ntt_domain_(value: Plaintext) -> Plaintext: ...2
Apply coefficient_domain_to_ntt_domain in place.
The payload tensor is transformed in its existing storage and polynomial_domain is updated; ciphertext also changes residue_representation to "montgomery". Aliases observe both payload and metadata mutation. Input must be in coefficient domain.
ntt_domain_to_coefficient_domain
method
def ntt_domain_to_coefficient_domain(value: Ciphertext) -> Ciphertext: ...
def ntt_domain_to_coefficient_domain(value: Plaintext) -> Plaintext: ...2
Return an RNS value after its normalized inverse NTT.
For every limb prime (ntt, montgomery) becomes (coefficient, montgomery). Ciphertext states are coupled: (ntt, montgomery) becomes (coefficient, standard) through inverse NTT and Montgomery reduction.
Tensor shape, engine integral dtype/device, level, actual scale, component count, Q/QP basis, and exact prime_ids are preserved. The functional result owns independent storage. Input already in coefficient domain is rejected because this is a strict source-to-target transition. It remains RNS; no CRT reconstruction occurs.
ntt_domain_to_coefficient_domain_
method
def ntt_domain_to_coefficient_domain_(value: Ciphertext) -> Ciphertext: ...
def ntt_domain_to_coefficient_domain_(value: Plaintext) -> Plaintext: ...2
Apply ntt_domain_to_coefficient_domain in place; result remains RNS.
The payload tensor is transformed in its existing storage and polynomial_domain is updated; ciphertext also changes residue_representation to "standard". Aliases observe payload and metadata mutation. Input must be in NTT domain.
standard_residues_to_montgomery_residues
method
def standard_residues_to_montgomery_residues(plaintext: Plaintext) -> Plaintext: ...Convert coefficient-domain standard RNS to Montgomery residues.
For limb prime [*batch, limb, coefficient] with engine integral dtype/device. Representation, level, actual scale, Q/QP basis, and exact prime_ids are preserved. The functional output has independent storage. Input already using Montgomery residues is rejected because this is a strict source-to-target transition.
standard_residues_to_montgomery_residues_
method
def standard_residues_to_montgomery_residues_(plaintext: Plaintext) -> Plaintext: ...Apply standard_residues_to_montgomery_residues in existing storage.
residue_representation becomes "montgomery" and aliases observe payload/metadata mutation. Input must use standard residues.
montgomery_residues_to_standard_residues
method
def montgomery_residues_to_standard_residues(plaintext: Plaintext) -> Plaintext: ...Convert coefficient-domain Montgomery RNS to standard residues.
For limb prime [*batch, limb, coefficient] with engine integral dtype/device. Representation, level, actual scale, Q/QP basis, and exact prime_ids are preserved. The functional output has independent storage. Input already using standard residues is rejected because this is a strict source-to-target transition.
montgomery_residues_to_standard_residues_
method
def montgomery_residues_to_standard_residues_(plaintext: Plaintext) -> Plaintext: ...Apply montgomery_residues_to_standard_residues in existing storage.
residue_representation becomes "standard" and aliases observe payload/metadata mutation. Input must use Montgomery residues.
rescale_to_next_drop_prime
method
def rescale_to_next_drop_prime(*, level: int) -> int: ...Return the Q prime used by one rescale-to-next transition.
For source level rescale_to_next_level divides by this integer and removes its residue row.
Parameters
level: Current public CKKS level. It must have a following public level, so the final legal public level is not accepted.
Returns
The canonical leading active Q modulus at level.
Raises
TypeError: Iflevelis not an integer.ValueError: Iflevelis negative.MaximumLevelError: If no further public rescale level exists.
rescale_to_next_output_scale
method
def rescale_to_next_output_scale(input_scale: float, *, level: int) -> float: ...Calculate the binary64 output scale of one rescale transition.
The result is the same binary64 calculation used by rescale_to_next_level:
Parameters
input_scale: Positive finite scale immediately before rescale.level: Current public CKKS level.
Returns
The positive finite binary64 scale after dropping the level's leading active Q modulus.
Raises
InvalidScaleError: Ifinput_scaleor the resulting quotient is not a positive finite binary64 value.TypeError: Iflevelis not an integer.ValueError: Iflevelis negative.MaximumLevelError: If no further public rescale level exists.
rescale_to_next_level
method
def rescale_to_next_level(ct: Ciphertext, *, rounding: Literal['nearest', 'floor']='nearest') -> Ciphertext: ...Rescale a ciphertext to the next public CKKS level.
For each component polynomial,
For Q input, rounding="nearest" selects nearest-integer quotient and rounding="floor" subtracts the least nonnegative dropped residue before exact division.
Parameters
ct: Full-layout coefficient-domain, standard-residue ciphertext at a non-final public level. Two- and three-component Q or QP values are accepted.rounding: Quotient rule, either"nearest"or"floor".
Returns
A new coefficient-domain standard ciphertext at ct.level + 1 with the same two/three component count and Q/QP basis, engine integral dtype/device, exact prime_ids=ct.prime_ids[1:], and canonical residues in ct is unchanged and output storage does not alias it.
Raises
MaximumLevelError: Ifctis already at the final public level.InvalidScaleError: If the output scale is not positive and finite.ValueError: If the ciphertext state or engine layout is invalid.
rescale_to_next_level_
method
def rescale_to_next_level_(ct: Ciphertext, *, rounding: Literal['nearest', 'floor']='nearest') -> Ciphertext: ...Advance one public level and update ct in place.
Native kernels update the remaining RNS rows through views into the original allocation. The method then narrows ct.data and updates level, prime_ids, and the actual per-value scale.
The mathematical quotient, scale transition, Q/QP row selection, and canonical standard-residue output are identical to rescale_to_next_level. Only storage ownership differs.
Parameters
ct: Full-layout coefficient-domain, standard-residue ciphertext at a non-final public level. Aliases observe the mutation.rounding: Quotient rule, either"nearest"or"floor".
Returns
ct itself after the rescale transition.
Raises
MaximumLevelError: Ifctis already at the final public level.InvalidScaleError: If the output scale is not positive and finite.ValueError: If the ciphertext state or engine layout is invalid.
mod_switch_to_next_level
method
def mod_switch_to_next_level(ct: Ciphertext) -> Ciphertext: ...Restrict a ciphertext to the next public RNS basis.
For every component,
This transition drops the leading Q row, preserves coefficient representatives and scale, and retains all P rows for QP input. Message preservation requires the represented centered value to remain within the smaller modulus.
Parameters
ct: Full-layout ciphertext at a non-final public CKKS level. Its component count, polynomial domain, representation, and Q/QP basis are preserved.
Returns
A new ciphertext at ct.level + 1 with unchanged component count, domain, basis, residue form, dtype/device, actual scale, and prime_ids=ct.prime_ids[1:]. ct is unchanged and storage is independent.
Raises
MaximumLevelError: Ifctis already at the final public level.ValueError: Ifctis incompatible with this engine or does not contain the complete active RNS layout.
mod_switch_to_next_level_
method
def mod_switch_to_next_level_(ct: Ciphertext) -> Ciphertext: ...Restrict ct to the next public RNS basis in place.
The mathematical RNS restriction and no-wrap condition are identical to mod_switch_to_next_level. ct.data is narrowed to a view of its existing allocation; level and exact prime_ids change, while actual scale and all other state axes are preserved. Aliases observe metadata mutation and retained storage rows.
Parameters
ct: Full-layout ciphertext at a non-final public level. Aliases observe its narrowed tensor and updated level metadata.
Returns
ct itself with unchanged scale and one fewer active Q row.
Raises
MaximumLevelError: Ifctis already at the final public level.ValueError: Ifctis incompatible with this engine or does not contain the complete active RNS layout.
mod_switch_to_level
method
def mod_switch_to_level(ct: Ciphertext, target_level: int) -> Ciphertext: ...Restrict a ciphertext to the RNS basis at target_level.
For target level
The transition drops the first
Parameters
ct: Full-layout ciphertext whose current level is no later thantarget_level.target_level: Destination public level in the inclusive range[ct.level, final_public_level].
Returns
A new ciphertext at target_level with unchanged component count, domain, Q/QP basis, residue form, dtype/device, and actual scale. Exact prime_ids are restricted accordingly. Passing the current level returns a full clone; output never aliases ct.
Raises
TypeError: Iftarget_levelis not an integer.ValueError: If the target is earlier thanct.level, beyond the final public level, or the ciphertext layout is incompatible.
mod_switch_to_level_
method
def mod_switch_to_level_(ct: Ciphertext, target_level: int) -> Ciphertext: ...In-place form of mod_switch_to_level.
The same RNS restriction and no-wrap condition apply. The narrowed tensor remains a view into the original allocation; level and exact prime_ids change while actual scale and other state axes are preserved. Aliases observe mutation.
Parameters
ct: Full-layout ciphertext to mutate.target_level: Destination public level in the inclusive range[ct.level, final_public_level].
Returns
ct itself with unchanged scale. Passing its current level is a no-op.
Raises
TypeError: Iftarget_levelis not an integer.ValueError: If the target or ciphertext layout is invalid.
reinterpret_at_scale
method
def reinterpret_at_scale(ct: Ciphertext, target_scale: float, *, max_relative_change: float | None=None) -> Ciphertext: ...Return a metadata-only reinterpretation at target_scale.
Ciphertext residues are not modified. Consequently the decoded message obeys
When supplied, max_relative_change bounds the symmetric ratio between the current and target scales.
Parameters
ct: Ciphertext whose payload and original metadata remain unchanged.target_scale: Positive finite scale used to reinterpret the same residues.max_relative_change: Optional upper bound on the symmetric ratio .
Returns
A new ciphertext with cloned payload, independent storage, and target_scale. Level, component count, domain, basis, residue form, dtype/device, and exact prime_ids are unchanged.
Raises
InvalidScaleError: Iftarget_scaleis invalid.ScaleMismatchError: If the provided relative-change bound is exceeded.ValueError: If the bound itself or ciphertext state is invalid.
reinterpret_at_scale_
method
def reinterpret_at_scale_(ct: Ciphertext, target_scale: float, *, max_relative_change: float | None=None) -> Ciphertext: ...Reinterpret ct at target_scale in place.
The equations and guard are identical to reinterpret_at_scale. Only ct.scale is mutated; payload storage and every other state axis remain unchanged. Aliases to the object observe the metadata change.
Parameters
ct: Ciphertext whose scale metadata is mutated; residues are not.target_scale: Positive finite replacement scale.max_relative_change: Optional symmetric relative-change bound.
Returns
ct itself after changing only its scale metadata.
Raises
InvalidScaleError: Iftarget_scaleis invalid.ScaleMismatchError: If the provided relative-change bound is exceeded.ValueError: If the bound itself or ciphertext state is invalid.
zero_plaintext_like
method
def zero_plaintext_like(plaintext: Plaintext) -> Plaintext: ...Construct semantic zero in the same exact plaintext state.
Slots, integer coefficients, approximate coefficients, or RNS payloads are materialized as zero with matching batch shape, level, actual scale, representation, domain, Q/QP basis, residue form, and exact prime_ids. The result uses the engine-compatible dtype/device for newly encoded storage and owns independent storage. The input is not mutated.
encrypt_zero_like
method
def encrypt_zero_like(ct: Ciphertext, public_key: PublicKey | None=None) -> Ciphertext: ...Create a randomized secure encryption of semantic zero.
The output phase satisfies ct's batch shape, level, actual scale, coefficient/NTT domain, Q/QP basis, standard/Montgomery form, engine dtype/device, and exact prime_ids. The supplied public key must select the same basis. Neither input nor key is mutated and no storage aliases them.
multiply
method
def multiply(lhs: Ciphertext, rhs: Ciphertext) -> Ciphertext: ...Multiply two NTT ciphertexts without relinearization or rescale.
Both inputs must be two-component NTT values, and the output is a three-component NTT value. Programs call relinearize and rescale_to_next_level as separate calls. This also makes communication required by a limb-parallel implementation visible.
For
with
Parameters
lhs: Two-component NTT/Montgomery ciphertext.rhs: Layout-compatible two-component NTT/Montgomery ciphertext. Its scale may differ fromlhs.scale.
Returns
A new three-component NTT/Montgomery ciphertext at unchanged level and Q/QP basis, with engine integral dtype/device, the exact shared prime_ids, the operands' batch shape, and product actual scale. Inputs are unchanged and output storage is independent.
Raises
InvalidScaleError: If the binary64 product scale is not positive and finite.ValueError: If either input state or the shared layout is invalid.
relinearize
method
def relinearize(ct: Ciphertext, relinearization_key: RelinearizationKey | None=None) -> Ciphertext: ...Key-switch a three-component product back to two components.
Relinearization transforms the phase relation
using QP material directed from
Parameters
ct: Three-component Q-basis NTT/Montgomery ciphertext.relinearization_key: Compatible QP key. The engine-installed key is used when this argument is omitted.
Returns
A new two-component coefficient-domain standard ciphertext with Q basis, the same batch shape, engine integral dtype/device, exact active Q prime_ids, level, and actual scale as ct. Inputs are unchanged and output storage is independent.
Raises
ValueError: If the ciphertext or key state is incompatible.
switch_key
method
def switch_key(ct: Ciphertext, key: KeySwitchKey) -> Ciphertext: ...Switch a two-component ciphertext from source to destination key.
key must have been generated for the declared direction
The key object does not self-identify these lineages; the caller owns that invariant. The represented message, level, and actual scale are preserved up to key-switch error. Input must be coefficient-domain standard, two-component, full-layout Q RNS. Output is a new Q value with unchanged batch shape, engine dtype/device, and exact active Q prime_ids; inputs are not mutated and storage is independent.
add
method
def add(lhs: Ciphertext, rhs: Ciphertext, *, inplace: bool=False) -> Ciphertext: ...Add ciphertexts with exactly equal scale and arithmetic state.
The method performs no level alignment, scale reinterpretation, relinearization, or domain conversion.
Parameters
lhs: Left ciphertext and, wheninplaceis true, destination.rhs: Ciphertext with identical layout, shape, and binary64 scale.inplace: Mutate and returnlhswhen true; otherwise return a new ciphertext and leave both inputs unchanged.
Returns
The sum with unchanged component count, level, domain, Q/QP basis, residue form, engine dtype/device, and exact prime_ids. The functional result owns independent storage; inplace=True mutates and returns lhs so aliases observe canonicalized payload changes.
Raises
ScaleMismatchError: Iflhs.scale != rhs.scale.ValueError: If context, level, state, shape, or RNS layout differs.
add_
method
def add_(lhs: Ciphertext, rhs: Ciphertext) -> Ciphertext: ...Add rhs to lhs in place with add's state requirements.
Only lhs payload storage is mutated; all metadata is preserved and aliases of lhs observe the component-wise modular sum.
sum_ciphertexts
method
def sum_ciphertexts(ciphertexts: Sequence[Ciphertext]) -> Ciphertext: ...Return the rank-local sum of compatible ciphertexts.
For each component
with one exactly shared actual scale. Every state axis, batch shape, dtype/device, and exact prime_ids must match.
Parameters
ciphertexts: Non-empty sequence satisfyingadd's exact layout and exact-scale requirements.
Returns
A new ciphertext. Every input remains unchanged.
Raises
ScaleMismatchError: If any scale differs from the first value.ValueError: If the sequence is empty or another value is layout incompatible.
sum_ciphertext_batch
method
def sum_ciphertext_batch(batch: Ciphertext, *, dim: int=0) -> Ciphertext: ...Reduce one logical batch axis by modular ciphertext addition.
A binary tree adds contiguous halves through the native batched RNS kernels. This exposes the complete remaining batch, component, limb, and polynomial-index work to each round instead of launching one addition per selected value. Odd tails are folded after the batched rounds.
Parameters
batch: Ciphertext with at least one logical batch axis.dim: Logical batch axis to remove. Ciphertext component and RNS axes are not addressable through this argument.
Returns
A new ciphertext with the selected batch axis removed. Level, scale, polynomial domain, modulus basis, residue representation, component count, device, dtype, and exact prime_ids are preserved. The input is unchanged.
Raises
IndexError: Ifdimdoes not identify a logical batch axis.ValueError: Ifbatchis unbatched or incompatible with this engine.
subtract
method
def subtract(lhs: Ciphertext, rhs: Ciphertext, *, inplace: bool=False) -> Ciphertext: ...Subtract ciphertexts with exactly equal scale and state.
Parameters
lhs: Minuend and, wheninplaceis true, destination.rhs: Subtrahend with identical layout, shape, and binary64 scale.inplace: Mutate and returnlhswhen true.
Returns
The difference with unchanged component count, level, domain, Q/QP basis, residue form, engine dtype/device, and exact prime_ids. The functional result owns independent storage; inplace=True mutates and returns lhs.
Raises
ScaleMismatchError: Iflhs.scale != rhs.scale.ValueError: If context, level, state, shape, or RNS layout differs.
subtract_
method
def subtract_(lhs: Ciphertext, rhs: Ciphertext) -> Ciphertext: ...Subtract rhs from lhs in place under subtract.
Only lhs payload storage is mutated; all metadata is preserved and aliases observe the component-wise modular difference.
negate
method
def negate(ct: Ciphertext, *, inplace: bool=False) -> Ciphertext: ...Negate a ciphertext without changing its state metadata.
Component count, level, domain, Q/QP basis, residue form, dtype/device, and exact prime_ids are preserved. The functional result owns independent storage; inplace=True mutates canonicalized residues in ct and aliases observe the change.
negate_
method
def negate_(ct: Ciphertext) -> Ciphertext: ...Negate ct in place with negate's state requirements.
add_plaintext
method
def add_plaintext(ct: Ciphertext, plaintext: Plaintext | CompressedPlaintext, *, inplace: bool=False) -> Ciphertext: ...Add an operation-ready plaintext at exactly the ciphertext scale.
No scale or level alignment is implicit.
Parameters
ct: Two-component coefficient-domain standard ciphertext.plaintext: Coefficient-domain RNSPlaintextor compatible compressed form at the same level, basis, rows, and exact binary64 scale. A batched plaintext must matchct.batch_shapeexactly; an unbatched plaintext broadcasts over every ciphertext batch entry.inplace: Update onlyct.c0and returnctwhen true.
Returns
A two-component coefficient-domain standard ciphertext with unchanged level, Q/QP basis, batch shape, engine dtype/device, exact prime_ids, and shared actual scale. Functional mode allocates independent output; inplace=True mutates only ct.c0 and aliases observe the change.
Raises
ScaleMismatchError: If the two scale values are not exactly equal.ValueError: If the plaintext or ciphertext arithmetic state, context, layout, device, or batch shape is incompatible.
add_plaintext_
method
def add_plaintext_(ct: Ciphertext, plaintext: Plaintext | CompressedPlaintext) -> Ciphertext: ...Mutate ct.c0 with add_plaintext's exact input requirements.
multiply_plaintext
method
def multiply_plaintext(ct: Ciphertext, plaintext: Plaintext | CompressedPlaintext, *, inplace: bool=False) -> Ciphertext: ...Multiply an NTT ciphertext by an operation-ready plaintext.
The operands need not have equal scales. This operation preserves the ciphertext level and records their binary64 scale product.
Both operands remain in NTT/Montgomery representation. Callers place coefficient_domain_to_ntt_domain before a multiplication region and ntt_domain_to_coefficient_domain before an operation that requires coefficient-domain standard residues, such as rescale.
Parameters
ct: Two-component NTT-domain Montgomery ciphertext.plaintext: NTT/Montgomery RNSPlaintextor compatible compressed form at the same context, level, basis, rows, and batch layout. A batched plaintext must matchct.batch_shapeexactly; an unbatched plaintext broadcasts over every ciphertext batch entry.inplace: Replacectwith the result and return it when true.
Returns
A new two-component NTT-domain Montgomery ciphertext at unchanged level and Q/QP basis, with the ciphertext batch shape, engine integral dtype/device, exact prime_ids, and product actual scale. Functional mode leaves inputs unchanged and owns independent storage; inplace=True replaces all ct state with the new storage, so old tensor aliases keep the old allocation.
Raises
InvalidScaleError: If the product scale is not positive and finite.ValueError: If the plaintext or ciphertext arithmetic state, context, layout, device, or batch shape is incompatible.
multiply_plaintext_
method
def multiply_plaintext_(ct: Ciphertext, plaintext: Plaintext | CompressedPlaintext) -> Ciphertext: ...Replace ct by its product under multiply_plaintext.
rotate_by_step
method
def rotate_by_step(ct: Ciphertext, rotation_step: int) -> Ciphertext: ...Rotate by one signed step using the engine-owned key inventory.
For
The step is canonicalized modulo rotate_with_key when the caller owns the exact direct key.
rotate_with_key
method
def rotate_with_key(ct: Ciphertext, key: RotationKey) -> Ciphertext: ...Rotate slots using the canonical signed step carried by key.
For
matching torch.roll(m, shifts=r). The backend applies the Galois automorphism key in the direction rotation_step is a slot displacement and is not the Galois element.
Input must be a two-component coefficient-domain standard full-layout Q ciphertext. Output is a new Q ciphertext with unchanged batch shape, level, actual scale, engine integral dtype/device, and exact active Q prime_ids; key switching adds its configured approximation error. Inputs are unchanged and output storage is independent. Step zero returns a clone without automorphism or key switch.
rotate_many_by_steps
method
def rotate_many_by_steps(ct: Ciphertext, rotation_steps: Sequence[int], *, use_hoisting: bool=True) -> list[Ciphertext]: ...Rotate by ordered signed steps using engine-owned keys.
Output order matches rotation_steps. Every result owns independent storage, including duplicate and zero-step entries. Direct-key paths share hybrid decomposition when use_hoisting is true; a step that requires a composed key path is evaluated independently.
rotate_many_with_keys
method
def rotate_many_with_keys(ct: Ciphertext, rotation_keys: Sequence[RotationKey], *, use_hoisting: bool=True) -> list[Ciphertext]: ...Rotate once for each ordered caller-owned direct key.
Every key self-describes its canonical signed step. Output order and duplicates match rotation_keys; results own independent storage. The keys are not installed into the engine.
rotate_by_step_
method
def rotate_by_step_(ct: Ciphertext, rotation_step: int) -> Ciphertext: ...Replace ct by rotate_by_step's result.
The result satisfies ct.data is replaced by newly allocated rotated/key-switched storage; aliases to the old tensor retain the old allocation, while aliases to the object observe all replaced state.
conjugate
method
def conjugate(ct: Ciphertext, key: ConjugationKey) -> Ciphertext: ...Apply complex conjugation to every semantic CKKS slot.
The backend applies key in the direction prime_ids, up to key-switch error. Inputs are unchanged and output storage is independent.
validate_ciphertext
method
def validate_ciphertext(ciphertext: Ciphertext) -> None: ...Validate a complete dense ciphertext against this engine.
The check re-runs the value's mutable-storage invariants and requires the exact context, ring dimension, dtype, device, public level, modulus basis, and complete active prime_ids expected by ordinary engine operations. It performs no evaluator work and does not mutate the ciphertext.
validate_public_key
method
def validate_public_key(key: PublicKey) -> None: ...Validate public encryption material against this engine.
The check re-runs the key's mutable-storage invariants and requires the exact context, ring dimension, dtype, device, polynomial domain, residue representation, and prime layout consumed by online encryption. The key is not installed or mutated.
validate_key_switch_key
method
def validate_key_switch_key(key: KeySwitchKey) -> None: ...Validate complete QP key-switch material against this engine.
The concrete key type is preserved while context, ring dimension, dtype, device, NTT/Montgomery state, QP prime layout, and hybrid digit count are checked. The key is not installed or mutated.