Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Minor fixes for new adder and multiplier gates classes #13530

Merged
merged 7 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ sk = "qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis:SolovayKitaevS
"HalfAdder.ripple_c04" = "qiskit.transpiler.passes.synthesis.hls_plugins:HalfAdderSynthesisC04"
"HalfAdder.ripple_v95" = "qiskit.transpiler.passes.synthesis.hls_plugins:HalfAdderSynthesisV95"
"HalfAdder.qft_d00" = "qiskit.transpiler.passes.synthesis.hls_plugins:HalfAdderSynthesisD00"
"FullAdder.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:FullAdderSynthesisC04"
"FullAdder.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:FullAdderSynthesisDefault"
"FullAdder.ripple_c04" = "qiskit.transpiler.passes.synthesis.hls_plugins:FullAdderSynthesisC04"
"FullAdder.ripple_v95" = "qiskit.transpiler.passes.synthesis.hls_plugins:FullAdderSynthesisV95"
"Multiplier.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:MultiplierSynthesisR17"
Expand Down
25 changes: 25 additions & 0 deletions qiskit/circuit/library/arithmetic/adders/adder.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@ def num_state_qubits(self) -> int:
"""
return self._num_state_qubits

def _define(self):
"""Populates self.definition with some decomposition of this gate."""
from qiskit.synthesis.arithmetic import adder_qft_d00

# This particular decomposition does not use any ancilla qubits.
# Note that the transpiler may choose a different decomposition
# based on the number of ancilla qubits available.
self.definition = adder_qft_d00(self.num_state_qubits, kind="half")


class ModularAdderGate(Gate):
r"""Compute the sum modulo :math:`2^n` of two :math:`n`-sized qubit registers.
Expand Down Expand Up @@ -162,6 +171,15 @@ def num_state_qubits(self) -> int:
"""
return self._num_state_qubits

def _define(self):
"""Populates self.definition with some decomposition of this gate."""
from qiskit.synthesis.arithmetic import adder_qft_d00

# This particular decomposition does not use any ancilla qubits.
# Note that the transpiler may choose a different decomposition
# based on the number of ancilla qubits available.
self.definition = adder_qft_d00(self.num_state_qubits, kind="fixed")


class FullAdderGate(Gate):
r"""Compute the sum of two :math:`n`-sized qubit registers, including carry-in and -out bits.
Expand Down Expand Up @@ -208,3 +226,10 @@ def num_state_qubits(self) -> int:
The number of state qubits.
"""
return self._num_state_qubits

def _define(self):
"""Populates self.definition with a decomposition of this gate."""
from qiskit.synthesis.arithmetic import adder_ripple_c04

# In the case of a full adder, this method does not use any ancilla qubits
self.definition = adder_ripple_c04(self.num_state_qubits, kind="full")
Cryoris marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 9 additions & 0 deletions qiskit/circuit/library/arithmetic/multipliers/multiplier.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,12 @@ def num_result_qubits(self) -> int:
The number of result qubits.
"""
return self._num_result_qubits

def _define(self):
"""Populates self.definition with some decomposition of this gate."""
from qiskit.synthesis.arithmetic import multiplier_qft_r17

# This particular decomposition does not use any ancilla qubits.
# Note that the transpiler may choose a different decomposition
# based on the number of ancilla qubits available.
self.definition = multiplier_qft_r17(self.num_state_qubits)
103 changes: 85 additions & 18 deletions qiskit/transpiler/passes/synthesis/hls_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,18 @@
- :class:`.ModularAdderSynthesisD00`
- 0
- a QFT-based adder
* - ``"default"``
- :class:`~.ModularAdderSynthesisDefault`
- any
- chooses the best algorithm based on the ancillas available

.. autosummary::
:toctree: ../stubs/

ModularAdderSynthesisC04
ModularAdderSynthesisD00
ModularAdderSynthesisV95
ModularAdderSynthesisDefault

Half Adder Synthesis
''''''''''''''''''''
Expand All @@ -330,13 +335,18 @@
- :class:`.HalfAdderSynthesisD00`
- 0
- a QFT-based adder
* - ``"default"``
- :class:`~.HalfAdderSynthesisDefault`
- any
- chooses the best algorithm based on the ancillas available

.. autosummary::
:toctree: ../stubs/

HalfAdderSynthesisC04
HalfAdderSynthesisD00
HalfAdderSynthesisV95
HalfAdderSynthesisDefault

Full Adder Synthesis
''''''''''''''''''''
Expand All @@ -356,12 +366,17 @@
- :class:`.FullAdderSynthesisV95`
- :math:`n-1`, for :math:`n`-bit numbers
- a ripple-carry adder
* - ``"default"``
- :class:`~.FullAdderSynthesisDefault`
- any
- chooses the best algorithm based on the ancillas available

.. autosummary::
:toctree: ../stubs/

FullAdderSynthesisC04
FullAdderSynthesisV95
FullAdderSynthesisDefault


Multiplier Synthesis
Expand Down Expand Up @@ -1212,10 +1227,26 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **
if not isinstance(high_level_object, ModularAdderGate):
return None

if options.get("num_clean_ancillas", 0) >= 1:
return adder_ripple_c04(high_level_object.num_state_qubits, kind="fixed")
# For up to 5 qubits, the QFT-based adder is best
Cryoris marked this conversation as resolved.
Show resolved Hide resolved
if high_level_object.num_state_qubits <= 5:
decomposition = ModularAdderSynthesisD00().run(
high_level_object, coupling_map, target, qubits, **options
)
if decomposition is not None:
return decomposition

return adder_qft_d00(high_level_object.num_state_qubits, kind="fixed")
# Otherwise, the following decomposition is best (if there are enough ancillas)
if (
decomposition := ModularAdderSynthesisC04().run(
high_level_object, coupling_map, target, qubits, **options
)
) is not None:
return decomposition

# Otherwise, use the QFT-adder again
return ModularAdderSynthesisD00().run(
high_level_object, coupling_map, target, qubits, **options
)


class ModularAdderSynthesisC04(HighLevelSynthesisPlugin):
Expand Down Expand Up @@ -1264,8 +1295,8 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **

num_state_qubits = high_level_object.num_state_qubits

# for more than 1 state qubit, we need an ancilla
if num_state_qubits > 1 > options.get("num_clean_ancillas", 1):
# The synthesis method needs n-1 clean ancilla qubits
if num_state_qubits - 1 > options.get("num_clean_ancillas", 0):
return None

return adder_ripple_v95(num_state_qubits, kind="fixed")
Expand Down Expand Up @@ -1309,10 +1340,26 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **
if not isinstance(high_level_object, HalfAdderGate):
return None

if options.get("num_clean_ancillas", 0) >= 1:
return adder_ripple_c04(high_level_object.num_state_qubits, kind="half")
# For up to 3 qubits, ripple_v95 is better (if there are enough ancilla qubits)
if high_level_object.num_state_qubits <= 3:
decomposition = HalfAdderSynthesisV95().run(
high_level_object, coupling_map, target, qubits, **options
)
if decomposition is not None:
return decomposition

return adder_qft_d00(high_level_object.num_state_qubits, kind="half")
# The next best option is to use ripple_c04 (if there are enough ancilla qubits)
if (
decomposition := HalfAdderSynthesisC04().run(
high_level_object, coupling_map, target, qubits, **options
)
) is not None:
return decomposition

# The QFT-based adder does not require ancilla qubits and should always succeed
return HalfAdderSynthesisD00().run(
high_level_object, coupling_map, target, qubits, **options
)


class HalfAdderSynthesisC04(HighLevelSynthesisPlugin):
Expand Down Expand Up @@ -1360,8 +1407,8 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **

num_state_qubits = high_level_object.num_state_qubits

# for more than 1 state qubit, we need an ancilla
if num_state_qubits > 1 > options.get("num_clean_ancillas", 1):
# The synthesis method needs n-1 clean ancilla qubits
if num_state_qubits - 1 > options.get("num_clean_ancillas", 0):
return None

return adder_ripple_v95(num_state_qubits, kind="half")
Expand All @@ -1381,18 +1428,38 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **
return adder_qft_d00(high_level_object.num_state_qubits, kind="half")


class FullAdderSynthesisC04(HighLevelSynthesisPlugin):
class FullAdderSynthesisDefault(HighLevelSynthesisPlugin):
"""A ripple-carry adder with a carry-in and a carry-out bit.

This plugin name is:``FullAdder.ripple_c04`` which can be used as the key on
This plugin name is:``FullAdder.default`` which can be used as the key on
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

This plugin requires at least one clean auxiliary qubit.
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
if not isinstance(high_level_object, FullAdderGate):
return None

The plugin supports the following plugin-specific options:
# FullAdderSynthesisC04 requires no ancilla qubits and returns better results
# than FullAdderSynthesisV95 in all cases except for n=1.
if high_level_object.num_state_qubits == 1:
decomposition = FullAdderSynthesisV95().run(
high_level_object, coupling_map, target, qubits, **options
)
if decomposition is not None:
return decomposition
Cryoris marked this conversation as resolved.
Show resolved Hide resolved

return FullAdderSynthesisC04().run(
high_level_object, coupling_map, target, qubits, **options
)

* ``num_clean_ancillas``: The number of clean auxiliary qubits available.

class FullAdderSynthesisC04(HighLevelSynthesisPlugin):
"""A ripple-carry adder with a carry-in and a carry-out bit.

This plugin name is:``FullAdder.ripple_c04`` which can be used as the key on
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.

This plugin requires no auxiliary qubits.
"""

def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
Expand All @@ -1409,7 +1476,7 @@ class FullAdderSynthesisV95(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.

For an adder on 2 registers with :math:`n` qubits each, this plugin requires at
least :math:`n-1` clean auxiliary qubit.
least :math:`n-1` clean auxiliary qubits.

The plugin supports the following plugin-specific options:

Expand All @@ -1422,8 +1489,8 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **

num_state_qubits = high_level_object.num_state_qubits

# for more than 1 state qubit, we need an ancilla
if num_state_qubits > 1 > options.get("num_clean_ancillas", 1):
# The synthesis method needs n-1 clean ancilla qubits
if num_state_qubits - 1 > options.get("num_clean_ancillas", 0):
return None

return adder_ripple_v95(num_state_qubits, kind="full")
Expand Down
18 changes: 18 additions & 0 deletions releasenotes/notes/fix-adder-gates-39cf3d5f683e8880.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
fixes:
- |
Added default definitions for :class:`.FullAdderGate`, :class:`.HalfAdderGate`,
:class:`.ModularAdderGate` and :class:`.MultiplierGate` gates, allowing to
contruct :class:`.Operator`\s from quantum circuits containing these gates.
- |
Fixed the number of clean ancilla qubits required by
:class:`.FullAdderSynthesisV95`, :class:`.HalfAdderSynthesisV95`, and
:class:`.ModularAdderSynthesisV95` plugins.
- |
Added missing :class:`.FullAdderSynthesisDefault` plugin that chooses the best
decomposition for :class:`.FullAdderGate` based on the number of clean ancilla qubits
available.
- |
Fixed :class:`.HalfAdderSynthesisDefault` and :class:`.ModularAdderSynthesisDefault`
plugins, for :class:`.HalfAdderGate` and :class:`.ModularAdderGate` respectively,
to choose the best decomposition based on the number of clean ancilla qubits available.
Loading
Loading