Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
04ef67c
extending HighLevelSynthesis and HighLevelSynthesisPlugin interface w…
alexanderivrii Jul 12, 2023
e4fad1a
improving docstrings and adjusting arguments to implemented plugins
alexanderivrii Jul 23, 2023
ac901a9
adding test that the coupling map gets passed correctly
alexanderivrii Jul 23, 2023
11a501f
Merge branch 'main' into hls-with-target
alexanderivrii Jul 23, 2023
8d224b2
release notes
alexanderivrii Jul 23, 2023
29611eb
docs
alexanderivrii Jul 23, 2023
b1f191f
Merge branch 'main' into hls-with-target
alexanderivrii Jul 25, 2023
11f9236
using DAGCircuit's find_bit
alexanderivrii Jul 25, 2023
3ded40c
removing debug print
alexanderivrii Jul 25, 2023
54bd00b
updating plugin interface docs
alexanderivrii Jul 25, 2023
5a1365a
improvements to release notes
alexanderivrii Jul 25, 2023
5d85f9c
removing unused variable
alexanderivrii Jul 25, 2023
e3211fb
adding use_qubit_indices; updating release notes and tests
alexanderivrii Jul 27, 2023
6d3f1c4
Merge branch 'main' into hls-with-target
alexanderivrii Jul 27, 2023
5044ec0
Update qiskit/transpiler/passes/synthesis/plugin.py
alexanderivrii Jul 28, 2023
b665af7
Update qiskit/transpiler/passes/synthesis/plugin.py
alexanderivrii Jul 28, 2023
5111afa
Update qiskit/transpiler/preset_passmanagers/common.py
alexanderivrii Jul 28, 2023
3b77b6b
Update qiskit/transpiler/preset_passmanagers/common.py
alexanderivrii Jul 28, 2023
5a159e3
Update qiskit/transpiler/preset_passmanagers/common.py
alexanderivrii Jul 28, 2023
af27ba8
Update qiskit/transpiler/preset_passmanagers/common.py
mtreinish Aug 4, 2023
9a70e46
Merge branch 'main' into hls-with-target
alexanderivrii Aug 11, 2023
a84cd62
setting coupling_map to None
alexanderivrii Aug 11, 2023
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
71 changes: 53 additions & 18 deletions qiskit/transpiler/passes/synthesis/high_level_synthesis.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
# (C) Copyright IBM 2022, 2023.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand All @@ -13,9 +13,12 @@

"""Synthesize higher-level objects."""

from typing import Optional

from qiskit.converters import circuit_to_dag
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.target import Target
from qiskit.transpiler.coupling import CouplingMap
from qiskit.dagcircuit.dagcircuit import DAGCircuit
from qiskit.transpiler.exceptions import TranspilerError

Expand Down Expand Up @@ -119,7 +122,27 @@ class HighLevelSynthesis(TransformationPass):
``default`` methods for all other high-level objects, including ``op_a``-objects.
"""

def __init__(self, hls_config=None):
def __init__(
self,
hls_config: Optional[HLSConfig] = None,
coupling_map: Optional[CouplingMap] = None,
target: Optional[Target] = None,
use_qubit_indices: bool = False,
):
"""
HighLevelSynthesis initializer.

Args:
hls_config: Optional, the high-level-synthesis config that specifies synthesis methods
and parameters for various high-level-objects in the circuit. If it is not specified,
the default synthesis methods and parameters will be used.
coupling_map: Optional, directed graph represented as a coupling map.
target: Optional, the backend target to use for this pass. If it is specified,
it will be used instead of the coupling map.
use_qubit_indices: a flag indicating whether this synthesis pass is running before or after
the layout is set, that is, whether the qubit indices of higher-level-objects correspond
to qubit indices on the target backend.
"""
super().__init__()

if hls_config is not None:
Expand All @@ -129,6 +152,11 @@ def __init__(self, hls_config=None):
# to synthesize Operations (when available).
self.hls_config = HLSConfig(True)
self.hls_plugin_manager = HighLevelSynthesisPluginManager()
self._coupling_map = coupling_map
self._target = target
self._use_qubit_indices = use_qubit_indices
if target is not None:
self._coupling_map = self._target.build_coupling_map()

def run(self, dag: DAGCircuit) -> DAGCircuit:
"""Run the HighLevelSynthesis pass on `dag`.
Expand All @@ -141,7 +169,6 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
Raises:
TranspilerError: when the specified synthesis method is not available.
"""

for node in dag.op_nodes():
if node.name in self.hls_config.methods.keys():
# the operation's name appears in the user-provided config,
Expand Down Expand Up @@ -187,9 +214,17 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
else:
plugin_method = plugin_specifier

# ToDo: similarly to UnitarySynthesis, we should pass additional parameters
# e.g. coupling_map to the synthesis algorithm.
decomposition = plugin_method.run(node.op, **plugin_args)
qubits = (
[dag.find_bit(x).index for x in node.qargs] if self._use_qubit_indices else None
)

decomposition = plugin_method.run(
node.op,
coupling_map=self._coupling_map,
target=self._target,
qubits=qubits,
**plugin_args,
)

# The synthesis methods that are not suited for the given higher-level-object
# will return None, in which case the next method in the list will be used.
Expand All @@ -211,7 +246,7 @@ class DefaultSynthesisClifford(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given Clifford."""
decomposition = synth_clifford_full(high_level_object)
return decomposition
Expand All @@ -224,7 +259,7 @@ class AGSynthesisClifford(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given Clifford."""
decomposition = synth_clifford_ag(high_level_object)
return decomposition
Expand All @@ -241,7 +276,7 @@ class BMSynthesisClifford(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given Clifford."""
if high_level_object.num_qubits <= 3:
decomposition = synth_clifford_bm(high_level_object)
Expand All @@ -258,7 +293,7 @@ class GreedySynthesisClifford(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given Clifford."""
decomposition = synth_clifford_greedy(high_level_object)
return decomposition
Expand All @@ -272,7 +307,7 @@ class LayerSynthesisClifford(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given Clifford."""
decomposition = synth_clifford_layers(high_level_object)
return decomposition
Expand All @@ -287,7 +322,7 @@ class LayerLnnSynthesisClifford(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given Clifford."""
decomposition = synth_clifford_depth_lnn(high_level_object)
return decomposition
Expand All @@ -300,7 +335,7 @@ class DefaultSynthesisLinearFunction(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given LinearFunction."""
decomposition = synth_cnot_count_full_pmh(high_level_object.linear)
return decomposition
Expand All @@ -313,7 +348,7 @@ class KMSSynthesisLinearFunction(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given LinearFunction."""
decomposition = synth_cnot_depth_line_kms(high_level_object.linear)
return decomposition
Expand All @@ -326,7 +361,7 @@ class PMHSynthesisLinearFunction(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given LinearFunction."""
decomposition = synth_cnot_count_full_pmh(high_level_object.linear)
return decomposition
Expand All @@ -339,7 +374,7 @@ class KMSSynthesisPermutation(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given Permutation."""
decomposition = synth_permutation_depth_lnn_kms(high_level_object.pattern)
return decomposition
Expand All @@ -352,7 +387,7 @@ class BasicSynthesisPermutation(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given Permutation."""
decomposition = synth_permutation_basic(high_level_object.pattern)
return decomposition
Expand All @@ -365,7 +400,7 @@ class ACGSynthesisPermutation(HighLevelSynthesisPlugin):
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
"""

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given Permutation."""
decomposition = synth_permutation_acg(high_level_object.pattern)
return decomposition
36 changes: 25 additions & 11 deletions qiskit/transpiler/passes/synthesis/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,25 @@ def run(self, unitary, **options):
which defines the interface and contract for high-level synthesis plugins.
The primary method is
:meth:`~qiskit.transpiler.passes.synthesis.plugin.HighLevelSynthesisPlugin.run`.
It takes in a single positional argument, a "higher-level-object" to be
synthesized, which is any object of type :class:`~qiskit.circuit.Operation`
The positional argument ``high_level_object`` specifies the "higher-level-object" to
be synthesized, which is any object of type :class:`~qiskit.circuit.Operation`
(including, for example,
:class:`~qiskit.circuit.library.generalized_gates.linear_function.LinearFunction` or
:class:`~qiskit.quantum_info.operators.symplectic.clifford.Clifford`).
The keyword argument ``target`` specifies the target backend, allowing the plugin
to access all target-specific information,
such as the coupling map, the supported gate set, and so on. The keyword argument
``coupling_map`` only specifies the coupling map, and is only used when ``target``
is not specified.
The keyword argument ``qubits`` specifies the list of qubits over which the
higher-level-object is defined, in case the synthesis is done on the physical circuit.
The value of ``None`` indicates that the layout has not yet been chosen and the physical qubits
in the target or coupling map that this operation is operating on has not yet been determined.
Additionally, plugin-specific options and tunables can be specified via ``options``,
which is a free form configuration dictionary.
If your plugin has these configuration options you
should clearly document how a user should specify these configuration options
and how they're used as it's a free form field.
The method
:meth:`~qiskit.transpiler.passes.synthesis.plugin.HighLevelSynthesisPlugin.run`
is expected to return a :class:`~qiskit.circuit.QuantumCircuit` object
Expand All @@ -180,11 +194,6 @@ def run(self, unitary, **options):
The actual synthesis of higher-level objects is performed by
:class:`~qiskit.transpiler.passes.synthesis.high_level_synthesis.HighLevelSynthesis`
transpiler pass.
In the near future,
:class:`~qiskit.transpiler.passes.synthesis.plugin.HighLevelSynthesisPlugin`
will be extended with additional information necessary to run this transpiler
pass, for instance whether the plugin supports and/or requires ``coupling_map``
to perform synthesis.
For the full details refer to the
:class:`~qiskit.transpiler.passes.synthesis.plugin.HighLevelSynthesisPlugin`
documentation for all the required fields. An example plugin class would look
Expand All @@ -196,7 +205,7 @@ def run(self, unitary, **options):

class SpecialSynthesisClifford(HighLevelSynthesisPlugin):

def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
if higher_level_object.num_qubits <= 3:
return synth_clifford_bm(high_level_object)
else:
Expand Down Expand Up @@ -544,13 +553,18 @@ class HighLevelSynthesisPlugin(abc.ABC):
"""

@abc.abstractmethod
def run(self, high_level_object, **options):
def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given Operation.

Args:
high_level_object (Operation): The Operation to synthesize to a
:class:`~qiskit.dagcircuit.DAGCircuit` object
options: The optional kwargs.
:class:`~qiskit.dagcircuit.DAGCircuit` object.
coupling_map (CouplingMap): The coupling map of the backend
in case synthesis is done on a physical circuit.
target (Target): A target representing the target backend.
qubits (list): List of qubits over which the operation is defined
in case synthesis is done on a physical circuit.
options: Additional method-specific optional kwargs.

Returns:
QuantumCircuit: The quantum circuit representation of the Operation
Expand Down
27 changes: 23 additions & 4 deletions qiskit/transpiler/preset_passmanagers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,11 @@ def generate_unroll_3q(
target=target,
)
)
unroll_3q.append(HighLevelSynthesis(hls_config=hls_config))
unroll_3q.append(
HighLevelSynthesis(
hls_config=hls_config, coupling_map=None, target=target, use_qubit_indices=False
Comment thread
mtreinish marked this conversation as resolved.
)
)
unroll_3q.append(Unroll3qOrMore(target=target, basis_gates=basis_gates))
return unroll_3q

Expand Down Expand Up @@ -422,7 +426,12 @@ def generate_translation_passmanager(
method=unitary_synthesis_method,
target=target,
),
HighLevelSynthesis(hls_config=hls_config),
HighLevelSynthesis(
hls_config=hls_config,
coupling_map=coupling_map,
target=target,
use_qubit_indices=True,
),
UnrollCustomDefinitions(sel, basis_gates=basis_gates, target=target),
BasisTranslator(sel, basis_gates, target),
]
Expand All @@ -440,7 +449,12 @@ def generate_translation_passmanager(
min_qubits=3,
target=target,
),
HighLevelSynthesis(hls_config=hls_config),
HighLevelSynthesis(
hls_config=hls_config,
coupling_map=coupling_map,
target=target,
use_qubit_indices=True,
),
Unroll3qOrMore(target=target, basis_gates=basis_gates),
Collect2qBlocks(),
Collect1qRuns(),
Expand All @@ -456,7 +470,12 @@ def generate_translation_passmanager(
method=unitary_synthesis_method,
target=target,
),
HighLevelSynthesis(hls_config=hls_config),
HighLevelSynthesis(
hls_config=hls_config,
coupling_map=coupling_map,
target=target,
use_qubit_indices=True,
),
]
else:
raise TranspilerError("Invalid translation method %s." % method)
Expand Down
28 changes: 28 additions & 0 deletions releasenotes/notes/add-target-to-hls.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
features:
- |
Added the arguments ``coupling_map``, ``target`` and ``use_qubit_indices`` to
:class:`.HighLevelSynthesis` transpiler pass. The argument ``target`` specifies
the target backend, allowing the synthesis plugins called within the pass to
access all target-specific information, such as the coupling map and the supported
gate set. The argument ``coupling_map`` only specifies the coupling map, and is only
used when ``target`` is not specified. The argument ``use_qubit_indices`` indicates
whether the high-level-synthesis pass is running before or after the layout is set,
that is, whether the qubit indices of higher-level-objects correspond to qubit indices
on the target backend.
- |
Added the arguments ``coupling_map``, ``target`` and ``qubits`` to :class:`.HighLevelSynthesisPlugin`.
The positional argument ``target`` specifies the target backend, allowing the plugin
to access all target-specific information,
such as the coupling map, the supported gate set, and so on. The positional argument
``coupling_map`` only specifies the coupling map, and is only used when ``target``
is not specified.
The positional argument ``qubits`` specifies the list of qubits over which the
higher-level-object is defined, in case the synthesis is done on the physical circuit.
The value of ``None`` indicates that the layout has not yet been chosen.

This enables a cleaner separation of synthesis plugins options into general interface options
for plugins (that is, ``coupling_map``, ``target``, and ``qubits``) and into plugin-specific options
(a free form configuration dictionary specified via ``options``). It is worthwhile to note that this
change is backward-compatible, if the options ``coupling_map``, etc. are not explicitly added to
the plugin's ``run()`` method, they will appear as part of ``options``.
Loading