Skip to content

Commit

Permalink
Merge branch 'main' into expose-circuit-data
Browse files Browse the repository at this point in the history
  • Loading branch information
raynelfss authored Aug 22, 2024
2 parents 1c5d39b + 072548f commit 0d00c3d
Show file tree
Hide file tree
Showing 17 changed files with 1,504 additions and 682 deletions.
40 changes: 23 additions & 17 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,29 @@ aqc = "qiskit.transpiler.passes.synthesis.aqc_plugin:AQCSynthesisPlugin"
sk = "qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis:SolovayKitaevSynthesis"

[project.entry-points."qiskit.synthesis"]
"clifford.default" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford"
"clifford.ag" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford"
"clifford.bm" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford"
"clifford.greedy" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford"
"clifford.layers" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford"
"clifford.lnn" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford"
"linear_function.default" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction"
"linear_function.kms" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction"
"linear_function.pmh" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction"
"permutation.default" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation"
"permutation.kms" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation"
"permutation.basic" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation"
"permutation.acg" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation"
"qft.full" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:QFTSynthesisFull"
"qft.line" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:QFTSynthesisLine"
"qft.default" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:QFTSynthesisFull"
"permutation.token_swapper" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:TokenSwapperSynthesisPermutation"
"clifford.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:DefaultSynthesisClifford"
"clifford.ag" = "qiskit.transpiler.passes.synthesis.hls_plugins:AGSynthesisClifford"
"clifford.bm" = "qiskit.transpiler.passes.synthesis.hls_plugins:BMSynthesisClifford"
"clifford.greedy" = "qiskit.transpiler.passes.synthesis.hls_plugins:GreedySynthesisClifford"
"clifford.layers" = "qiskit.transpiler.passes.synthesis.hls_plugins:LayerSynthesisClifford"
"clifford.lnn" = "qiskit.transpiler.passes.synthesis.hls_plugins:LayerLnnSynthesisClifford"
"linear_function.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:DefaultSynthesisLinearFunction"
"linear_function.kms" = "qiskit.transpiler.passes.synthesis.hls_plugins:KMSSynthesisLinearFunction"
"linear_function.pmh" = "qiskit.transpiler.passes.synthesis.hls_plugins:PMHSynthesisLinearFunction"
"mcx.n_dirty_i15" = "qiskit.transpiler.passes.synthesis.hls_plugins:MCXSynthesisNDirtyI15"
"mcx.n_clean_m15" = "qiskit.transpiler.passes.synthesis.hls_plugins:MCXSynthesisNCleanM15"
"mcx.1_clean_b95" = "qiskit.transpiler.passes.synthesis.hls_plugins:MCXSynthesis1CleanB95"
"mcx.gray_code" = "qiskit.transpiler.passes.synthesis.hls_plugins:MCXSynthesisGrayCode"
"mcx.noaux_v24" = "qiskit.transpiler.passes.synthesis.hls_plugins:MCXSynthesisNoAuxV24"
"mcx.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:MCXSynthesisDefault"
"permutation.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:BasicSynthesisPermutation"
"permutation.kms" = "qiskit.transpiler.passes.synthesis.hls_plugins:KMSSynthesisPermutation"
"permutation.basic" = "qiskit.transpiler.passes.synthesis.hls_plugins:BasicSynthesisPermutation"
"permutation.acg" = "qiskit.transpiler.passes.synthesis.hls_plugins:ACGSynthesisPermutation"
"qft.full" = "qiskit.transpiler.passes.synthesis.hls_plugins:QFTSynthesisFull"
"qft.line" = "qiskit.transpiler.passes.synthesis.hls_plugins:QFTSynthesisLine"
"qft.default" = "qiskit.transpiler.passes.synthesis.hls_plugins:QFTSynthesisFull"
"permutation.token_swapper" = "qiskit.transpiler.passes.synthesis.hls_plugins:TokenSwapperSynthesisPermutation"

[project.entry-points."qiskit.transpiler.init"]
default = "qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager"
Expand Down
77 changes: 53 additions & 24 deletions qiskit/circuit/library/standard_gates/x.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import _ctrl_state_to_int, with_gate_array, with_controlled_gate_array
from qiskit._accelerate.circuit import StandardGate
from qiskit.utils.deprecation import deprecate_func

_X_ARRAY = [[0, 1], [1, 0]]
_SX_ARRAY = [[0.5 + 0.5j, 0.5 - 0.5j], [0.5 - 0.5j, 0.5 + 0.5j]]
Expand Down Expand Up @@ -1168,6 +1169,19 @@ def inverse(self, annotated: bool = False):
return MCXGate(num_ctrl_qubits=self.num_ctrl_qubits, ctrl_state=self.ctrl_state)

@staticmethod
@deprecate_func(
additional_msg=(
"For an MCXGate it is no longer possible to know the number of ancilla qubits "
"that would be eventually used by the transpiler when the gate is created. "
"Instead, it is recommended to use MCXGate and let HighLevelSynthesis choose "
"the best synthesis method depending on the number of ancilla qubits available. "
"However, if a specific synthesis method using a specific number of ancilla "
"qubits is require, one can create a custom gate by calling the corresponding "
"synthesis function directly."
),
since="1.3",
pending=True,
)
def get_num_ancilla_qubits(num_ctrl_qubits: int, mode: str = "noancilla") -> int:
"""Get the number of required ancilla qubits without instantiating the class.
Expand All @@ -1185,23 +1199,10 @@ def get_num_ancilla_qubits(num_ctrl_qubits: int, mode: str = "noancilla") -> int
def _define(self):
"""This definition is based on MCPhaseGate implementation."""
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.synthesis.multi_controlled import synth_mcx_noaux_v24

q = QuantumRegister(self.num_qubits, name="q")
qc = QuantumCircuit(q)
if self.num_qubits == 4:
qc._append(C3XGate(), q[:], [])
self.definition = qc
elif self.num_qubits == 5:
qc._append(C4XGate(), q[:], [])
self.definition = qc
else:
q_controls = list(range(self.num_ctrl_qubits))
q_target = self.num_ctrl_qubits
qc.h(q_target)
qc.mcp(numpy.pi, q_controls, q_target)
qc.h(q_target)
self.definition = qc
qc = synth_mcx_noaux_v24(self.num_ctrl_qubits)
self.definition = qc

@property
def num_ancilla_qubits(self):
Expand Down Expand Up @@ -1280,6 +1281,17 @@ def __new__(
return gate
return super().__new__(cls)

@deprecate_func(
additional_msg=(
"It is recommended to use MCXGate and let HighLevelSynthesis choose "
"the best synthesis method depending on the number of ancilla qubits available. "
"If this specific synthesis method is required, one can specify it using the "
"high-level-synthesis plugin `gray_code` for MCX gates, or, alternatively, "
"one can use synth_mcx_gray_code to construct the gate directly."
),
since="1.3",
pending=True,
)
def __init__(
self,
num_ctrl_qubits: int,
Expand All @@ -1305,15 +1317,9 @@ def inverse(self, annotated: bool = False):
def _define(self):
"""Define the MCX gate using the Gray code."""
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
from .u1 import MCU1Gate
from .h import HGate
from qiskit.synthesis.multi_controlled import synth_mcx_gray_code

q = QuantumRegister(self.num_qubits, name="q")
qc = QuantumCircuit(q, name=self.name)
qc._append(HGate(), [q[-1]], [])
qc._append(MCU1Gate(numpy.pi, num_ctrl_qubits=self.num_ctrl_qubits), q[:], [])
qc._append(HGate(), [q[-1]], [])
qc = synth_mcx_gray_code(self.num_ctrl_qubits)
self.definition = qc


Expand All @@ -1330,6 +1336,17 @@ class MCXRecursive(MCXGate):
2. Iten et al., 2015. https://arxiv.org/abs/1501.06911
"""

@deprecate_func(
additional_msg=(
"It is recommended to use MCXGate and let HighLevelSynthesis choose "
"the best synthesis method depending on the number of ancilla qubits available. "
"If this specific synthesis method is required, one can specify it using the "
"high-level-synthesis plugin '1_clean_b95' for MCX gates, or, alternatively, "
"one can use synth_mcx_1_clean to construct the gate directly."
),
since="1.3",
pending=True,
)
def __init__(
self,
num_ctrl_qubits: int,
Expand Down Expand Up @@ -1409,6 +1426,18 @@ def __new__(
unit=unit,
)

@deprecate_func(
additional_msg=(
"It is recommended to use MCXGate and let HighLevelSynthesis choose "
"the best synthesis method depending on the number of ancilla qubits available. "
"If this specific synthesis method is required, one can specify it using the "
"high-level-synthesis plugins `n_clean_m15` (using clean ancillas) or "
"`n_dirty_i15` (using dirty ancillas) for MCX gates. Alternatively, one can "
"use synth_mcx_n_dirty_i15 and synth_mcx_n_clean_m15 to construct the gate directly."
),
since="1.3",
pending=True,
)
def __init__(
self,
num_ctrl_qubits: int,
Expand Down
9 changes: 8 additions & 1 deletion qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -3212,10 +3212,17 @@ def decompose(
from qiskit.converters.dag_to_circuit import dag_to_circuit

dag = circuit_to_dag(self, copy_operations=True)
dag = HighLevelSynthesis().run(dag)

if gates_to_decompose is None:
# We should not rewrite the circuit using HLS when we have gates_to_decompose,
# or else HLS will rewrite all objects with available plugins (e.g., Cliffords,
# PermutationGates, and now also MCXGates)
dag = HighLevelSynthesis().run(dag)

pass_ = Decompose(gates_to_decompose)
for _ in range(reps):
dag = pass_.run(dag)

# do not copy operations, this is done in the conversion with circuit_to_dag
return dag_to_circuit(dag, copy_operations=False)

Expand Down
7 changes: 4 additions & 3 deletions qiskit/pulse/library/symbolic_pulses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1773,12 +1773,13 @@ def Square(
is the sign function with the convention :math:`\\text{sign}\\left(0\\right)=1`.
Args:
duration: Pulse length in terms of the sampling period `dt`.
amp: The magnitude of the amplitude of the square wave. Wave range is [-`amp`,`amp`].
duration: Pulse length in terms of the sampling period ``dt``.
amp: The magnitude of the amplitude of the square wave. Wave range is
:math:`\\left[-\\texttt{amp},\\texttt{amp}\\right]`.
phase: The phase of the square wave (note that this is not equivalent to the angle of
the complex amplitude).
freq: The frequency of the square wave, in terms of 1 over sampling period.
If not provided defaults to a single cycle (i.e :math:'\\frac{1}{\\text{duration}}').
If not provided defaults to a single cycle (i.e :math:`\\frac{1}{\\text{duration}}`).
The frequency is limited to the range :math:`\\left(0,0.5\\right]` (the Nyquist frequency).
angle: The angle in radians of the complex phase factor uniformly
scaling the pulse. Default value 0.
Expand Down
76 changes: 76 additions & 0 deletions qiskit/quantum_info/operators/operator_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2024.
#
# 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.

"""
Additional utilities for Operators.
"""

from __future__ import annotations

from qiskit.quantum_info import Operator
from qiskit.quantum_info.operators.predicates import matrix_equal


def _equal_with_ancillas(
op1: Operator,
op2: Operator,
ancilla_qubits: list[int],
ignore_phase: bool = False,
rtol: float | None = None,
atol: float | None = None,
) -> bool:
r"""Test if two Operators are equal on the subspace where ancilla qubits
are :math:`|0\rangle`.
Args:
op1 (Operator): an operator object.
op2 (Operator): an operator object.
ancilla_qubits (list[int]): a list of clean ancilla qubits.
ignore_phase (bool): ignore complex-phase difference between matrices.
rtol (float): relative tolerance value for comparison.
atol (float): absolute tolerance value for comparison.
Returns:
bool: True iff operators are equal up to clean ancilla qubits.
"""
if op1.dim != op2.dim:
return False

if atol is None:
atol = op1.atol
if rtol is None:
rtol = op1.rtol

num_qubits = op1._op_shape._num_qargs_l
num_non_ancillas = num_qubits - len(ancilla_qubits)

# Find a permutation that moves all ancilla qubits to the back
pattern = []
ancillas = []
for q in range(num_qubits):
if q not in ancilla_qubits:
pattern.append(q)
else:
ancillas.append(q)
pattern = pattern + ancillas

# Apply this permutation to both operators
permuted1 = op1.apply_permutation(pattern)
permuted2 = op2.apply_permutation(pattern)

# Restrict to the subspace where ancillas are 0
restricted1 = permuted1.data[: 2**num_non_ancillas, : 2**num_qubits]
restricted2 = permuted2.data[: 2**num_non_ancillas, : 2**num_qubits]

return matrix_equal(
restricted1, restricted2.data, ignore_phase=ignore_phase, rtol=rtol, atol=atol
)
10 changes: 9 additions & 1 deletion qiskit/synthesis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@
.. autofunction:: synth_mcx_n_dirty_i15
.. autofunction:: synth_mcx_n_clean_m15
.. autofunction:: synth_mcx_1_clean_b95
.. autofunction:: synth_mcx_noaux_v24
.. autofunction:: synth_mcx_gray_code
.. autofunction:: synth_c3x
.. autofunction:: synth_c4x
"""

Expand Down Expand Up @@ -180,8 +184,12 @@
two_qubit_cnot_decompose,
TwoQubitWeylDecomposition,
)
from .multi_controlled.mcx_with_ancillas_synth import (
from .multi_controlled import (
synth_mcx_n_dirty_i15,
synth_mcx_n_clean_m15,
synth_mcx_1_clean_b95,
synth_mcx_noaux_v24,
synth_mcx_gray_code,
synth_c3x,
synth_c4x,
)
6 changes: 5 additions & 1 deletion qiskit/synthesis/multi_controlled/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@

"""Module containing multi-controlled circuits synthesis"""

from .mcx_with_ancillas_synth import (
from .mcx_synthesis import (
synth_mcx_n_dirty_i15,
synth_mcx_n_clean_m15,
synth_mcx_1_clean_b95,
synth_mcx_gray_code,
synth_mcx_noaux_v24,
synth_c3x,
synth_c4x,
)
Loading

0 comments on commit 0d00c3d

Please sign in to comment.