Skip to content

Fix #880: return simplified QubitOperator from symmetry_conserving_bravyi_kitaev - #1440

Open
stark256-spec wants to merge 1 commit into
quantumlib:mainfrom
stark256-spec:fix/scbk-simplify-qubit-operator
Open

Fix #880: return simplified QubitOperator from symmetry_conserving_bravyi_kitaev#1440
stark256-spec wants to merge 1 commit into
quantumlib:mainfrom
stark256-spec:fix/scbk-simplify-qubit-operator

Conversation

@stark256-spec

Copy link
Copy Markdown

Closes #880.

Problem

get_sparse_operator raises on the operator produced by symmetry_conserving_bravyi_kitaev:

from openfermion import get_sparse_operator, FermionOperator, symmetry_conserving_bravyi_kitaev

fermion_op = FermionOperator("0^ 1^")
qubit_op = symmetry_conserving_bravyi_kitaev(fermion_op, 4, 2)
get_sparse_operator(qubit_op)   # ValueError: axis 0 index 7 exceeds matrix dimension 4

Root cause

symmetry_conserving_bravyi_kitaev finishes by calling remove_indices, which shifts qubit indices. When two qubits are mapped onto the same new index, a term ends up with multiple Paulis acting on one qubit, e.g.:

((0, X), (1, Y), (1, X))

remove_indices writes these terms straight into the operator’s .terms dict, so they bypass the simplification QubitOperator normally performs on construction. qubit_operator_sparse assumes each qubit appears at most once per term (it grows the tensor product one factor per Pauli), so a repeated qubit makes the per-term matrix larger than the n_qubits Hilbert space and the assembly raises.

Fix

Rebuild the operator after remove_indices so every term is routed back through QubitOperator’s simplification, restoring canonical one-Pauli-per-qubit form (e.g. ((0, X), (1, Y), (1, X)) → ((0, X), (1, Z))). This keeps the operator mathematically identical — the existing eigenspectrum-based tests still pass — while satisfying the invariant that QubitOperators are simplified, which get_sparse_operator and other consumers rely on.

Tests

Added test_output_is_simplified_qubit_operator (the reporter’s reproducer), asserting:

  1. every returned term is canonical (at most one Pauli per qubit);
  2. get_sparse_operator no longer raises;
  3. the resulting sparse operator matches the explicitly-simplified operator.

Verification (Python 3.12):

  • pytest remove_symmetry_qubits_test.py — 6 passed (5 existing + new regression).
  • black --check (line-length 100), pylint --rcfile=.pylintrc (3.3.9), and mypy all clean on the changed files.

…serving_bravyi_kitaev

symmetry_conserving_bravyi_kitaev could return QubitOperators with
un-simplified terms: remove_indices() shifts qubit indices and can map
two qubits onto the same index, producing a term with multiple Paulis
acting on one qubit (e.g. ((0, 'X'), (1, 'Y'), (1, 'X'))). Because those
terms are written straight into the operator's .terms dict, they bypass
the simplification normally done on construction.

get_sparse_operator (via qubit_operator_sparse) assumes each qubit
appears at most once per term, so it raised
'ValueError: axis 0 index 7 exceeds matrix dimension 4' on such operators.

Rebuild the operator after remove_indices so every term is routed back
through QubitOperator's simplification, restoring canonical
one-Pauli-per-qubit form. Add a regression test using the reporter's
reproducer that checks the terms are canonical and that the resulting
sparse operator matches the explicitly-simplified operator.

Closes quantumlib#880
@google-cla

google-cla Bot commented Aug 12, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request addresses issue #880 by ensuring that symmetry_conserving_bravyi_kitaev returns a simplified QubitOperator in canonical form (with at most one Pauli operator per qubit). This is achieved by rebuilding the operator term-by-term, which triggers QubitOperator's internal simplification and prevents errors when calling get_sparse_operator. A regression test has also been added to verify this behavior. There are no review comments, so I have no additional feedback to provide.

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.

get_sparse_operator fails on non-simplified QubitOperators

1 participant