Skip to content

Feat: Added Verstraete-Cirac fermion-to-qubit encoding (#482)#515

Open
luffy-orf wants to merge 3 commits into
microsoft:mainfrom
luffy-orf:feat/verstraete-cirac-encoding
Open

Feat: Added Verstraete-Cirac fermion-to-qubit encoding (#482)#515
luffy-orf wants to merge 3 commits into
microsoft:mainfrom
luffy-orf:feat/verstraete-cirac-encoding

Conversation

@luffy-orf

Copy link
Copy Markdown

Summary

Adds the Verstraete-Cirac (VC) locality-preserving fermion-to-qubit encoding as a
new MajoranaMapping.verstraete_cirac(lattice) factory, consumable by QubitMapper
with no special-casing.

  • Geometry-free MajoranaMapping: introduces a generic, optional stabilizers_
    member (a list of Pauli terms) with accessor + JSON/HDF5 serialization. No grid
    metadata is stored on the data class.
  • Factory built from graph edges: verstraete_cirac recovers lattice
    coordinates directly from a rectangular LatticeGraph's adjacency, emits one VC
    block per spin sector (num_modes == 2 * n_sites, num_qubits == 4 * n_sites),
    and produces codespace stabilizers.
  • Generic engine penalty: the mapper appends λ·(I − S) per stabilizer
    uniformly — no encoding-name checks, no grid logic — so non-redundant encodings
    (Jordan-Wigner, etc.) are completely unaffected.
  • pybind11: exposes the verstraete_cirac factory and a stabilizers accessor.
  • Docs: lists VC under "Supported encodings" and adds the three references.

Test plan

New module python/tests/test_verstraete_cirac.py covering the acceptance criteria:

  • Factory accepts 2x2, 2x3, 3x3, 4x4 lattices; QubitMapper consumes each
    without error; non-rectangular lattices are rejected.
  • Fermi-Hubbard 2x2 (t=1, U=4, half-filling): four lowest codespace eigenvalues
    match Jordan-Wigner to < 1e-10.
  • Nearest-neighbour hopping Pauli weight is the same finite integer for
    L in {2, 3, 4}.
  • JSON and HDF5 round-trips reproduce the qubit Hamiltonian term-by-term.
  • Full existing test suite passes; pre-commit succeeds on the diff.

Closes #482

Copilot AI review requested due to automatic review settings June 6, 2026 17:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds Verstraete–Cirac (auxiliary-qubit) fermion-to-qubit encoding support, including stabilizer exposure/serialization and a generic mapper-side stabilizer penalty to recover the physical (codespace) spectrum.

Changes:

  • Implement MajoranaMapping::verstraete_cirac(LatticeGraph) and expose it (plus stabilizers) to Python via pybind11.
  • Add generic stabilizer penalty injection in the mapping engine and extend JSON serialization to persist stabilizers (and explicit qubit count for bilinear-only mappings).
  • Add documentation + references and Python tests covering spectrum/locality/round-trips.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
python/tests/test_verstraete_cirac.py New acceptance tests for VC factory, mapper integration, spectrum match, locality, and serialization round-trips.
python/src/pybind11/data/majorana_mapping.cpp Exposes stabilizers and the verstraete_cirac factory to Python.
docs/source/user/comprehensive/algorithms/qubit_mapper.rst Documents Verstraete–Cirac encoding and its stabilizer penalty behavior.
docs/source/references.bib Adds VC-related citations.
cpp/src/qdk/chemistry/data/majorana_mapping_factories.cpp Implements VC factory and stabilizer construction.
cpp/src/qdk/chemistry/data/majorana_mapping.cpp Adds stabilizers storage + JSON persistence; adjusts without_tapering() and summary.
cpp/src/qdk/chemistry/data/majorana_map_engine.cpp Adds generic stabilizer penalty term merging into the mapped Hamiltonian.
cpp/include/qdk/chemistry/data/majorana_mapping.hpp Adds stabilizers() API and verstraete_cirac() factory declaration/docs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/source/user/comprehensive/algorithms/qubit_mapper.rst Outdated
Comment thread cpp/include/qdk/chemistry/data/majorana_mapping.hpp Outdated
Comment thread cpp/src/qdk/chemistry/data/majorana_mapping.cpp Outdated
Comment thread cpp/src/qdk/chemistry/data/majorana_map_engine.cpp Outdated
Comment thread cpp/src/qdk/chemistry/data/majorana_mapping_factories.cpp
@luffy-orf luffy-orf requested a review from Copilot June 6, 2026 17:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comment thread cpp/src/qdk/chemistry/data/majorana_mapping_factories.cpp
Comment thread cpp/src/qdk/chemistry/data/majorana_map_engine.cpp Outdated
Comment thread cpp/src/qdk/chemistry/data/majorana_map_engine.cpp
Comment thread cpp/src/qdk/chemistry/data/majorana_mapping.cpp Outdated
@luffy-orf luffy-orf requested a review from Copilot June 6, 2026 18:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comment on lines +199 to +203
with tempfile.NamedTemporaryFile(suffix=".h5") as f:
with h5py.File(f.name, "w") as hf:
mapping.to_hdf5(hf)
with h5py.File(f.name, "r") as hf:
reloaded = MajoranaMapping.from_hdf5(hf)
Comment on lines +566 to +571
std::unordered_map<SparsePauliWord, std::complex<double>, SparsePauliWordHash>
merged;
merged.reserve(result.words.size() + stabilizers.size() + 1);
for (std::size_t i = 0; i < result.words.size(); ++i) {
merged[result.words[i]] += result.coefficients[i];
}
Comment on lines +589 to +597
MajoranaMapResult penalized;
penalized.words.reserve(merged.size());
penalized.coefficients.reserve(merged.size());
for (auto& [word, coeff] : merged) {
if (protected_words.count(word) || std::abs(coeff) >= threshold) {
penalized.words.push_back(word);
penalized.coefficients.push_back(coeff);
}
}
Comment on lines +569 to +580
std::vector<std::pair<std::complex<double>, SparsePauliWord>> bilinears;
bilinears.reserve(M * (M - 1) / 2);
for (std::size_t J = 0; J < M; ++J) {
std::size_t p = J / 2, ap = J % 2;
std::size_t qp_qubit = 2 * sigma(p);
auto gamma_J = vc_jw_majorana(qp_qubit, ap == 0 ? op_x : op_y);
for (std::size_t K = J + 1; K < M; ++K) {
std::size_t q = K / 2, bq = K % 2;
std::size_t qq_qubit = 2 * sigma(q);
auto gamma_K = vc_jw_majorana(qq_qubit, bq == 0 ? op_x : op_y);

auto bare = PauliTermAccumulator::multiply_uncached(gamma_J, gamma_K);
@nabbelbabbel nabbelbabbel added the UnitaryHack Bountied Issues for UnitaryHACK label Jun 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

UnitaryHack Bountied Issues for UnitaryHACK

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Verstraete-Cirac Fermion-to-Qubit Encoding

3 participants