Skip to content

Commit b9ea266

Browse files
Minor fixes for new adder and multiplier gates classes (Qiskit#13530)
* Fixes to AdderGate classes Added define method to HalfAdder, FullAdder, and ModularAdder classes to allow constructing Operators from quantum circuits containing such adder gates. Fixing plugins related to adder_ripple_v95, the plugins can be used only if n-1 clean ancillas are available. Improved the default adder plugins to choose the best decomposition based on the number of state qubits and the number of ancilla qubits. * Adding tests for the case that adder plugins do not apply * Constructing Operators from circuits with MultiplierGates * release notes * docstring fix * apply suggestions from code review * futher improving tests based on additional review comments
1 parent 17a2ccc commit b9ea266

File tree

7 files changed

+279
-20
lines changed

7 files changed

+279
-20
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ sk = "qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis:SolovayKitaevS
109109
"HalfAdder.ripple_c04" = "qiskit.transpiler.passes.synthesis.hls_plugins:HalfAdderSynthesisC04"
110110
"HalfAdder.ripple_v95" = "qiskit.transpiler.passes.synthesis.hls_plugins:HalfAdderSynthesisV95"
111111
"HalfAdder.qft_d00" = "qiskit.transpiler.passes.synthesis.hls_plugins:HalfAdderSynthesisD00"
112-
"FullAdder.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:FullAdderSynthesisC04"
112+
"FullAdder.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:FullAdderSynthesisDefault"
113113
"FullAdder.ripple_c04" = "qiskit.transpiler.passes.synthesis.hls_plugins:FullAdderSynthesisC04"
114114
"FullAdder.ripple_v95" = "qiskit.transpiler.passes.synthesis.hls_plugins:FullAdderSynthesisV95"
115115
"Multiplier.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:MultiplierSynthesisR17"

qiskit/circuit/library/arithmetic/adders/adder.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,15 @@ def num_state_qubits(self) -> int:
116116
"""
117117
return self._num_state_qubits
118118

119+
def _define(self):
120+
"""Populates self.definition with some decomposition of this gate."""
121+
from qiskit.synthesis.arithmetic import adder_qft_d00
122+
123+
# This particular decomposition does not use any ancilla qubits.
124+
# Note that the transpiler may choose a different decomposition
125+
# based on the number of ancilla qubits available.
126+
self.definition = adder_qft_d00(self.num_state_qubits, kind="half")
127+
119128

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

174+
def _define(self):
175+
"""Populates self.definition with some decomposition of this gate."""
176+
from qiskit.synthesis.arithmetic import adder_qft_d00
177+
178+
# This particular decomposition does not use any ancilla qubits.
179+
# Note that the transpiler may choose a different decomposition
180+
# based on the number of ancilla qubits available.
181+
self.definition = adder_qft_d00(self.num_state_qubits, kind="fixed")
182+
165183

166184
class FullAdderGate(Gate):
167185
r"""Compute the sum of two :math:`n`-sized qubit registers, including carry-in and -out bits.
@@ -208,3 +226,10 @@ def num_state_qubits(self) -> int:
208226
The number of state qubits.
209227
"""
210228
return self._num_state_qubits
229+
230+
def _define(self):
231+
"""Populates self.definition with a decomposition of this gate."""
232+
from qiskit.synthesis.arithmetic import adder_ripple_c04
233+
234+
# In the case of a full adder, this method does not use any ancilla qubits
235+
self.definition = adder_ripple_c04(self.num_state_qubits, kind="full")

qiskit/circuit/library/arithmetic/multipliers/multiplier.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,12 @@ def num_result_qubits(self) -> int:
190190
The number of result qubits.
191191
"""
192192
return self._num_result_qubits
193+
194+
def _define(self):
195+
"""Populates self.definition with some decomposition of this gate."""
196+
from qiskit.synthesis.arithmetic import multiplier_qft_r17
197+
198+
# This particular decomposition does not use any ancilla qubits.
199+
# Note that the transpiler may choose a different decomposition
200+
# based on the number of ancilla qubits available.
201+
self.definition = multiplier_qft_r17(self.num_state_qubits)

qiskit/transpiler/passes/synthesis/hls_plugins.py

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -300,13 +300,18 @@
300300
- :class:`.ModularAdderSynthesisD00`
301301
- 0
302302
- a QFT-based adder
303+
* - ``"default"``
304+
- :class:`~.ModularAdderSynthesisDefault`
305+
- any
306+
- chooses the best algorithm based on the ancillas available
303307
304308
.. autosummary::
305309
:toctree: ../stubs/
306310
307311
ModularAdderSynthesisC04
308312
ModularAdderSynthesisD00
309313
ModularAdderSynthesisV95
314+
ModularAdderSynthesisDefault
310315
311316
Half Adder Synthesis
312317
''''''''''''''''''''
@@ -330,13 +335,18 @@
330335
- :class:`.HalfAdderSynthesisD00`
331336
- 0
332337
- a QFT-based adder
338+
* - ``"default"``
339+
- :class:`~.HalfAdderSynthesisDefault`
340+
- any
341+
- chooses the best algorithm based on the ancillas available
333342
334343
.. autosummary::
335344
:toctree: ../stubs/
336345
337346
HalfAdderSynthesisC04
338347
HalfAdderSynthesisD00
339348
HalfAdderSynthesisV95
349+
HalfAdderSynthesisDefault
340350
341351
Full Adder Synthesis
342352
''''''''''''''''''''
@@ -356,12 +366,17 @@
356366
- :class:`.FullAdderSynthesisV95`
357367
- :math:`n-1`, for :math:`n`-bit numbers
358368
- a ripple-carry adder
369+
* - ``"default"``
370+
- :class:`~.FullAdderSynthesisDefault`
371+
- any
372+
- chooses the best algorithm based on the ancillas available
359373
360374
.. autosummary::
361375
:toctree: ../stubs/
362376
363377
FullAdderSynthesisC04
364378
FullAdderSynthesisV95
379+
FullAdderSynthesisDefault
365380
366381
367382
Multiplier Synthesis
@@ -1212,10 +1227,26 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **
12121227
if not isinstance(high_level_object, ModularAdderGate):
12131228
return None
12141229

1215-
if options.get("num_clean_ancillas", 0) >= 1:
1216-
return adder_ripple_c04(high_level_object.num_state_qubits, kind="fixed")
1230+
# For up to 5 qubits, the QFT-based adder is best
1231+
if high_level_object.num_state_qubits <= 5:
1232+
decomposition = ModularAdderSynthesisD00().run(
1233+
high_level_object, coupling_map, target, qubits, **options
1234+
)
1235+
if decomposition is not None:
1236+
return decomposition
12171237

1218-
return adder_qft_d00(high_level_object.num_state_qubits, kind="fixed")
1238+
# Otherwise, the following decomposition is best (if there are enough ancillas)
1239+
if (
1240+
decomposition := ModularAdderSynthesisC04().run(
1241+
high_level_object, coupling_map, target, qubits, **options
1242+
)
1243+
) is not None:
1244+
return decomposition
1245+
1246+
# Otherwise, use the QFT-adder again
1247+
return ModularAdderSynthesisD00().run(
1248+
high_level_object, coupling_map, target, qubits, **options
1249+
)
12191250

12201251

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

12651296
num_state_qubits = high_level_object.num_state_qubits
12661297

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

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

1312-
if options.get("num_clean_ancillas", 0) >= 1:
1313-
return adder_ripple_c04(high_level_object.num_state_qubits, kind="half")
1343+
# For up to 3 qubits, ripple_v95 is better (if there are enough ancilla qubits)
1344+
if high_level_object.num_state_qubits <= 3:
1345+
decomposition = HalfAdderSynthesisV95().run(
1346+
high_level_object, coupling_map, target, qubits, **options
1347+
)
1348+
if decomposition is not None:
1349+
return decomposition
13141350

1315-
return adder_qft_d00(high_level_object.num_state_qubits, kind="half")
1351+
# The next best option is to use ripple_c04 (if there are enough ancilla qubits)
1352+
if (
1353+
decomposition := HalfAdderSynthesisC04().run(
1354+
high_level_object, coupling_map, target, qubits, **options
1355+
)
1356+
) is not None:
1357+
return decomposition
1358+
1359+
# The QFT-based adder does not require ancilla qubits and should always succeed
1360+
return HalfAdderSynthesisD00().run(
1361+
high_level_object, coupling_map, target, qubits, **options
1362+
)
13161363

13171364

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

13611408
num_state_qubits = high_level_object.num_state_qubits
13621409

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

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

13831430

1384-
class FullAdderSynthesisC04(HighLevelSynthesisPlugin):
1431+
class FullAdderSynthesisDefault(HighLevelSynthesisPlugin):
13851432
"""A ripple-carry adder with a carry-in and a carry-out bit.
13861433
1387-
This plugin name is:``FullAdder.ripple_c04`` which can be used as the key on
1434+
This plugin name is:``FullAdder.default`` which can be used as the key on
13881435
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
1436+
"""
13891437

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

1392-
The plugin supports the following plugin-specific options:
1442+
# FullAdderSynthesisC04 requires no ancilla qubits and returns better results
1443+
# than FullAdderSynthesisV95 in all cases except for n=1.
1444+
if high_level_object.num_state_qubits == 1:
1445+
decomposition = FullAdderSynthesisV95().run(
1446+
high_level_object, coupling_map, target, qubits, **options
1447+
)
1448+
if decomposition is not None:
1449+
return decomposition
1450+
1451+
return FullAdderSynthesisC04().run(
1452+
high_level_object, coupling_map, target, qubits, **options
1453+
)
13931454

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

1456+
class FullAdderSynthesisC04(HighLevelSynthesisPlugin):
1457+
"""A ripple-carry adder with a carry-in and a carry-out bit.
1458+
1459+
This plugin name is:``FullAdder.ripple_c04`` which can be used as the key on
1460+
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
1461+
1462+
This plugin requires no auxiliary qubits.
13961463
"""
13971464

13981465
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
@@ -1409,7 +1476,7 @@ class FullAdderSynthesisV95(HighLevelSynthesisPlugin):
14091476
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
14101477
14111478
For an adder on 2 registers with :math:`n` qubits each, this plugin requires at
1412-
least :math:`n-1` clean auxiliary qubit.
1479+
least :math:`n-1` clean auxiliary qubits.
14131480
14141481
The plugin supports the following plugin-specific options:
14151482
@@ -1422,8 +1489,8 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **
14221489

14231490
num_state_qubits = high_level_object.num_state_qubits
14241491

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

14291496
return adder_ripple_v95(num_state_qubits, kind="full")
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
fixes:
3+
- |
4+
Added default definitions for :class:`.FullAdderGate`, :class:`.HalfAdderGate`,
5+
:class:`.ModularAdderGate` and :class:`.MultiplierGate` gates, allowing to
6+
contruct :class:`.Operator`\s from quantum circuits containing these gates.
7+
- |
8+
Fixed the number of clean ancilla qubits required by
9+
:class:`.FullAdderSynthesisV95`, :class:`.HalfAdderSynthesisV95`, and
10+
:class:`.ModularAdderSynthesisV95` plugins.
11+
- |
12+
Added missing :class:`.FullAdderSynthesisDefault` plugin that chooses the best
13+
decomposition for :class:`.FullAdderGate` based on the number of clean ancilla qubits
14+
available.
15+
- |
16+
Fixed :class:`.HalfAdderSynthesisDefault` and :class:`.ModularAdderSynthesisDefault`
17+
plugins, for :class:`.HalfAdderGate` and :class:`.ModularAdderGate` respectively,
18+
to choose the best decomposition based on the number of clean ancilla qubits available.

0 commit comments

Comments
 (0)