Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Remove dependance of BooleanExpression on tweedledum #13769

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

gadial
Copy link
Contributor

@gadial gadial commented Jan 30, 2025

Summary

Rewrites the BooleanExpression class such that expression parsing and circuit synthesis does not rely on the tweedledum library, and moves it to the synthesis library. Adds a BitFlipOracle class to preserve the user-facing usage of BooleanExpression.

Partially addresses #13755.

Details and comments

BooleanExpression is used to describe classical boolean functions and synthesize phase/bit-flip oracles for them. Currently it is used directly within qiskit only for the PhaseOracle class.

The current implementation of BooleanExpression relies on the tweedledum library for parsing and synthesizing. Since qiskit 2.0 will stop using tweedledum, this PR makes the code of BooleanExpression independent, in the cost of lower capabilities.

These are the main changes:

  1. Parsing is now done using the ast library, using a custom boolean_expression_visitor. Parsing DIMACS files is done directly using regexp.
  2. Oracle synthesis is now performed directly (within the new boolean_expression_synth module) using a straightforward approach seemingly also employed by tweedledum, given a representation of the function as ESOP (Exclusive sum of squares).
  3. A rudimentary procedure for converting a BooleanExpression into ESOP representation was also added. While it features basic optimizations to reduce the size of the ESOP, it needs to generate the full truth table of the represented function, making it a viable approach only for functions on a relatively low number of variables. This can be improved upon in the future if required, using more sophisticated ESOP generation and minimization techniques (such as using BDDs or SAT-solvers).
  4. All the synthesis-related code was moved to a new directory, synthesis/boolean. A new class, BitFlipOracle was added to the circuit library. The old BooleanExpression code remains unchanged and will be deprecated in a separate PR along with the rest of the classicalfunction library.

Non comprehensive benchmarking indicates a factor-10 runtime blowup for the non-tweedledum version, but oracle generation for functions of around 10 variables is still viable.

╒═══════════════════════════════╤═════════════╤═════════════╤═══════════════════╕
│ Test Case                     │         new │         old │   Ratio (new/old) │
╞═══════════════════════════════╪═════════════╪═════════════╪═══════════════════╡
│ 3 vars, 9 clauses (seed 42)   │ 0.000578117 │ 0.00033474  │          1.72707  │
├───────────────────────────────┼─────────────┼─────────────┼───────────────────┤
│ 2 vars, 6 clauses (seed 42)   │ 0.000580263 │ 0.0016449   │          0.352766 │
├───────────────────────────────┼─────────────┼─────────────┼───────────────────┤
│ 4 vars, 12 clauses (seed 42)  │ 0.00112677  │ 0.000320101 │          3.52004  │
├───────────────────────────────┼─────────────┼─────────────┼───────────────────┤
│ 5 vars, 15 clauses (seed 42)  │ 0.00388117  │ 0.00120397  │          3.22365  │
├───────────────────────────────┼─────────────┼─────────────┼───────────────────┤
│ 6 vars, 18 clauses (seed 42)  │ 0.0112587   │ 0.00248165  │          4.5368   │
├───────────────────────────────┼─────────────┼─────────────┼───────────────────┤
│ 7 vars, 21 clauses (seed 42)  │ 0.0248424   │ 0.0142735   │          1.74045  │
├───────────────────────────────┼─────────────┼─────────────┼───────────────────┤
│ 8 vars, 24 clauses (seed 42)  │ 0.0663635   │ 0.0208194   │          3.18758  │
├───────────────────────────────┼─────────────┼─────────────┼───────────────────┤
│ 9 vars, 27 clauses (seed 42)  │ 0.156358    │ 0.0419133   │          3.73051  │
├───────────────────────────────┼─────────────┼─────────────┼───────────────────┤
│ 10 vars, 30 clauses (seed 42) │ 0.406802    │ 0.071683    │          5.67501  │
├───────────────────────────────┼─────────────┼─────────────┼───────────────────┤
│ 11 vars, 33 clauses (seed 42) │ 0.863733    │ 0.156799    │          5.50854  │
├───────────────────────────────┼─────────────┼─────────────┼───────────────────┤
│ 12 vars, 36 clauses (seed 42) │ 2.25606     │ 0.234907    │          9.60408  │
╘═══════════════════════════════╧═════════════╧═════════════╧═══════════════════╛

Comparing the CX gate count in the result of transpiling the oracle for GenericBackendV2(num_qubits=27) with pass manager generate_preset_pass_manager(optimization_level=2, backend=backend) yields a slight advantage to the new implementation:

╒═══════════════════════════════╤═══════╤═══════╤═══════════════════╕
│ Test Case                     │   new │   old │   Ratio (new/old) │
╞═══════════════════════════════╪═══════╪═══════╪═══════════════════╡
│ 2 vars, 6 clauses (seed 42)   │     0 │     0 │        nan        │
├───────────────────────────────┼───────┼───────┼───────────────────┤
│ 3 vars, 9 clauses (seed 42)   │     0 │     0 │        nan        │
├───────────────────────────────┼───────┼───────┼───────────────────┤
│ 4 vars, 12 clauses (seed 42)  │     0 │     0 │        nan        │
├───────────────────────────────┼───────┼───────┼───────────────────┤
│ 5 vars, 15 clauses (seed 42)  │    36 │    36 │          1        │
├───────────────────────────────┼───────┼───────┼───────────────────┤
│ 6 vars, 18 clauses (seed 42)  │   218 │   244 │          0.893443 │
├───────────────────────────────┼───────┼───────┼───────────────────┤
│ 7 vars, 21 clauses (seed 42)  │   694 │  1088 │          0.637868 │
├───────────────────────────────┼───────┼───────┼───────────────────┤
│ 8 vars, 24 clauses (seed 42)  │  1842 │  3125 │          0.58944  │
├───────────────────────────────┼───────┼───────┼───────────────────┤
│ 9 vars, 27 clauses (seed 42)  │  6954 │  8238 │          0.844137 │
├───────────────────────────────┼───────┼───────┼───────────────────┤
│ 10 vars, 30 clauses (seed 42) │ 21927 │ 25955 │          0.844808 │
╘═══════════════════════════════╧═══════╧═══════╧═══════════════════╛

@gadial gadial requested a review from a team as a code owner January 30, 2025 19:14
@qiskit-bot
Copy link
Collaborator

One or more of the following people are relevant to this code:

  • @Cryoris
  • @Qiskit/terra-core
  • @ajavadia

@gadial gadial added this to the 2.0.0 milestone Jan 30, 2025
@ShellyGarion ShellyGarion self-assigned this Jan 31, 2025
@coveralls
Copy link

coveralls commented Feb 2, 2025

Pull Request Test Coverage Report for Build 13430933012

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 221 of 227 (97.36%) changed or added relevant lines in 6 files are covered.
  • 2130 unchanged lines in 78 files lost coverage.
  • Overall coverage decreased (-0.7%) to 88.143%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/synthesis/boolean/boolean_expression.py 89 90 98.89%
qiskit/synthesis/boolean/boolean_expression_visitor.py 38 39 97.44%
qiskit/circuit/library/bit_flip_oracle.py 13 15 86.67%
qiskit/circuit/library/phase_oracle.py 14 16 87.5%
Files with Coverage Reduction New Missed Lines %
crates/accelerate/src/euler_one_qubit_decomposer.rs 1 91.5%
crates/accelerate/src/gate_direction.rs 1 97.34%
crates/accelerate/src/target_transpiler/nullable_index_map.rs 1 67.17%
crates/accelerate/src/twirling.rs 1 97.62%
qiskit/circuit/add_control.py 1 97.78%
qiskit/providers/basic_provider/basic_provider_tools.py 1 83.33%
qiskit/providers/fake_provider/fake_backend.py 1 85.51%
qiskit/providers/fake_provider/fake_openpulse_3q.py 1 92.86%
qiskit/providers/models/backendproperties.py 1 94.74%
qiskit/pulse/calibration_entries.py 1 90.57%
Totals Coverage Status
Change from base Build 13072040346: -0.7%
Covered Lines: 78573
Relevant Lines: 89143

💛 - Coveralls

@ShellyGarion ShellyGarion linked an issue Feb 2, 2025 that may be closed by this pull request
Copy link
Member

@ShellyGarion ShellyGarion left a comment

Choose a reason for hiding this comment

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

Thanks @gadial for taking care of this - it looks great!

Note that there are some necessary API changes here, which makes sense and it's also allowed for Qiskit 2.0, but probably need better release notes, as well as raise deprecation warnings in Qiskit 1.4.

Did you make some experiments to check the performance compared to Tweedledum?



@HAS_TWEEDLEDUM.require_in_instance
class BooleanExpression(ClassicalElement):
class BooleanExpression(Gate):
Copy link
Contributor

Choose a reason for hiding this comment

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

The easiest deprecation path would be to just deprecate BooleanExpression in place and have the new version in the circuit library.

It might also be worth considering a renaming to avoid too much confusion with the new object. What does this circuit implement exactly? Does it flip a target bit if the boolean expression is satisfied or does it something in-place? If it's the first, maybe BitflipOracle could be a good name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This seems like a sound strategy - we already have a PhaseOracle so we can add BitFlipOracle which will be similar but with a different synthesis, and both gates would rely on a non-gate version of BooleanExpression (maybe with a different name) which handles parsing, simulating (to create the truth table) and synthesizing (since we might want to invest in optimized algorithms later.

The main question is where to put those files. We can put BitFlipOracle next to PhaseOracle in qiskit.circuit.library but we still need to put the NewBooleanExpression module (which would consist at least of boolean_expression, boolean_expression_visitor and boolean_expression_synth files) somewhere that makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in e403028

Copy link
Member

@ShellyGarion ShellyGarion left a comment

Choose a reason for hiding this comment

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

Thanks @gadial for taking care of this! I have a few comments and questions.

@@ -345,6 +345,7 @@
QuantumVolume
PhaseEstimation
GroverOperator
BitFlipOracle
Copy link
Member

Choose a reason for hiding this comment

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

perhaps this should be done in another PR, like @Cryoris's #13520, but I think that the 3 oracles (grover, phase and bitflip) deserve their own section in the circuit library docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems like a good idea but I agree it belongs in another PR.

Copy link
Member

@ShellyGarion ShellyGarion left a comment

Choose a reason for hiding this comment

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

LGTM, thanks @gadial !

Thanks for the comparison of synthesis time compared to tweedledum.
I also wonder what is the CX-cost comparison of the transpiled circuits.

@ShellyGarion ShellyGarion added the Changelog: New Feature Include in the "Added" section of the changelog label Feb 12, 2025
ShellyGarion
ShellyGarion previously approved these changes Feb 12, 2025
Copy link
Member

@ShellyGarion ShellyGarion left a comment

Choose a reason for hiding this comment

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

LGTM. @Cryoris - do you have any further comments?

@gadial gadial requested a review from Cryoris February 12, 2025 11:39
Comment on lines 20 to 21
class BitFlipOracle(QuantumCircuit):
r"""Bit-flip Oracle.
Copy link
Contributor

Choose a reason for hiding this comment

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

In the spirit of circuit library refactoring, do we want BitFlipOracle and PhaseOracle to also have Gate variants? @Cryoris, what are your thoughts on this? What about BooleanExpression?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Personally I'm againt BooleanExpression being a Gate (as is currently the situation) since it's "interface" is not clear, as is reflected in the difference between phase oracle (n qubits, applies Z conditioned on f(x)) and bit flip oracle (n+1 qubits, applies X on a specific qubit conditioned on f(x)). Currently the somewhat arbitrary choice was to have BooleanExpression as a gate act as a bit-flip oracle.

Copy link
Contributor

Choose a reason for hiding this comment

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

I completely agree that BooleanExpression should not be a Gate, thanks! I also think that we probably want to have the new BitFlipOracle available as a subclass of a quantum circuit, at the least to be consistent with PhaseOracle. However, for the sake of circuit library refactoring, in a follow-up we should also expose phase oracle and bit flip oracles as gates.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would like to make a point for Gates 🙂 in the circuit library modernization (#13046) we define that all objects are either gates or functions -- gates, if their action is defined by a mathematical operation and we are free to synthesize with different algorithms, and functions, if they are defined by their structure.

To me, this is therefor a clear case of a Gate. We can discuss what interface makes most sense, if there are multiple options. What you suggested above makes sense to me:

  • a BitFlipOracle flips a target qubit if f(x) is satisfied
  • a PhaseOracle flips the phase of states satisfying f(x) -- note that it is fine if the actual implementation uses an auxiliary qubit.

I would prefer not introducing a BitFlipOracle(QuantumCircuit) if we add a gate. The PhaseOracle should also be updated to be a gate once we agree on the interface, to match the rest of the library and enable different synthesis algorithms.

Copy link
Contributor

Choose a reason for hiding this comment

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

On the point of interface to the future PhaseOracleGate and BitFlipOracleGate, do you think these gates should have the "evaluate" and "from_dimacs" methods?

Copy link
Contributor

Choose a reason for hiding this comment

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

From the docstrings,

  • The phase oracle implements $|x\rangle \mapsto (-1)^{f(x)}|x\rangle$, and thus does not involve an additional qubit,
  • The bitflip oracle implements $|x\rangle|y\rangle \mapsto |x\rangle|f(x)\oplus y\rangle$, and thus involves an additional "target" qubit $y$.

@Cryoris, I think the questions are: would you like PhaseOracleGate and BitFlipOracleGate be a part of this PR and/or would you like to pending-deprecate PhaseOracle as a part of this PR?

Alternatively, maybe it's fine to introduce a BitFlipOracle quantum circuit (as this PR is doing), and add the two gates in a follow-up?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My concrete question is whether I can simply change the line class BitFlipOracle(QuantumCircuit) into class BitFlipOracleGate(Gate) and be done with it, or whether that means I should implement additional methods currently not implemented.

As for evaluate_bitstring I don't mind removing it from bit flip oracle, and after deprecating PhaseOracle in favor of PhaseOracleGate we can also drop it from PhaseOracleGate. It's not a functionality that a gate should supply, and anyway the user can always do oracle.boolean_expression.simulate.

Copy link
Member

@ShellyGarion ShellyGarion Feb 20, 2025

Choose a reason for hiding this comment

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

If we are already introducing BitFlipOracleGate as part of this PR, should we also add PhaseOracleGate ?
(and PhaseOracle will be deprecated at Qiskit 3.0)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems reasonable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 991ded1

I think we can merge.

Copy link
Contributor

@alexanderivrii alexanderivrii left a comment

Choose a reason for hiding this comment

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

This is a really-really cool PR. Thanks for making the phase oracle (and its friend, bit-flip oracle) available to those who can no longer use tweedledum.

My only suggestions are around documentation: expanding the release notes and adding python type hints to various functions.

Comment on lines 20 to 21
class BitFlipOracle(QuantumCircuit):
r"""Bit-flip Oracle.
Copy link
Contributor

Choose a reason for hiding this comment

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

I completely agree that BooleanExpression should not be a Gate, thanks! I also think that we probably want to have the new BitFlipOracle available as a subclass of a quantum circuit, at the least to be consistent with PhaseOracle. However, for the sake of circuit library refactoring, in a follow-up we should also expose phase oracle and bit flip oracles as gates.

Comment on lines 52 to 59
var_order: list = None,
) -> None:
"""Creates a BitFlipOracle object

Args:
expression: A Python-like boolean expression.
var_order(list): A list with the order in which variables will be created.
(default: by appearance)
Copy link
Contributor

Choose a reason for hiding this comment

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

Some minor comments on docstrings (which might be off in the original class already, not that you added this 🙂)

  • if None is supported we can annotate it with OtherType | None (this might require from __future__ import annotations)
  • you could also add the type of the list elements -- I'm not sure but this might be list[str]?
  • if the type is in the signature it can be skipped in the docstring
  • we typically skip the initial sentence in the initializer
Suggested change
var_order: list = None,
) -> None:
"""Creates a BitFlipOracle object
Args:
expression: A Python-like boolean expression.
var_order(list): A list with the order in which variables will be created.
(default: by appearance)
var_order: list[str] | None = None,
) -> None:
"""
Args:
expression: A Python-like boolean expression.
var_order: A list with the order in which variables will be created.
(default: by appearance)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in d94f490

Comment on lines 20 to 21
class BitFlipOracle(QuantumCircuit):
r"""Bit-flip Oracle.
Copy link
Contributor

Choose a reason for hiding this comment

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

On the point of interface to the future PhaseOracleGate and BitFlipOracleGate, do you think these gates should have the "evaluate" and "from_dimacs" methods?

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Changelog: New Feature Include in the "Added" section of the changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Remove tweedledum from qiskit-sdk
6 participants