Skip to main content

← HomOps Reference

HomPolyEval

Fit and evaluate a function approximation on an encrypted value.

PePolynomials
RunsServer
Changes shapeNo
Changes scaleYes - grows through the evaluation
Levels spentSeveral - grows with the degree
RotatesNo
KeysSquare key

What it does​

HomPolyEval is a helper that fits a Chebyshev polynomial to a callable func at a chosen degree, then evaluates that approximation on every ciphertext element. This is how smooth nonlinearities run on encrypted data.
The evaluation uses a baby-step / giant-step schedule, so a degree-d polynomial costs far fewer multiplications than d, but it is still deep: expect it to spend several levels, more for higher degrees.
For already-computed Chebyshev coefficients, use the registered leaf HomPolyEvalBase instead.
ClassWhat it approximates
HomPolyIndicatorAn indicator: 1 near a target value, 0 elsewhere. Used to select matches in search pipelines.
HomPolyThresholdA step: 0 below a margin, 1 above it. Variants sigmoid, piecewise_linear, minimax.
Both construct polynomial approximations with their coefficients computed for you from the degree and shaping parameters.

Signature​

HomPolyEval(
    func,               # required: callable to approximate on the domain
    degree,             # required: Chebyshev degree
    left=-1,
    right=1,
    tol=1e-8,
    plot=False,
    rows_budget=None,
)
The variants take the degree instead of coefficients:
HomPolyIndicator(deg, right=4, tol=1e-8, rows_budget=None)

HomPolyThreshold(deg, variant='sigmoid', margin=(0.4, 0.6),
                 tol=1e-5, out_val=1, domain_start=-1, rows_budget=None)
No set_data; this helper fits coefficients at construction. Use HomPolyEvalBase when you need to supply coefficients directly.

Parameters​

ParameterTypeDefaultDescription
funccallablerequiredFunction to approximate on the configured domain.
degreeintrequiredDegree of the Chebyshev approximation fitted to func.
left, rightint-1, 1Domain passed to the fit and evaluation.
tolfloat1e-8Skip near-identity rescale factors while evaluating.
plotboolFalseShow a debug plot when fitting the approximation.
rows_budgetsequenceNoneRestricts which levels the internal mod-switches may spend, by absolute row index. See rows_budget.
HomPolyThreshold adds: variant (the curve family), margin (where the step happens), out_val (the high value), domain_start.
Rules that depend on the value
SettingRuleIf you break it
left, rightThe actual input values must lie inside [left, right]. The polynomial is only a valid approximation on its domain; values outside it produce garbage, silently.Wrong results, not an error. Check the range your previous ops can produce.
left, rightA domain other than [-1, 1] adds an affine remap step before evaluation, which can cost one extra mod-switch.-
rows_budgetAt least one listed row must be droppable at each internal mod-switch.Compilation fails if none are eligible.

Requirements​

  • Input in domain. The single most important rule for this operator: whatever arrives must fit in [left, right]. This is a numerical requirement the compiler cannot check for you.
  • Square key. The internal Chebyshev multiplies are ciphertext multiplies; any pipeline with a HomPolyEval needs the square key (generated for you during key generation).
  • Level headroom. The deepest operator here. Budget several levels and verify with the compile-time simulation before deploying; see Level budget.

Shape effect - no​

The output shape equals the input shape; the polynomial is applied element by element.

Scale effect - yes​

Scale grows through the evaluation and is paid down by the internal mod-switches as it goes; the final scale depends on the degree and the mod-switch schedule. You don't manage this step by step. What you do is check the compiled result: the pipeline's compile-time simulation reports the output scale, and that is the number to plan the next operator around.
How scale moves through a pipeline is on Ciphertext state - Scale.

Level budget​

HomPolyEval spends levels at several points inside:
PhaseLevels
Domain remap to [-1, 1]0 or 1, only when [left, right] is not [-1, 1]
The evaluation itself (baby steps, giant steps, recursion)Several; grows with the degree
The exact count for a given degree is settled at compile time; verify it with the compile-time simulation rather than estimating by hand. Optionally pin drops with rows_budget:
pipeline.pyPYTHON
HomPolyIndicator(deg=80, rows_budget=[0, 1, 3, 4, 5, 6])
How primes are organized in the chain is explained on Level budget - The chain.

Keys​

Square key (relinearization key). The Chebyshev evaluation multiplies ciphertexts by ciphertexts, so every multiply is followed by a relinearization. Generated for you during key generation. No rotation key.

Example​

Threshold in a search pipeline​

After inner products, turn similarity scores into a 0/1 selection:
pipeline.pyPYTHON
from lattica_build.operators import HomPolyThreshold
from lattica_build.base_classes.hom_pipeline import HomomorphicPipeline
from lattica_build.operators.composite.sequential import SequentialHomOp

ROWS_BUDGET = [0, 1, 3, 4, 5, 6]

pipeline = HomomorphicPipeline(
    hom=SequentialHomOp(
        HomPolyThreshold(deg=80, variant='sigmoid', rows_budget=ROWS_BUDGET),
    ),
    input_shape=(128,),
)
The other variants differ only in the curve:
pipeline.pyPYTHON
HomPolyThreshold(deg=80, variant='piecewise_linear', rows_budget=ROWS_BUDGET)

HomPolyThreshold(deg=80, variant='minimax',
                 margin=(0.45, 0.9), out_val=1, domain_start=-0.5,
                 rows_budget=ROWS_BUDGET)

Custom coefficients​

For precomputed Chebyshev coefficients, construct HomPolyEvalBase:
pipeline.pyPYTHON
import numpy as np
from lattica_build.operators import HomPolyEval, HomPolyEvalBase
import torch

# Helper: fit Chebyshev coeffs from a callable
HomPolyEval(lambda x: np.maximum(x, 0), degree=16, left=-1, right=1)

# Leaf: supply coeffs directly via HomPolyEvalBase
coefs = torch.randn(64)          # degree 63
HomPolyEvalBase(coefs, left=-2, right=2)
The inputs must genuinely lie in [-2, 2] for the result to be meaningful.

See also​