diff --git a/docs/apidoc/synthesis_aqc.rst b/docs/apidoc/synthesis_aqc.rst index 0ce100071a52..1be57fbc8182 100644 --- a/docs/apidoc/synthesis_aqc.rst +++ b/docs/apidoc/synthesis_aqc.rst @@ -1,6 +1,6 @@ -.. _qiskit-transpiler-synthesis-aqc: +.. _qiskit-synthesis-unitary-aqc: -.. automodule:: qiskit.transpiler.synthesis.aqc +.. automodule:: qiskit.synthesis.unitary.aqc :no-members: :no-inherited-members: :no-special-members: diff --git a/pyproject.toml b/pyproject.toml index d42b249a1058..96b3477921ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,7 +70,7 @@ Changelog = "https://qiskit.org/documentation/release_notes.html" [project.entry-points."qiskit.unitary_synthesis"] default = "qiskit.transpiler.passes.synthesis.unitary_synthesis:DefaultUnitarySynthesis" -aqc = "qiskit.transpiler.synthesis.aqc.aqc_plugin:AQCSynthesisPlugin" +aqc = "qiskit.transpiler.passes.synthesis.aqc_plugin:AQCSynthesisPlugin" sk = "qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis:SolovayKitaevSynthesis" [project.entry-points."qiskit.synthesis"] diff --git a/qiskit/circuit/library/generalized_gates/linear_function.py b/qiskit/circuit/library/generalized_gates/linear_function.py index b90652255312..3097a4952d77 100644 --- a/qiskit/circuit/library/generalized_gates/linear_function.py +++ b/qiskit/circuit/library/generalized_gates/linear_function.py @@ -30,7 +30,7 @@ class LinearFunction(Gate): as a n x n matrix of 0s and 1s in numpy array format. A linear function can be synthesized into CX and SWAP gates using the Patel–Markov–Hayes - algorithm, as implemented in :func:`~qiskit.transpiler.synthesis.cnot_synth` + algorithm, as implemented in :func:`~qiskit.synthesis.synth_cnot_count_full_pmh` based on reference [1]. For efficiency, the internal n x n matrix is stored in the format expected diff --git a/qiskit/synthesis/__init__.py b/qiskit/synthesis/__init__.py index f4d6a73f2a70..3c6df12c92d1 100644 --- a/qiskit/synthesis/__init__.py +++ b/qiskit/synthesis/__init__.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2017 - 2023. +# (C) Copyright IBM 2017, 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 @@ -43,6 +43,7 @@ .. autofunction:: synth_cz_depth_line_mr .. autofunction:: synth_cx_cz_depth_line_my +.. autofunction:: synth_cnot_phase_aam Permutation Synthesis ===================== @@ -89,6 +90,11 @@ .. autofunction:: synth_qft_line +Unitary Synthesis +================= + +The Approximate Quantum Compiler is available here: :mod:`qiskit.synthesis.unitary.aqc` + """ from .evolution import ( @@ -126,3 +132,4 @@ from .stabilizer import synth_stabilizer_layers, synth_stabilizer_depth_lnn from .discrete_basis import SolovayKitaevDecomposition, generate_basic_approximations from .qft import synth_qft_line +from .unitary import aqc diff --git a/qiskit/synthesis/unitary/__init__.py b/qiskit/synthesis/unitary/__init__.py new file mode 100644 index 000000000000..ebc27df47226 --- /dev/null +++ b/qiskit/synthesis/unitary/__init__.py @@ -0,0 +1,13 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 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 +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Module containing unitary synthesis methods.""" diff --git a/qiskit/synthesis/unitary/aqc/__init__.py b/qiskit/synthesis/unitary/aqc/__init__.py new file mode 100644 index 000000000000..abd4253d75d2 --- /dev/null +++ b/qiskit/synthesis/unitary/aqc/__init__.py @@ -0,0 +1,177 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2022. +# +# 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 +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +r""" +===================================================================== +Approximate Quantum Compiler (:mod:`qiskit.synthesis.unitary.aqc`) +===================================================================== + +.. currentmodule:: qiskit.synthesis.unitary.aqc + +Implementation of Approximate Quantum Compiler as described in the paper [1]. + +Interface +========= + +The main public interface of this module is reached by passing ``unitary_synthesis_method='aqc'`` to +:func:`~.compiler.transpile`. This will swap the synthesis method to use +:class:`~.transpiler.passes.synthesis.AQCSynthesisPlugin`. +The individual classes are: + +.. autosummary:: + :toctree: ../stubs + :template: autosummary/class_no_inherited_members.rst + + AQC + ApproximateCircuit + ApproximatingObjective + CNOTUnitCircuit + CNOTUnitObjective + DefaultCNOTUnitObjective + FastCNOTUnitObjective + + +Mathematical Detail +=================== + +We are interested in compiling a quantum circuit, which we formalize as finding the best +circuit representation in terms of an ordered gate sequence of a target unitary matrix +:math:`U\in U(d)`, with some additional hardware constraints. In particular, we look at +representations that could be constrained in terms of hardware connectivity, as well +as gate depth, and we choose a gate basis in terms of CNOT and rotation gates. +We recall that the combination of CNOT and rotation gates is universal in :math:`SU(d)` and +therefore it does not limit compilation. + +To properly define what we mean by best circuit representation, we define the metric +as the Frobenius norm between the unitary matrix of the compiled circuit :math:`V` and +the target unitary matrix :math:`U`, i.e., :math:`\|V - U\|_{\mathrm{F}}`. This choice +is motivated by mathematical programming considerations, and it is related to other +formulations that appear in the literature. Let's take a look at the problem in more details. + +Let :math:`n` be the number of qubits and :math:`d=2^n`. Given a CNOT structure :math:`ct` +and a vector of rotation angles :math:`\theta`, the parametric circuit forms a matrix +:math:`Vct(\theta)\in SU(d)`. If we are given a target circuit forming a matrix +:math:`U\in SU(d)`, then we would like to compute + +.. math:: + + \mathrm{argmax}_{\theta}\frac{1}{d}|\langle Vct(\theta),U\rangle| + +where the inner product is the Frobenius inner product. Note that +:math:`|\langle V,U\rangle|\leq d` for all unitaries :math:`U` and :math:`V`, so the objective +has range in :math:`[0,1]`. + +Our strategy is to maximize + +.. math:: + + \frac{1}{d}\Re \langle Vct(\theta),U\rangle + +using its gradient. We will now discuss the specifics by going through an example. + +While the range of :math:`Vct` is a subset of :math:`SU(d)` by construction, the target +circuit may form a general unitary matrix. However, for any :math:`U\in U(d)`, + +.. math:: + + \frac{\exp(2\pi i k/d)}{\det(U)^{1/d}}U\in SU(d)\text{ for all }k\in\{0,\ldots,d-1\}. + +Thus, we should normalize the target circuit by its global phase and then approximately +compile the normalized circuit. We can add the global phase back in afterwards. + +In the algorithm let :math:`U'` denote the un-normalized target matrix and :math:`U` +the normalized target matrix. Now that we have :math:`U`, we give the gradient function +to the Nesterov's method optimizer and compute :math:`\theta`. + +To add the global phase back in, we can form the control circuit as + +.. math:: + + \frac{\langle Vct(\theta),U'\rangle}{|\langle Vct(\theta),U'\rangle|}Vct(\theta). + +Note that while we optimized using Nesterov's method in the paper, this was for its convergence +guarantees, not its speed in practice. It is much faster to use L-BFGS which is used as a +default optimizer in this implementation. + +A basic usage of the AQC algorithm should consist of the following steps:: + + # Define a target circuit as a unitary matrix + unitary = ... + + # Define a number of qubits for the algorithm, at least 3 qubits + num_qubits = int(round(np.log2(unitary.shape[0]))) + + # Choose a layout of the CNOT structure for the approximate circuit, e.g. ``spin`` for + # a linear layout. + layout = options.get("layout") or "spin" + + # Choose a connectivity type, e.g. ``full`` for full connectivity between qubits. + connectivity = options.get("connectivity") or "full" + + # Define a targeted depth of the approximate circuit in the number of CNOT units. + depth = int(options.get("depth") or 0) + + # Generate a network made of CNOT units + cnots = make_cnot_network( + num_qubits=num_qubits, + network_layout=layout, + connectivity_type=connectivity, + depth=depth + ) + + # Create an optimizer to be used by AQC + optimizer = partial(scipy.optimize.minimize, method="L-BFGS-B") + + # Create an instance + aqc = AQC(optimizer) + + # Create a template circuit that will approximate our target circuit + approximate_circuit = CNOTUnitCircuit(num_qubits=num_qubits, cnots=cnots) + + # Create an objective that defines our optimization problem + approximating_objective = DefaultCNOTUnitObjective(num_qubits=num_qubits, cnots=cnots) + + # Run optimization process to compile the unitary + aqc.compile_unitary( + target_matrix=unitary, + approximate_circuit=approximate_circuit, + approximating_objective=approximating_objective + ) + +Now ``approximate_circuit`` is a circuit that approximates the target unitary to a certain +degree and can be used instead of the original matrix. + +This uses a helper function, :obj:`make_cnot_network`. + +.. autofunction:: make_cnot_network + +One can take advantage of accelerated version of objective function. It implements the same +mathematical algorithm as the default one ``DefaultCNOTUnitObjective`` but runs several times +faster. Instantiation of accelerated objective function class is similar to the default case: + + # Create an objective that defines our optimization problem + approximating_objective = FastCNOTUnitObjective(num_qubits=num_qubits, cnots=cnots) + +The rest of the code in the above example does not change. + +References: + + [1]: Liam Madden, Andrea Simonetto, Best Approximate Quantum Compiling Problems. + `arXiv:2106.05649 `_ +""" + +from .approximate import ApproximateCircuit, ApproximatingObjective +from .aqc import AQC +from .cnot_structures import make_cnot_network +from .cnot_unit_circuit import CNOTUnitCircuit +from .cnot_unit_objective import CNOTUnitObjective, DefaultCNOTUnitObjective +from .fast_gradient.fast_gradient import FastCNOTUnitObjective diff --git a/qiskit/transpiler/synthesis/aqc/approximate.py b/qiskit/synthesis/unitary/aqc/approximate.py similarity index 98% rename from qiskit/transpiler/synthesis/aqc/approximate.py rename to qiskit/synthesis/unitary/aqc/approximate.py index 6c3f11fb71fc..6294196b13c1 100644 --- a/qiskit/transpiler/synthesis/aqc/approximate.py +++ b/qiskit/synthesis/unitary/aqc/approximate.py @@ -15,7 +15,7 @@ from typing import Optional, SupportsFloat import numpy as np -from qiskit import QuantumCircuit +from qiskit.circuit.quantumcircuit import QuantumCircuit class ApproximateCircuit(QuantumCircuit, ABC): diff --git a/qiskit/transpiler/synthesis/aqc/aqc.py b/qiskit/synthesis/unitary/aqc/aqc.py similarity index 100% rename from qiskit/transpiler/synthesis/aqc/aqc.py rename to qiskit/synthesis/unitary/aqc/aqc.py diff --git a/qiskit/transpiler/synthesis/aqc/cnot_structures.py b/qiskit/synthesis/unitary/aqc/cnot_structures.py similarity index 100% rename from qiskit/transpiler/synthesis/aqc/cnot_structures.py rename to qiskit/synthesis/unitary/aqc/cnot_structures.py diff --git a/qiskit/transpiler/synthesis/aqc/cnot_unit_circuit.py b/qiskit/synthesis/unitary/aqc/cnot_unit_circuit.py similarity index 100% rename from qiskit/transpiler/synthesis/aqc/cnot_unit_circuit.py rename to qiskit/synthesis/unitary/aqc/cnot_unit_circuit.py diff --git a/qiskit/transpiler/synthesis/aqc/cnot_unit_objective.py b/qiskit/synthesis/unitary/aqc/cnot_unit_objective.py similarity index 100% rename from qiskit/transpiler/synthesis/aqc/cnot_unit_objective.py rename to qiskit/synthesis/unitary/aqc/cnot_unit_objective.py diff --git a/qiskit/transpiler/synthesis/aqc/elementary_operations.py b/qiskit/synthesis/unitary/aqc/elementary_operations.py similarity index 97% rename from qiskit/transpiler/synthesis/aqc/elementary_operations.py rename to qiskit/synthesis/unitary/aqc/elementary_operations.py index b5739267e793..3aeba6a749f2 100644 --- a/qiskit/transpiler/synthesis/aqc/elementary_operations.py +++ b/qiskit/synthesis/unitary/aqc/elementary_operations.py @@ -15,7 +15,7 @@ import numpy as np -from qiskit.circuit.library import RXGate, RZGate, RYGate +from qiskit.circuit.library.standard_gates import RXGate, RZGate, RYGate def place_unitary(unitary: np.ndarray, n: int, j: int) -> np.ndarray: diff --git a/qiskit/transpiler/synthesis/aqc/fast_gradient/__init__.py b/qiskit/synthesis/unitary/aqc/fast_gradient/__init__.py similarity index 98% rename from qiskit/transpiler/synthesis/aqc/fast_gradient/__init__.py rename to qiskit/synthesis/unitary/aqc/fast_gradient/__init__.py index df13193f12bb..7695325baba7 100644 --- a/qiskit/transpiler/synthesis/aqc/fast_gradient/__init__.py +++ b/qiskit/synthesis/unitary/aqc/fast_gradient/__init__.py @@ -13,10 +13,10 @@ r""" ================================================================================ Fast implementation of objective function class -(:mod:`qiskit.transpiler.synthesis.aqc.fast_gradient`) +(:mod:`qiskit.synthesis.unitary.aqc.fast_gradient`) ================================================================================ -.. currentmodule:: qiskit.transpiler.synthesis.aqc.fast_gradient +.. currentmodule:: qiskit.synthesis.unitary.aqc.fast_gradient Extension to the implementation of Approximate Quantum Compiler as described in the paper [1]. diff --git a/qiskit/transpiler/synthesis/aqc/fast_gradient/fast_grad_utils.py b/qiskit/synthesis/unitary/aqc/fast_gradient/fast_grad_utils.py similarity index 100% rename from qiskit/transpiler/synthesis/aqc/fast_gradient/fast_grad_utils.py rename to qiskit/synthesis/unitary/aqc/fast_gradient/fast_grad_utils.py diff --git a/qiskit/transpiler/synthesis/aqc/fast_gradient/fast_gradient.py b/qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py similarity index 100% rename from qiskit/transpiler/synthesis/aqc/fast_gradient/fast_gradient.py rename to qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py diff --git a/qiskit/transpiler/synthesis/aqc/fast_gradient/layer.py b/qiskit/synthesis/unitary/aqc/fast_gradient/layer.py similarity index 100% rename from qiskit/transpiler/synthesis/aqc/fast_gradient/layer.py rename to qiskit/synthesis/unitary/aqc/fast_gradient/layer.py diff --git a/qiskit/transpiler/synthesis/aqc/fast_gradient/pmatrix.py b/qiskit/synthesis/unitary/aqc/fast_gradient/pmatrix.py similarity index 100% rename from qiskit/transpiler/synthesis/aqc/fast_gradient/pmatrix.py rename to qiskit/synthesis/unitary/aqc/fast_gradient/pmatrix.py diff --git a/qiskit/transpiler/passes/__init__.py b/qiskit/transpiler/passes/__init__.py index 9616b11740a3..19925f3fe147 100644 --- a/qiskit/transpiler/passes/__init__.py +++ b/qiskit/transpiler/passes/__init__.py @@ -147,6 +147,7 @@ HLSConfig SolovayKitaev SolovayKitaevSynthesis + AQCSynthesisPlugin Post Layout (Post transpile qubit selection) ============================================ @@ -253,6 +254,7 @@ from .synthesis import HLSConfig from .synthesis import SolovayKitaev from .synthesis import SolovayKitaevSynthesis +from .synthesis import AQCSynthesisPlugin # calibration from .calibration import PulseGates diff --git a/qiskit/transpiler/passes/synthesis/__init__.py b/qiskit/transpiler/passes/synthesis/__init__.py index 11540d89387a..dba751b90be8 100644 --- a/qiskit/transpiler/passes/synthesis/__init__.py +++ b/qiskit/transpiler/passes/synthesis/__init__.py @@ -17,3 +17,4 @@ from .linear_functions_synthesis import LinearFunctionsSynthesis, LinearFunctionsToPermutations from .high_level_synthesis import HighLevelSynthesis, HLSConfig from .solovay_kitaev_synthesis import SolovayKitaev, SolovayKitaevSynthesis +from .aqc_plugin import AQCSynthesisPlugin diff --git a/qiskit/transpiler/synthesis/aqc/aqc_plugin.py b/qiskit/transpiler/passes/synthesis/aqc_plugin.py similarity index 93% rename from qiskit/transpiler/synthesis/aqc/aqc_plugin.py rename to qiskit/transpiler/passes/synthesis/aqc_plugin.py index c3e67861d016..350683daa2e7 100644 --- a/qiskit/transpiler/synthesis/aqc/aqc_plugin.py +++ b/qiskit/transpiler/passes/synthesis/aqc_plugin.py @@ -106,12 +106,10 @@ def run(self, unitary, **options): # Runtime imports to avoid the overhead of these imports for # plugin discovery and only use them if the plugin is run/used from scipy.optimize import minimize - from qiskit.transpiler.synthesis.aqc.aqc import AQC - from qiskit.transpiler.synthesis.aqc.cnot_structures import make_cnot_network - from qiskit.transpiler.synthesis.aqc.cnot_unit_circuit import CNOTUnitCircuit - from qiskit.transpiler.synthesis.aqc.fast_gradient.fast_gradient import ( - FastCNOTUnitObjective, - ) + from qiskit.synthesis.unitary.aqc import AQC + from qiskit.synthesis.unitary.aqc.cnot_structures import make_cnot_network + from qiskit.synthesis.unitary.aqc.cnot_unit_circuit import CNOTUnitCircuit + from qiskit.synthesis.unitary.aqc.fast_gradient.fast_gradient import FastCNOTUnitObjective num_qubits = int(round(np.log2(unitary.shape[0]))) diff --git a/qiskit/transpiler/synthesis/aqc/__init__.py b/qiskit/transpiler/synthesis/aqc/__init__.py index 2227a78a0984..f6c51b2c1dd4 100644 --- a/qiskit/transpiler/synthesis/aqc/__init__.py +++ b/qiskit/transpiler/synthesis/aqc/__init__.py @@ -10,169 +10,28 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -r""" -===================================================================== -Approximate Quantum Compiler (:mod:`qiskit.transpiler.synthesis.aqc`) -===================================================================== - -.. currentmodule:: qiskit.transpiler.synthesis.aqc - -Implementation of Approximate Quantum Compiler as described in the paper [1]. - -Interface -========= - -The main public interface of this module is reached by passing ``unitary_synthesis_method='aqc'`` to -:obj:`~.compiler.transpile`. This will swap the synthesis method to use :obj:`AQCSynthesisPlugin`. -The individual classes are: - -.. autosummary:: - :toctree: ../stubs - :template: autosummary/class_no_inherited_members.rst - - AQC - AQCSynthesisPlugin - ApproximateCircuit - ApproximatingObjective - CNOTUnitCircuit - CNOTUnitObjective - DefaultCNOTUnitObjective - FastCNOTUnitObjective - - -Mathematical Detail -=================== - -We are interested in compiling a quantum circuit, which we formalize as finding the best -circuit representation in terms of an ordered gate sequence of a target unitary matrix -:math:`U\in U(d)`, with some additional hardware constraints. In particular, we look at -representations that could be constrained in terms of hardware connectivity, as well -as gate depth, and we choose a gate basis in terms of CNOT and rotation gates. -We recall that the combination of CNOT and rotation gates is universal in :math:`SU(d)` and -therefore it does not limit compilation. - -To properly define what we mean by best circuit representation, we define the metric -as the Frobenius norm between the unitary matrix of the compiled circuit :math:`V` and -the target unitary matrix :math:`U`, i.e., :math:`\|V - U\|_{\mathrm{F}}`. This choice -is motivated by mathematical programming considerations, and it is related to other -formulations that appear in the literature. Let's take a look at the problem in more details. - -Let :math:`n` be the number of qubits and :math:`d=2^n`. Given a CNOT structure :math:`ct` -and a vector of rotation angles :math:`\theta`, the parametric circuit forms a matrix -:math:`Vct(\theta)\in SU(d)`. If we are given a target circuit forming a matrix -:math:`U\in SU(d)`, then we would like to compute - -.. math:: - - argmax_{\theta}\frac{1}{d}|\langle Vct(\theta),U\rangle| - -where the inner product is the Frobenius inner product. Note that -:math:`|\langle V,U\rangle|\leq d` for all unitaries :math:`U` and :math:`V`, so the objective -has range in :math:`[0,1]`. - -Our strategy is to maximize - -.. math:: - - \frac{1}{d}\Re \langle Vct(\theta),U\rangle - -using its gradient. We will now discuss the specifics by going through an example. - -While the range of :math:`Vct` is a subset of :math:`SU(d)` by construction, the target -circuit may form a general unitary matrix. However, for any :math:`U\in U(d)`, - -.. math:: - - \frac{\exp(2\pi i k/d)}{\det(U)^{1/d}}U\in SU(d)\text{ for all }k\in\{0,\ldots,d-1\}. - -Thus, we should normalize the target circuit by its global phase and then approximately -compile the normalized circuit. We can add the global phase back in afterwards. - -In the algorithm let :math:`U'` denote the un-normalized target matrix and :math:`U` -the normalized target matrix. Now that we have :math:`U`, we give the gradient function -to the Nesterov's method optimizer and compute :math:`\theta`. - -To add the global phase back in, we can form the control circuit as - -.. math:: - - \frac{\langle Vct(\theta),U'\rangle}{|\langle Vct(\theta),U'\rangle|}Vct(\theta). - -Note that while we optimized using Nesterov's method in the paper, this was for its convergence -guarantees, not its speed in practice. It is much faster to use L-BFGS which is used as a -default optimizer in this implementation. - -A basic usage of the AQC algorithm should consist of the following steps:: - - # Define a target circuit as a unitary matrix - unitary = ... - - # Define a number of qubits for the algorithm, at least 3 qubits - num_qubits = int(round(np.log2(unitary.shape[0]))) - - # Choose a layout of the CNOT structure for the approximate circuit, e.g. ``spin`` for - # a linear layout. - layout = options.get("layout") or "spin" - - # Choose a connectivity type, e.g. ``full`` for full connectivity between qubits. - connectivity = options.get("connectivity") or "full" - - # Define a targeted depth of the approximate circuit in the number of CNOT units. - depth = int(options.get("depth") or 0) - - # Generate a network made of CNOT units - cnots = make_cnot_network( - num_qubits=num_qubits, - network_layout=layout, - connectivity_type=connectivity, - depth=depth - ) - - # Create an optimizer to be used by AQC - optimizer = L_BFGS_B() - - # Create an instance - aqc = AQC(optimizer) - - # Create a template circuit that will approximate our target circuit - approximate_circuit = CNOTUnitCircuit(num_qubits=num_qubits, cnots=cnots) - - # Create an objective that defines our optimization problem - approximating_objective = DefaultCNOTUnitObjective(num_qubits=num_qubits, cnots=cnots) - - # Run optimization process to compile the unitary - aqc.compile_unitary( - target_matrix=unitary, - approximate_circuit=approximate_circuit, - approximating_objective=approximating_objective - ) - -Now ``approximate_circuit`` is a circuit that approximates the target unitary to a certain -degree and can be used instead of the original matrix. - -This uses a helper function, :obj:`make_cnot_network`. - -.. autofunction:: make_cnot_network - -One can take advantage of accelerated version of objective function. It implements the same -mathematical algorithm as the default one ``DefaultCNOTUnitObjective`` but runs several times -faster. Instantiation of accelerated objective function class is similar to the default case: - - # Create an objective that defines our optimization problem - approximating_objective = FastCNOTUnitObjective(num_qubits=num_qubits, cnots=cnots) - -The rest of the code in the above example does not change. - -References: - - [1]: Liam Madden, Andrea Simonetto, Best Approximate Quantum Compiling Problems. - `arXiv:2106.05649 `_ +""" +============================ +Approximate Quantum Compiler +============================ """ -from .approximate import ApproximateCircuit, ApproximatingObjective -from .aqc import AQC -from .aqc_plugin import AQCSynthesisPlugin -from .cnot_structures import make_cnot_network -from .cnot_unit_circuit import CNOTUnitCircuit -from .cnot_unit_objective import CNOTUnitObjective, DefaultCNOTUnitObjective -from .fast_gradient.fast_gradient import FastCNOTUnitObjective +import warnings + +from qiskit.synthesis.unitary.aqc.approximate import ApproximateCircuit, ApproximatingObjective +from qiskit.synthesis.unitary.aqc import AQC +from qiskit.synthesis.unitary.aqc.cnot_structures import make_cnot_network +from qiskit.synthesis.unitary.aqc.cnot_unit_circuit import CNOTUnitCircuit +from qiskit.synthesis.unitary.aqc.cnot_unit_objective import ( + CNOTUnitObjective, + DefaultCNOTUnitObjective, +) +from qiskit.synthesis.unitary.aqc.fast_gradient.fast_gradient import FastCNOTUnitObjective +from qiskit.transpiler.passes.synthesis.aqc_plugin import AQCSynthesisPlugin + +warnings.warn( + "The qiskit.transpiler.synthesis.aqc module is deprecated since Qiskit 0.46.0 " + "and will be removed in Qiskit 1.0.", + stacklevel=2, + category=DeprecationWarning, +) diff --git a/qiskit/transpiler/synthesis/graysynth.py b/qiskit/transpiler/synthesis/graysynth.py index 7f0ab1c08ba0..5439243d84cd 100644 --- a/qiskit/transpiler/synthesis/graysynth.py +++ b/qiskit/transpiler/synthesis/graysynth.py @@ -19,12 +19,16 @@ """ -# Redirect getattrs to modules new location -# TODO: Deprecate in 0.24.0 and remove in 0.26.0 from qiskit.synthesis.linear.cnot_synth import * from qiskit.synthesis.linear_phase.cnot_phase_synth import * +from qiskit.utils.deprecation import deprecate_func +@deprecate_func( + since="0.46.0", + additional_msg="Instead, use the function ``synth_cnot_count_full_pmh`` from the module" + + "``qiskit.synthesis.linear``.", +) def cnot_synth(state, section_size=2): """ Synthesize linear reversible circuits for all-to-all architecture @@ -55,6 +59,11 @@ def cnot_synth(state, section_size=2): return synth_cnot_count_full_pmh(state, section_size=section_size) +@deprecate_func( + since="0.46.0", + additional_msg="Instead, use the function ``synth_cnot_phase_aam`` from the module" + + "``qiskit.synthesis.linear_phase``.", +) def graysynth(cnots, angles, section_size=2): """This function is an implementation of the GraySynth algorithm of Amy, Azimadeh and Mosca. diff --git a/releasenotes/notes/deprecate-transpiler-synthesis-cf4e6e6dcdb42eca.yaml b/releasenotes/notes/deprecate-transpiler-synthesis-cf4e6e6dcdb42eca.yaml new file mode 100644 index 000000000000..bb89cfbdfbe2 --- /dev/null +++ b/releasenotes/notes/deprecate-transpiler-synthesis-cf4e6e6dcdb42eca.yaml @@ -0,0 +1,14 @@ +--- +deprecations: + - | + The :mod:`qiskit.transpiler.synthesis` module is deprecated and + will be removed in a future release. The following objects have been moved: + + * :mod:`qiskit.transpiler.synthesis.aqc` has been moved to :mod:`qiskit.synthesis.unitary.aqc` + (except of :class:`qiskit.synthesis.unitary.aqc.AQCSynthesisPlugin`). + * :class:`qiskit.synthesis.unitary.aqc.AQCSynthesisPlugin` has been moved to + :class:`qiskit.transpiler.passes.synthesis.AQCSynthesisPlugin`. + * :func:`qiskit.transpiler.synthesis.graysynth` has been moved to + :func:`qiskit.synthesis.synth_cnot_phase_aam`. + * :func:`qiskit.transpiler.synthesis.cnot_synth` has been moved to + :func:`qiskit.synthesis.synth_cnot_count_full_pmh`. diff --git a/test/python/transpiler/aqc/__init__.py b/test/python/synthesis/aqc/__init__.py similarity index 100% rename from test/python/transpiler/aqc/__init__.py rename to test/python/synthesis/aqc/__init__.py diff --git a/test/python/transpiler/aqc/fast_gradient/__init__.py b/test/python/synthesis/aqc/fast_gradient/__init__.py similarity index 100% rename from test/python/transpiler/aqc/fast_gradient/__init__.py rename to test/python/synthesis/aqc/fast_gradient/__init__.py diff --git a/test/python/transpiler/aqc/fast_gradient/test_cmp_gradients.py b/test/python/synthesis/aqc/fast_gradient/test_cmp_gradients.py similarity index 92% rename from test/python/transpiler/aqc/fast_gradient/test_cmp_gradients.py rename to test/python/synthesis/aqc/fast_gradient/test_cmp_gradients.py index 27e7d96aba90..3f648d946c7b 100644 --- a/test/python/transpiler/aqc/fast_gradient/test_cmp_gradients.py +++ b/test/python/synthesis/aqc/fast_gradient/test_cmp_gradients.py @@ -17,10 +17,10 @@ import unittest from typing import Tuple from time import perf_counter -from test.python.transpiler.aqc.fast_gradient.utils_for_testing import rand_circuit, rand_su_mat +from test.python.synthesis.aqc.fast_gradient.utils_for_testing import rand_circuit, rand_su_mat import numpy as np -from qiskit.transpiler.synthesis.aqc.fast_gradient.fast_gradient import FastCNOTUnitObjective -from qiskit.transpiler.synthesis.aqc.cnot_unit_objective import DefaultCNOTUnitObjective +from qiskit.synthesis.unitary.aqc.fast_gradient.fast_gradient import FastCNOTUnitObjective +from qiskit.synthesis.unitary.aqc.cnot_unit_objective import DefaultCNOTUnitObjective from qiskit.test import QiskitTestCase diff --git a/test/python/transpiler/aqc/fast_gradient/test_layer1q.py b/test/python/synthesis/aqc/fast_gradient/test_layer1q.py similarity index 96% rename from test/python/transpiler/aqc/fast_gradient/test_layer1q.py rename to test/python/synthesis/aqc/fast_gradient/test_layer1q.py index 2c74e98e2324..2173e6d5c792 100644 --- a/test/python/transpiler/aqc/fast_gradient/test_layer1q.py +++ b/test/python/synthesis/aqc/fast_gradient/test_layer1q.py @@ -16,10 +16,10 @@ import unittest from random import randint -import test.python.transpiler.aqc.fast_gradient.utils_for_testing as tut +import test.python.synthesis.aqc.fast_gradient.utils_for_testing as tut import numpy as np -import qiskit.transpiler.synthesis.aqc.fast_gradient.layer as lr -from qiskit.transpiler.synthesis.aqc.fast_gradient.pmatrix import PMatrix +import qiskit.synthesis.unitary.aqc.fast_gradient.layer as lr +from qiskit.synthesis.unitary.aqc.fast_gradient.pmatrix import PMatrix from qiskit.test import QiskitTestCase diff --git a/test/python/transpiler/aqc/fast_gradient/test_layer2q.py b/test/python/synthesis/aqc/fast_gradient/test_layer2q.py similarity index 96% rename from test/python/transpiler/aqc/fast_gradient/test_layer2q.py rename to test/python/synthesis/aqc/fast_gradient/test_layer2q.py index b8a19ccf19ed..f97f53ad7d7c 100644 --- a/test/python/transpiler/aqc/fast_gradient/test_layer2q.py +++ b/test/python/synthesis/aqc/fast_gradient/test_layer2q.py @@ -16,10 +16,10 @@ import unittest from random import randint -import test.python.transpiler.aqc.fast_gradient.utils_for_testing as tut +import test.python.synthesis.aqc.fast_gradient.utils_for_testing as tut import numpy as np -import qiskit.transpiler.synthesis.aqc.fast_gradient.layer as lr -from qiskit.transpiler.synthesis.aqc.fast_gradient.pmatrix import PMatrix +import qiskit.synthesis.unitary.aqc.fast_gradient.layer as lr +from qiskit.synthesis.unitary.aqc.fast_gradient.pmatrix import PMatrix from qiskit.test import QiskitTestCase diff --git a/test/python/transpiler/aqc/fast_gradient/test_utils.py b/test/python/synthesis/aqc/fast_gradient/test_utils.py similarity index 94% rename from test/python/transpiler/aqc/fast_gradient/test_utils.py rename to test/python/synthesis/aqc/fast_gradient/test_utils.py index 9d31ebd8cd9a..ea84650791c6 100644 --- a/test/python/transpiler/aqc/fast_gradient/test_utils.py +++ b/test/python/synthesis/aqc/fast_gradient/test_utils.py @@ -16,13 +16,13 @@ import unittest import random -import test.python.transpiler.aqc.fast_gradient.utils_for_testing as tut +import test.python.synthesis.aqc.fast_gradient.utils_for_testing as tut import numpy as np -import qiskit.transpiler.synthesis.aqc.fast_gradient.fast_grad_utils as myu +import qiskit.synthesis.unitary.aqc.fast_gradient.fast_grad_utils as myu from qiskit.test import QiskitTestCase -from qiskit.transpiler.synthesis.aqc.elementary_operations import rx_matrix as _rx -from qiskit.transpiler.synthesis.aqc.elementary_operations import ry_matrix as _ry -from qiskit.transpiler.synthesis.aqc.elementary_operations import rz_matrix as _rz +from qiskit.synthesis.unitary.aqc.elementary_operations import rx_matrix as _rx +from qiskit.synthesis.unitary.aqc.elementary_operations import ry_matrix as _ry +from qiskit.synthesis.unitary.aqc.elementary_operations import rz_matrix as _rz class TestUtils(QiskitTestCase): diff --git a/test/python/transpiler/aqc/fast_gradient/utils_for_testing.py b/test/python/synthesis/aqc/fast_gradient/utils_for_testing.py similarity index 98% rename from test/python/transpiler/aqc/fast_gradient/utils_for_testing.py rename to test/python/synthesis/aqc/fast_gradient/utils_for_testing.py index bffc03e066bd..c6a61930141d 100644 --- a/test/python/transpiler/aqc/fast_gradient/utils_for_testing.py +++ b/test/python/synthesis/aqc/fast_gradient/utils_for_testing.py @@ -17,7 +17,7 @@ from typing import Tuple import numpy as np from scipy.stats import unitary_group -import qiskit.transpiler.synthesis.aqc.fast_gradient.fast_grad_utils as fgu +import qiskit.synthesis.unitary.aqc.fast_gradient.fast_grad_utils as fgu def relative_error(a_mat: np.ndarray, b_mat: np.ndarray) -> float: diff --git a/test/python/transpiler/aqc/sample_data.py b/test/python/synthesis/aqc/sample_data.py similarity index 100% rename from test/python/transpiler/aqc/sample_data.py rename to test/python/synthesis/aqc/sample_data.py diff --git a/test/python/transpiler/aqc/test_aqc.py b/test/python/synthesis/aqc/test_aqc.py similarity index 86% rename from test/python/transpiler/aqc/test_aqc.py rename to test/python/synthesis/aqc/test_aqc.py index c54de8c21dc7..97c70eb3b587 100644 --- a/test/python/transpiler/aqc/test_aqc.py +++ b/test/python/synthesis/aqc/test_aqc.py @@ -15,7 +15,7 @@ from functools import partial import unittest -from test.python.transpiler.aqc.sample_data import ORIGINAL_CIRCUIT, INITIAL_THETAS +from test.python.synthesis.aqc.sample_data import ORIGINAL_CIRCUIT, INITIAL_THETAS from ddt import ddt, data import numpy as np @@ -23,11 +23,11 @@ from qiskit.quantum_info import Operator from qiskit.test import QiskitTestCase -from qiskit.transpiler.synthesis.aqc.aqc import AQC -from qiskit.transpiler.synthesis.aqc.cnot_structures import make_cnot_network -from qiskit.transpiler.synthesis.aqc.cnot_unit_circuit import CNOTUnitCircuit -from qiskit.transpiler.synthesis.aqc.cnot_unit_objective import DefaultCNOTUnitObjective -from qiskit.transpiler.synthesis.aqc.fast_gradient.fast_gradient import FastCNOTUnitObjective +from qiskit.synthesis.unitary.aqc.aqc import AQC +from qiskit.synthesis.unitary.aqc.cnot_structures import make_cnot_network +from qiskit.synthesis.unitary.aqc.cnot_unit_circuit import CNOTUnitCircuit +from qiskit.synthesis.unitary.aqc.cnot_unit_objective import DefaultCNOTUnitObjective +from qiskit.synthesis.unitary.aqc.fast_gradient.fast_gradient import FastCNOTUnitObjective @ddt @@ -134,6 +134,12 @@ def test_aqc_determinant_minus_one(self): error = 0.5 * (np.linalg.norm(approx_matrix - target_matrix, "fro") ** 2) self.assertTrue(error < 1e-3) + def test_deprecation(self): + """Test that importing this module is deprecated.""" + # pylint: disable = unused-import + with self.assertWarns(DeprecationWarning): + import qiskit.transpiler.synthesis.aqc + if __name__ == "__main__": unittest.main() diff --git a/test/python/transpiler/aqc/test_aqc_plugin.py b/test/python/synthesis/aqc/test_aqc_plugin.py similarity index 97% rename from test/python/transpiler/aqc/test_aqc_plugin.py rename to test/python/synthesis/aqc/test_aqc_plugin.py index b5f3bf1858f4..b55e6ffb379b 100644 --- a/test/python/transpiler/aqc/test_aqc_plugin.py +++ b/test/python/synthesis/aqc/test_aqc_plugin.py @@ -23,7 +23,7 @@ from qiskit.test import QiskitTestCase from qiskit.transpiler import PassManager from qiskit.transpiler.passes import UnitarySynthesis -from qiskit.transpiler.synthesis.aqc.aqc_plugin import AQCSynthesisPlugin +from qiskit.transpiler.passes.synthesis import AQCSynthesisPlugin class TestAQCSynthesisPlugin(QiskitTestCase): diff --git a/test/python/transpiler/aqc/test_cnot_networks.py b/test/python/synthesis/aqc/test_cnot_networks.py similarity index 93% rename from test/python/transpiler/aqc/test_cnot_networks.py rename to test/python/synthesis/aqc/test_cnot_networks.py index 3f4b17943e7c..f844d1a4b6e5 100644 --- a/test/python/transpiler/aqc/test_cnot_networks.py +++ b/test/python/synthesis/aqc/test_cnot_networks.py @@ -12,13 +12,13 @@ """ Tests building up CNOT unit structures. """ -from test.python.transpiler.aqc.sample_data import CARTAN_4, CARTAN_3 +from test.python.synthesis.aqc.sample_data import CARTAN_4, CARTAN_3 import numpy as np from ddt import ddt, data, unpack from qiskit.test import QiskitTestCase -from qiskit.transpiler.synthesis.aqc import make_cnot_network +from qiskit.synthesis.unitary.aqc import make_cnot_network @ddt diff --git a/test/python/transpiler/aqc/test_gradient.py b/test/python/synthesis/aqc/test_gradient.py similarity index 93% rename from test/python/transpiler/aqc/test_gradient.py rename to test/python/synthesis/aqc/test_gradient.py index 1c2235441b55..584b0f3c7313 100644 --- a/test/python/transpiler/aqc/test_gradient.py +++ b/test/python/synthesis/aqc/test_gradient.py @@ -14,11 +14,11 @@ """ import unittest -from test.python.transpiler.aqc.sample_data import ORIGINAL_CIRCUIT, INITIAL_THETAS +from test.python.synthesis.aqc.sample_data import ORIGINAL_CIRCUIT, INITIAL_THETAS import numpy as np from qiskit.test import QiskitTestCase -from qiskit.transpiler.synthesis.aqc.cnot_structures import make_cnot_network -from qiskit.transpiler.synthesis.aqc.cnot_unit_objective import DefaultCNOTUnitObjective +from qiskit.synthesis.unitary.aqc.cnot_structures import make_cnot_network +from qiskit.synthesis.unitary.aqc.cnot_unit_objective import DefaultCNOTUnitObjective class TestGradientAgainstFiniteDiff(QiskitTestCase): diff --git a/test/python/synthesis/test_cnot_phase_synthesis.py b/test/python/synthesis/test_cnot_phase_synthesis.py index d96d7cbc8448..8bf6c28a8cc3 100644 --- a/test/python/synthesis/test_cnot_phase_synthesis.py +++ b/test/python/synthesis/test_cnot_phase_synthesis.py @@ -75,7 +75,11 @@ def test_gray_synth(self, synth_func): [0, 1, 0, 0, 1, 0], ] angles = ["s", "t", "z", "s", "t", "t"] - c_gray = synth_func(cnots, angles) + if synth_func.__name__ == "graysynth": + with self.assertWarns(DeprecationWarning): + c_gray = synth_func(cnots, angles) + else: + c_gray = synth_func(cnots, angles) unitary_gray = UnitaryGate(Operator(c_gray)) # Create the circuit displayed above: @@ -133,7 +137,11 @@ def test_paper_example(self, synth_func): """ cnots = [[0, 1, 1, 1, 1, 1], [1, 0, 0, 1, 1, 1], [1, 0, 0, 1, 0, 0], [0, 0, 1, 0, 1, 0]] angles = ["t"] * 6 - c_gray = synth_func(cnots, angles) + if synth_func.__name__ == "graysynth": + with self.assertWarns(DeprecationWarning): + c_gray = synth_func(cnots, angles) + else: + c_gray = synth_func(cnots, angles) unitary_gray = UnitaryGate(Operator(c_gray)) # Create the circuit displayed above: @@ -186,7 +194,11 @@ def test_ccz(self, synth_func): """ cnots = [[1, 0, 0, 1, 1, 0, 1], [0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 1, 1, 1]] angles = ["t", "t", "t", "tdg", "tdg", "tdg", "t"] - c_gray = synth_func(cnots, angles) + if synth_func.__name__ == "graysynth": + with self.assertWarns(DeprecationWarning): + c_gray = synth_func(cnots, angles) + else: + c_gray = synth_func(cnots, angles) unitary_gray = UnitaryGate(Operator(c_gray)) # Create the circuit displayed above: @@ -252,7 +264,11 @@ def test_patel_markov_hayes(self, synth_func): [1, 1, 0, 1, 1, 1], [0, 0, 1, 1, 1, 0], ] - c_patel = synth_func(state) + if synth_func.__name__ == "cnot_synth": + with self.assertWarns(DeprecationWarning): + c_patel = synth_func(state) + else: + c_patel = synth_func(state) unitary_patel = UnitaryGate(Operator(c_patel)) # Create the circuit displayed above: