Skip to content

Updates to v1.6.1.0 - #294

Merged
dsuponitskiy merged 2 commits into
mainfrom
dev
Oct 27, 2026
Merged

dsuponitskiy merged 2 commits into
mainfrom
dev

Conversation

@yspolyakov

Copy link
Copy Markdown
Contributor

No description provided.

dsuponitskiy and others added 2 commits September 29, 2026 11:45
* Expose automorphism key indices as a Python list
- Add GetExistingEvalAutomorphismKeyIndices to query generated keys by tag.
- Document its behavior, demonstrate bootstrapping key counts, and add
  tests for rotation and bootstrapping keys.

* Fix automorphism index bindings to accept signed rotation offsets
- Accept signed 32-bit offsets in FindAutomorphismIndex and FindAutomorphismIndices, converting them to the C++ API's unsigned form.
- Update docstrings and add CKKS coverage for negative offsets, empty lists, and out-of-range inputs.

---------

Co-authored-by: Dmitriy Suponitskiy <dsuponitskiy@dualitytech.com>
* Add Python bindings and examples for OpenFHE 1.6

- Port the missing PKE and BinFHE examples and expose the APIs they need, including functional bootstrapping,
  BinFHE public-key support, and serialization.
- Move the functional-bootstrapping bindings into fbt.cpp and extend the example tests
  to check the complete v1.6 inventory.

* Fix plaintext modulus value in depth-bgvrns.py

* Run each example test in a subprocess to avoid "out of memory" error

The binfhe examples keep their cryptocontexts and bootstrapping keys in module-level globals (1-2.5 GB each),
so running all of them in one pytest process accumulates tens of GB and gets pytest killed on the runner.
Running every example in its own interpreter returns the memory to the OS after each test.

* Add 128-bit guards to CKKS examples

The examples added for 1.6.0 hard-coded FLEXIBLEAUTO scaling, which 128-bit CKKS rejects. Pick FIXEDAUTO and
matching modulus sizes when get_native_int() is 128, the same way the older examples do, and skip the composite scaling
and functional bootstrapping examples on 128-bit builds since those techniques only exist for 64-bit.

* Move CKKS depth APIs to CryptoContext and do not expose the FHECKKSRNS class to Python

* Expose the API-coverage backlog to Python

- Fix bind_eval_multinplace_scalar_overloads, which registered EvalAddInPlace instead of EvalMultInPlace.
  The scalar EvalAddInPlace overloads are bound elsewhere, so nothing is lost.
- Bind PrivateKey(cryptoContext). RecoverSharedKey() dereferences sk->GetCryptoContext(),
  so a default-constructed key crashed it.
- Add CryptoContext.GetElementParams(), needed to call GetPlaintextForDecrypt().
- Add the CryptoContext overloads of SerializeEvalSumKey and check is_open(),
  so an unusable path returns False instead of a cereal error.
- Add tests/test_api_coverage.py, which checks decrypted values rather than method presence.

* Expose the BinFHE API-coverage backlog to Python

Bind missing BinFHE APIs, plus LWESwitchingKey which KeySwitchGen returns and SwitchCTtoqn consumes.
- Drop the LWEPrivateKey default constructor. BinFHEContext only checks for a null key, so an empty one segfaulted
  BTKeyGen, PubKeyGen and KeySwitchGen. Keys come from KeyGen(), KeyGenN() or deserialization.
- Move the integer conversions into integer_conversions.h, replacing the copies in bindings.cpp, binfhe_bindings.cpp and
  fbt.cpp.
- Widen LWECiphertext.GetModulus(), which truncated for NATIVE_INT=128.
- Add tests/test_binfhe_api_coverage.py.

* Document the new bindings and fix the existing docstrings

- Add docstrings for the 79 bindings added by the two API-coverage commits, taken from the doxygen comments
  in openfhe-development.
- Move the remaining docstrings out of the .cpp files into *_docs.h.  fbt.cpp gets its own header, since these hold
  external-linkage globals.
- Fix stale parameter names (keyId -> keyTag, i -> index and others), and add or drop the :param: and :return: entries
  that no longer matched.
- Indent the 20 docstrings that were flush left.

* Fix binding and build bugs, remove dead code, and list all examples in README

- Fix MakeCoefPackedPlaintext kwarg bound as "noiseScaleDeg " (trailing space)
- Return false early when EvalMultKey/EvalAutomorphismKey files cannot be opened
- Make the GenerateLUTviaFunction callback exception-safe via thread_local + guard
- Remove BinFHE bindings that could never be called (cereal save/load, getters returning unregistered types) and
  the unused PKE serialization wrappers/header
- Replace manual enum re-export blocks with py::enum_::export_values()
- Add missing py::arg names (CCParams/SchSwchParams setters, keys, plaintext, ciphertext)
- Select Python before pybind11 in CMake and install to sysconfig platlib
- Add regression tests; list all 59 examples in the README with fixed links

* Fix various bugs:

- Reject negative, oversized, and bool Python ints in PyIntToInteger
- Fix and enable simple_bootstrap_stc_example()
- Remove the unsafe default LWEPublicKey constructor
- Check is_open() in the EvalMultKey/EvalAutomorphismKey/EvalBootstrapKey serializers
- Correct BinFHE docstrings and add a timeout to the example runner

* Add null-context guards and strengthen the test suite

- Reject None CryptoContext in ConvertRLWEToCKKS and SchemeSwitchingDataSerializer, with tests covering all three serializer arguments passed as None
- Add regression tests for unwritable key-serialization paths
- Use tmp_path-based unusable paths; make the eval-sum round trip able to fail
- Gate slow examples with the long marker; FE example behind RUN_HIGH_MEMORY_EXAMPLES
- Automate the nested LUT callback test

* Range-check integer conversions against a cached maximum instead of a decimal round trip, improving conversion performance.

* Add a deferred CI workflow, expose __version__, and update the docs

- New CI workflow reusing the Manual pipeline, pinned to OpenFHE v1.6.1; triggers stay commented out,
  with the PR-checkout and runner-policy preconditions documented in the file
- Inject openfhe.__version__ from CMake and re-export it from the package, since the star import skips underscore names
- Pin the version test to CMakeLists.txt as the single source of truth
- Derive the Sphinx release from the module; refresh the copyright
- Document LWESwitchingKey and the BinFHE deserialization functions

* Fill the missing docstrings and fix the LUT tests on 32-bit builds

- Replace all empty py::doc("") placeholders; commuted subtraction overloads get direction-correct text
- Document CCParams, SchSwchParams, Ciphertext, and BinFHE context methods, noting that SchSwchParams getters require
  an object populated from a CKKS cryptocontext and that GetElements/GetElementsMutable return copies, not views
- Use the plain TOY context in the LUT callback tests: the large-precision overload needs primes wider than
  NATIVE_SIZE=32 supports

* Fix the integer-overflow test on 128-bit builds

The test used the hardcoded value 3*10**19, which overflows a 64-bit NativeInteger but is representable on NATIVE_SIZE=128,
so no OverflowError was raised and the CI job failed. Derive the overflow probes from the configured native size instead:
native_max + 1 and native_max * 10. The conversion guard itself was correct.

* Add docstrings to all remaining bindings

All 661 public APIs in the openfhe module now carry a docstring (verified at runtime). Adds docstrings/serialization_docs.h
for the (de)serialization functions, inline py::doc() for the CryptoContext getters/setters, key, Plaintext, Ciphertext and
BinFHE/LWE methods and free functions, and class docstrings for every exposed class.

* Resolve the remaining v1.6 review items

- Reject None/empty arguments in the SchemeletRLWEMP entry points instead of segfaulting, and add a regression test for every guard
- Fix the GetPublicKey docstring: only BTKeyGen(PUB_ENCRYPT) stores the key
- Raise OverflowError for oversized ints on both math backends, and static_assert that the overflow check cannot silently switch off
- Add a README section on the v1.6 breaking changes (FHECKKSRNS and the removed BinFHEContext attributes)
- Install only the extension module (no stray __init__.py) and update the README install notes

* Restore the __init__.py install for the wheel packager

openfhe-python-packager composes the pip wheel as an openfhe/ package from the install tree; without __init__.py setuptools packages nothing,
the wheel ships empty, and import openfhe fails in CI.

* Release the GIL in compute-heavy PKE bindings and serialization

Attach py::call_guard<py::gil_scoped_release> (the shared release_gil
constant in bindings.h) to the PKE bindings whose C++ bodies never touch
Python objects: the EvalAdd/Sub/Mult families, rotations and key
switching, Encrypt/Decrypt, the Make*Plaintext encoders, key generation,
multiparty decryption, bootstrapping, scheme switching, and polynomial/
Chebyshev evaluation, plus every (de)serialization entry point. Wrappers
that must touch Python objects mid-body (the py::bytes serialization
wrappers, EvalMultNoCheck's py::int_ conversion) release the GIL around
their pure-C++ section instead. Also drop a dead local in the
EvalAutomorphismKey string deserializer.

Python threads can now run OpenFHE work in parallel instead of
serializing on the GIL; callers that previously needed process pools
(paying per-worker context/key re-deserialization and ciphertext IPC
round trips) can share one in-memory CryptoContext across a thread
pool. Measured on 4 threads with OMP_NUM_THREADS=1 at ring dimension
16384: EvalMult 2.35x, EvalAtIndex 2.80x, MakeCKKSPackedPlaintext
3.11x, ciphertext deserialization 4.80x.

The thread-safety contract is unchanged from the C++ API but is now
observable from Python and documented in bindings.h: the static
evaluation-key maps are unsynchronized, so keys must not be generated,
inserted, or deserialized while another thread evaluates. The BinFHE
and FBT bindings are not yet covered and are left for a follow-up.

* Remove the redundant buffer copies from bytes (de)serialization

Deserialize the py::bytes wrappers straight from the bytes object's
internal buffer through a non-owning streambuf, instead of copying the
payload into a std::string and again into an istringstream; the GIL is
released once the buffer is borrowed. Serialize into a single owned
std::string through an appending streambuf, instead of copying out of a
stringbuf; the only remaining copy in either direction is into the
returned py::bytes. The JSON/string wrappers use the same streambufs,
dropping their stringstream copies.

Transient memory per call falls from roughly three times the payload to
one, and at 1 MB ciphertexts (ring dimension 16384) serialization runs
3.5x faster and deserialization 1.65x faster single-threaded.

* Make numpy a required runtime dependency

Declare the module's runtime dependencies in requirements.txt, which
openfhe-python-packager reads into the wheel's install_requires, and
import numpy eagerly at module load so a missing install fails with a
message naming the fix instead of a lazy mid-computation error. With
numpy guaranteed, the numpy bindings are registered unconditionally.
The README documents pip install -r requirements.txt in the source and
conda build flows and in the test instructions.

Add numpy fast paths for encode/decode and fix overload dispatch

* Stop copying the digit decomposition in the fast-rotation bindings

EvalFastRotationPrecompute now moves the decomposition into the carrier
ciphertext, and EvalFastRotation/EvalFastRotationExt pass the C++ API an
aliasing shared_ptr into the carrier's elements instead of deep-copying
the full decomposition on every rotation call. Hoisted rotations run
~1.8x faster than repeated EvalRotate (8 rotations, ring dim 16384).

Also accept signed rotation offsets like EvalRotate does (the uint32_t
binding rejected them), add the GIL release EvalFastRotationExt was
missing, and add a fast-vs-plain regression test with a reused
precompute.

* updates

* Select the openfhe wheel by name in the docs wheel builder

* Fix stale CKKS plaintext docs, install-prefix reconfigure bug, and README dependency notes

* Correct the CKKS plaintext params docstrings and warn on pre-fix build directories

---------

Co-authored-by: Dmitriy Suponitskiy <dsuponitskiy@dualitytech.com>
Co-authored-by: Carlo Pascoe <cpascoe@dualitytech.com>
@yspolyakov yspolyakov added this to the Release 1.6.1.0 milestone Oct 27, 2026
@dsuponitskiy
dsuponitskiy merged commit a016560 into main Oct 27, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants