diff --git a/Cargo.lock b/Cargo.lock index dac13c88aed3..7fbac930d755 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,9 +113,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" dependencies = [ "autocfg", "hashbrown", diff --git a/Cargo.toml b/Cargo.toml index 153450959ebc..4f9ff33a8d49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ numpy = "0.16.2" rand = "0.8" rand_pcg = "0.3" rand_distr = "0.4.3" -indexmap = "1.8.1" +indexmap = "1.8.2" ahash = "0.7.6" num-complex = "0.4" diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index fcc24beed0c7..5c6ff13e9a90 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -1736,7 +1736,7 @@ def draw( **text**: ASCII art TextDrawing that can be printed in the console. - **matplotlib**: images with color rendered purely in Python. + **mpl**: images with color rendered purely in Python using matplotlib. **latex**: high-quality images compiled via latex. diff --git a/qiskit/extensions/quantum_initializer/diagonal.py b/qiskit/extensions/quantum_initializer/diagonal.py index 137dce0741d5..b4c24fc41b61 100644 --- a/qiskit/extensions/quantum_initializer/diagonal.py +++ b/qiskit/extensions/quantum_initializer/diagonal.py @@ -55,7 +55,7 @@ def __init__(self, diag): raise QiskitError( "Not all of the diagonal entries can be converted to complex numbers." ) from ex - if not np.abs(z) - 1 < _EPS: + if not np.abs(np.abs(z) - 1) < _EPS: raise QiskitError("A diagonal entry has not absolute value one.") # Create new gate. super().__init__("diagonal", int(num_action_qubits), diag) diff --git a/qiskit/providers/backend.py b/qiskit/providers/backend.py index 028cf8895742..2690583bd575 100644 --- a/qiskit/providers/backend.py +++ b/qiskit/providers/backend.py @@ -211,10 +211,10 @@ def options(self): def run(self, run_input, **options): """Run on the backend. - This method that will return a :class:`~qiskit.providers.Job` object - that run circuits. Depending on the backend this may be either an async - or sync call. It is the discretion of the provider to decide whether - running should block until the execution is finished or not. The Job + This method returns a :class:`~qiskit.providers.Job` object + that runs circuits. Depending on the backend this may be either an async + or sync call. It is at the discretion of the provider to decide whether + running should block until the execution is finished or not: the Job class can handle either situation. Args: @@ -603,10 +603,10 @@ def provider(self): def run(self, run_input, **options): """Run on the backend. - This method that will return a :class:`~qiskit.providers.Job` object - that run circuits. Depending on the backend this may be either an async - or sync call. It is the discretion of the provider to decide whether - running should block until the execution is finished or not. The Job + This method returns a :class:`~qiskit.providers.Job` object + that runs circuits. Depending on the backend this may be either an async + or sync call. It is at the discretion of the provider to decide whether + running should block until the execution is finished or not: the Job class can handle either situation. Args: diff --git a/qiskit/quantum_info/operators/symplectic/base_pauli.py b/qiskit/quantum_info/operators/symplectic/base_pauli.py index ae0f3556e26c..8f97b72f6dfc 100644 --- a/qiskit/quantum_info/operators/symplectic/base_pauli.py +++ b/qiskit/quantum_info/operators/symplectic/base_pauli.py @@ -131,9 +131,9 @@ def compose(self, other, qargs=None, front=False, inplace=False): # Get phase shift phase = self._phase + other._phase if front: - phase += 2 * np.sum(np.logical_and(x1, z2), axis=1) + phase += 2 * _count_y(x1, z2) else: - phase += 2 * np.sum(np.logical_and(z1, x2), axis=1) + phase += 2 * _count_y(x2, z1) # Update Pauli x = np.logical_xor(x1, x2) @@ -219,8 +219,8 @@ def commutes(self, other, qargs=None): x1, z1 = self._x[:, inds], self._z[:, inds] else: x1, z1 = self._x, self._z - a_dot_b = np.mod(np.sum(np.logical_and(x1, other._z), axis=1), 2) - b_dot_a = np.mod(np.sum(np.logical_and(z1, other._x), axis=1), 2) + a_dot_b = np.mod(_count_y(x1, other._z), 2) + b_dot_a = np.mod(_count_y(other._x, z1), 2) return a_dot_b == b_dot_a def evolve(self, other, qargs=None, frame="h"): @@ -339,9 +339,9 @@ def __neg__(self): ret._phase = np.mod(self._phase + 2, 4) return ret - def _count_y(self): + def _count_y(self, dtype=None): """Count the number of I Pauli's""" - return np.sum(np.logical_and(self._x, self._z), axis=1) + return _count_y(self._x, self._z, dtype=dtype) @staticmethod def _stack(array, size, vertical=True): @@ -390,7 +390,7 @@ def _from_array(z, x, phase=0): raise QiskitError("z and x vectors are different size.") # Convert group phase convention to internal ZX-phase conversion. - base_phase = np.mod(np.sum(np.logical_and(base_x, base_z), axis=1, dtype=int) + phase, 4) + base_phase = np.mod(_count_y(base_x, base_z) + phase, 4) return base_z, base_x, base_phase @staticmethod @@ -474,6 +474,7 @@ def _to_label(z, x, phase, group_phase=False, full_group=True, return_phase=Fals for the label from the full Pauli group. """ num_qubits = z.size + phase = int(phase) coeff_labels = {0: "", 1: "-i", 2: "-", 3: "i"} label = "" for i in range(num_qubits): @@ -684,3 +685,11 @@ def _evolve_swap(base_pauli, q1, q2): base_pauli._x[:, q2] = x1 base_pauli._z[:, q2] = z1 return base_pauli + + +def _count_y(x, z, dtype=None): + """Count the number of I Pauli's""" + axis = 1 + if dtype is None: + dtype = np.min_scalar_type(x.shape[axis]) + return (x & z).sum(axis=axis, dtype=dtype) diff --git a/qiskit/quantum_info/operators/symplectic/clifford.py b/qiskit/quantum_info/operators/symplectic/clifford.py index 90b0ec2d3503..89abd9528970 100644 --- a/qiskit/quantum_info/operators/symplectic/clifford.py +++ b/qiskit/quantum_info/operators/symplectic/clifford.py @@ -23,6 +23,7 @@ from qiskit.quantum_info.operators.scalar_op import ScalarOp from qiskit.quantum_info.synthesis.clifford_decompose import decompose_clifford from qiskit.quantum_info.operators.mixins import generate_apidocs, AdjointMixin +from qiskit.quantum_info.operators.symplectic.base_pauli import _count_y from .stabilizer_table import StabilizerTable from .clifford_circuits import _append_circuit @@ -517,7 +518,7 @@ def _conjugate_transpose(clifford, method): ret.table.phase ^= clifford.dot(ret).table.phase if method in ["C", "T"]: # Apply conjugate - ret.table.phase ^= np.mod(np.sum(ret.table.X & ret.table.Z, axis=1), 2).astype(bool) + ret.table.phase ^= np.mod(_count_y(ret.table.X, ret.table.Z), 2).astype(bool) return ret def _pad_with_identity(self, clifford, qargs): diff --git a/qiskit/quantum_info/operators/symplectic/pauli.py b/qiskit/quantum_info/operators/symplectic/pauli.py index 6d4c85459309..5e0df3cd2df1 100644 --- a/qiskit/quantum_info/operators/symplectic/pauli.py +++ b/qiskit/quantum_info/operators/symplectic/pauli.py @@ -28,7 +28,7 @@ from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.mixins import generate_apidocs from qiskit.quantum_info.operators.scalar_op import ScalarOp -from qiskit.quantum_info.operators.symplectic.base_pauli import BasePauli +from qiskit.quantum_info.operators.symplectic.base_pauli import BasePauli, _count_y from qiskit.utils.deprecation import deprecate_function @@ -105,7 +105,7 @@ class initialization (``Pauli('-iXYZ')``). A ``Pauli`` object can be .. math:: - P &= (-i)^{q + z\cdot x} Z^z \cdot X^x. + P = (-i)^{q + z\cdot x} Z^z \cdot X^x. The :math:`k`th qubit corresponds to the :math:`k`th entry in the :math:`z` and :math:`x` arrays @@ -323,7 +323,7 @@ def __setitem__(self, qubits, value): self._z[0, qubits] = value.z self._x[0, qubits] = value.x # Add extra phase from new Pauli to current - self._phase += value._phase + self._phase = self._phase + value._phase def delete(self, qubits): """Return a Pauli with qubits deleted. @@ -629,9 +629,7 @@ def _from_scalar_op(cls, op): raise QiskitError(f"{op} is not an N-qubit identity") base_z = np.zeros((1, op.num_qubits), dtype=bool) base_x = np.zeros((1, op.num_qubits), dtype=bool) - base_phase = np.mod( - cls._phase_from_complex(op.coeff) + np.sum(np.logical_and(base_z, base_x), axis=1), 4 - ) + base_phase = np.mod(cls._phase_from_complex(op.coeff) + _count_y(base_x, base_z), 4) return base_z, base_x, base_phase @classmethod diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index b14a49bdb7fe..3545e63fddce 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -99,9 +99,10 @@ def __init__(self, data, coeffs=None, *, ignore_pauli_phase=False, copy=True): self._coeffs = coeffs else: # move the phase of `pauli_list` to `self._coeffs` - phase = pauli_list.phase - self._coeffs = np.asarray((-1j) ** phase * coeffs, dtype=complex) - pauli_list._phase = np.mod(pauli_list._phase - phase, 4) + phase = pauli_list._phase + count_y = pauli_list._count_y() + self._coeffs = np.asarray((-1j) ** (phase - count_y) * coeffs, dtype=complex) + pauli_list._phase = np.mod(count_y, 4) self._pauli_list = pauli_list if self._coeffs.shape != (self._pauli_list.size,): @@ -245,7 +246,7 @@ def transpose(self): # Hence we need to multiply coeffs by -1 for rows with an # odd number of Y terms. ret = self.copy() - minus = (-1) ** np.mod(np.sum(ret.paulis.x & ret.paulis.z, axis=1), 2) + minus = (-1) ** ret.paulis._count_y() ret._coeffs *= minus return ret @@ -287,7 +288,7 @@ def compose(self, other, qargs=None, front=False): else: q = np.logical_and(z1[:, np.newaxis], x2).reshape((-1, num_qubits)) # `np.mod` will be applied to `phase` in `SparsePauliOp.__init__` - phase = phase + 2 * np.sum(q, axis=1) + phase = phase + 2 * q.sum(axis=1, dtype=np.uint8) x3 = np.logical_xor(x1[:, np.newaxis], x2).reshape((-1, num_qubits)) z3 = np.logical_xor(z1[:, np.newaxis], z2).reshape((-1, num_qubits)) diff --git a/qiskit/quantum_info/states/densitymatrix.py b/qiskit/quantum_info/states/densitymatrix.py index 4e500e42a44e..bd91e4c9ca6f 100644 --- a/qiskit/quantum_info/states/densitymatrix.py +++ b/qiskit/quantum_info/states/densitymatrix.py @@ -381,7 +381,7 @@ def _expectation_value_pauli(self, pauli, qargs=None): return pauli_phase * density_expval_pauli_no_x(data, self.num_qubits, z_mask) x_max = qubits[pauli.x][-1] - y_phase = (-1j) ** np.sum(pauli.x & pauli.z) + y_phase = (-1j) ** pauli._count_y() return pauli_phase * density_expval_pauli_with_x( data, self.num_qubits, z_mask, x_mask, y_phase, x_max ) diff --git a/qiskit/quantum_info/states/statevector.py b/qiskit/quantum_info/states/statevector.py index 5e45cae9dc06..99ecbda86f58 100644 --- a/qiskit/quantum_info/states/statevector.py +++ b/qiskit/quantum_info/states/statevector.py @@ -475,7 +475,7 @@ def _expectation_value_pauli(self, pauli, qargs=None): return pauli_phase * expval_pauli_no_x(self.data, self.num_qubits, z_mask) x_max = qubits[pauli.x][-1] - y_phase = (-1j) ** np.sum(pauli.x & pauli.z) + y_phase = (-1j) ** pauli._count_y() return pauli_phase * expval_pauli_with_x( self.data, self.num_qubits, z_mask, x_mask, y_phase, x_max diff --git a/qiskit/transpiler/passes/layout/_csp_custom_solver.py b/qiskit/transpiler/passes/layout/_csp_custom_solver.py new file mode 100644 index 000000000000..d700a60e4c85 --- /dev/null +++ b/qiskit/transpiler/passes/layout/_csp_custom_solver.py @@ -0,0 +1,63 @@ +# 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. + +"""A custom python-constraint solver used by the :class:`~.CSPLayout` pass""" +from time import time + +from qiskit.utils import optionals as _optionals + +# This isn't ideal usage because we will import constraint at import time +# but to ensure the CustomSolver class is defined we need to do this. +# If constraint is not installed this will not raise a missing library +# exception until CSPLayout is initialized +if _optionals.HAS_CONSTRAINT: + from constraint import RecursiveBacktrackingSolver + + class CustomSolver(RecursiveBacktrackingSolver): + """A wrap to RecursiveBacktrackingSolver to support ``call_limit``""" + + def __init__(self, call_limit=None, time_limit=None): + self.call_limit = call_limit + self.time_limit = time_limit + self.call_current = None + self.time_start = None + self.time_current = None + super().__init__() + + def limit_reached(self): + """Checks if a limit is reached.""" + if self.call_current is not None: + self.call_current += 1 + if self.call_current > self.call_limit: + return True + if self.time_start is not None: + self.time_current = time() - self.time_start + if self.time_current > self.time_limit: + return True + return False + + def getSolution(self, domains, constraints, vconstraints): + """Wrap RecursiveBacktrackingSolver.getSolution to add the limits.""" + if self.call_limit is not None: + self.call_current = 0 + if self.time_limit is not None: + self.time_start = time() + return super().getSolution(domains, constraints, vconstraints) + + def recursiveBacktracking(self, solutions, domains, vconstraints, assignments, single): + """Like ``constraint.RecursiveBacktrackingSolver.recursiveBacktracking`` but + limited in the amount of calls by ``self.call_limit``""" + if self.limit_reached(): + return None + return super().recursiveBacktracking( + solutions, domains, vconstraints, assignments, single + ) diff --git a/qiskit/transpiler/passes/layout/csp_layout.py b/qiskit/transpiler/passes/layout/csp_layout.py index f91faa7013fb..704c91c4de46 100644 --- a/qiskit/transpiler/passes/layout/csp_layout.py +++ b/qiskit/transpiler/passes/layout/csp_layout.py @@ -16,52 +16,13 @@ found, no ``property_set['layout']`` is set. """ import random -from time import time -from constraint import Problem, RecursiveBacktrackingSolver, AllDifferentConstraint from qiskit.transpiler.layout import Layout from qiskit.transpiler.basepasses import AnalysisPass +from qiskit.utils import optionals as _optionals -class CustomSolver(RecursiveBacktrackingSolver): - """A wrap to RecursiveBacktrackingSolver to support ``call_limit``""" - - def __init__(self, call_limit=None, time_limit=None): - self.call_limit = call_limit - self.time_limit = time_limit - self.call_current = None - self.time_start = None - self.time_current = None - super().__init__() - - def limit_reached(self): - """Checks if a limit is reached.""" - if self.call_current is not None: - self.call_current += 1 - if self.call_current > self.call_limit: - return True - if self.time_start is not None: - self.time_current = time() - self.time_start - if self.time_current > self.time_limit: - return True - return False - - def getSolution(self, domains, constraints, vconstraints): - """Wrap RecursiveBacktrackingSolver.getSolution to add the limits.""" - if self.call_limit is not None: - self.call_current = 0 - if self.time_limit is not None: - self.time_start = time() - return super().getSolution(domains, constraints, vconstraints) - - def recursiveBacktracking(self, solutions, domains, vconstraints, assignments, single): - """Like ``constraint.RecursiveBacktrackingSolver.recursiveBacktracking`` but - limited in the amount of calls by ``self.call_limit``""" - if self.limit_reached(): - return None - return super().recursiveBacktracking(solutions, domains, vconstraints, assignments, single) - - +@_optionals.HAS_CONSTRAINT.require_in_instance class CSPLayout(AnalysisPass): """If possible, chooses a Layout as a CSP, using backtracking.""" @@ -102,6 +63,9 @@ def run(self, dag): qubits = dag.qubits cxs = set() + from constraint import Problem, AllDifferentConstraint, RecursiveBacktrackingSolver + from qiskit.transpiler.passes.layout._csp_custom_solver import CustomSolver + for gate in dag.two_qubit_ops(): cxs.add((qubits.index(gate.qargs[0]), qubits.index(gate.qargs[1]))) edges = set(self.coupling_map.get_edges()) diff --git a/qiskit/transpiler/passmanager_config.py b/qiskit/transpiler/passmanager_config.py index 80f01f0cd078..b472bc87d5d1 100644 --- a/qiskit/transpiler/passmanager_config.py +++ b/qiskit/transpiler/passmanager_config.py @@ -12,6 +12,8 @@ """Pass Manager Configuration class.""" +import pprint + from qiskit.transpiler.coupling import CouplingMap from qiskit.transpiler.instruction_durations import InstructionDurations @@ -124,3 +126,31 @@ def from_backend(cls, backend, **pass_manager_options): res.target = backend.target return res + + def __str__(self): + newline = "\n" + newline_tab = "\n\t" + if self.backend_properties is not None: + backend_props = pprint.pformat(self.backend_properties.to_dict()) + backend_props = backend_props.replace(newline, newline_tab) + else: + backend_props = str(None) + return ( + "Pass Manager Config:\n" + f"\tinitial_layout: {self.initial_layout}\n" + f"\tbasis_gates: {self.basis_gates}\n" + f"\tinst_map: {str(self.inst_map).replace(newline, newline_tab)}\n" + f"\tcoupling_map: {self.coupling_map}\n" + f"\tlayout_method: {self.layout_method}\n" + f"\trouting_method: {self.routing_method}\n" + f"\ttranslation_method: {self.translation_method}\n" + f"\tscheduling_method: {self.scheduling_method}\n" + f"\tinstruction_durations: {str(self.instruction_durations).replace(newline, newline_tab)}\n" + f"\tbackend_properties: {backend_props}\n" + f"\tapproximation_degree: {self.approximation_degree}\n" + f"\tseed_transpiler: {self.seed_transpiler}\n" + f"\ttiming_constraints: {self.timing_constraints}\n" + f"\tunitary_synthesis_method: {self.unitary_synthesis_method}\n" + f"\tunitary_synthesis_plugin_config: {self.unitary_synthesis_plugin_config}\n" + f"\ttarget: {str(self.target).replace(newline, newline_tab)}\n" + ) diff --git a/qiskit/utils/optionals.py b/qiskit/utils/optionals.py index 2789dd5ca370..5017a9599924 100644 --- a/qiskit/utils/optionals.py +++ b/qiskit/utils/optionals.py @@ -51,6 +51,10 @@ .. list-table:: :widths: 25 75 + * - .. py:data:: HAS_CONSTRAINT + - `python-constraint __ is a + constraint satisfaction problem solver, used in the :class:`~.CSPLayout` transpiler pass. + * - .. py:data:: HAS_CPLEX - The `IBM CPLEX Optimizer `__ is a high-performance mathematical programming solver for linear, mixed-integer and quadratic @@ -146,7 +150,6 @@ - `Z3 `__ is a theorem prover, used in the :class:`.CrosstalkAdaptiveSchedule` and :class:`.HoareOptimizer` transpiler passes. - External Command-Line Tools --------------------------- @@ -212,6 +215,12 @@ ) HAS_TOQM = _LazyImportTester("qiskit_toqm", name="Qiskit TOQM", install="pip install qiskit-toqm") +HAS_CONSTRAINT = _LazyImportTester( + "constraint", + name="python-constraint", + install="pip install python-constraint", +) + HAS_CPLEX = _LazyImportTester( "cplex", install="pip install 'qiskit-terra[bip-mapper]'", diff --git a/qiskit/version.py b/qiskit/version.py index c277c12a1098..384298dbb049 100644 --- a/qiskit/version.py +++ b/qiskit/version.py @@ -96,7 +96,6 @@ def __init__(self): "qiskit-aer": None, "qiskit-ignis": None, "qiskit-ibmq-provider": None, - "qiskit-aqua": None, "qiskit": None, } self._loaded = False @@ -122,14 +121,6 @@ def _load_versions(self): self._version_dict["qiskit-ibmq-provider"] = ibmq.__version__ except Exception: self._version_dict["qiskit-ibmq-provider"] = None - # TODO: Remove aqua after deprecation is complete and it is removed from - # the metapackage - try: - from qiskit import aqua - - self._version_dict["qiskit-aqua"] = aqua.__version__ - except Exception: - self._version_dict["qiskit-aqua"] = None try: import qiskit_nature diff --git a/releasenotes/notes/3842-14af3f8d922a7253.yaml b/releasenotes/notes/3842-14af3f8d922a7253.yaml new file mode 100644 index 000000000000..08c8c8555453 --- /dev/null +++ b/releasenotes/notes/3842-14af3f8d922a7253.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - | + Extra validation was added to :class:`.DiagonalGate` to check the argument has modulus one. diff --git a/releasenotes/notes/constraint-optional-b6a2b2ee21211ccd.yaml b/releasenotes/notes/constraint-optional-b6a2b2ee21211ccd.yaml new file mode 100644 index 000000000000..15e84b0cfa8b --- /dev/null +++ b/releasenotes/notes/constraint-optional-b6a2b2ee21211ccd.yaml @@ -0,0 +1,12 @@ +--- +upgrade: + - | + The ``python-constraint`` dependency, which is used solely by the + :class:`~.CSPLayout` transpiler pass, is no longer in the requirements list + for the Qiskit Terra package. This is because the :class:`~.CSPLayout` pass + is no longer used by default in any of the preset pass managers for + :func:`~.transpile`. While the pass is still available, if you're using it + you will need to manually install ``python-contraint`` or when you + install ``qiskit-terra`` you can use the ``csp-layout`` extra, for example:: + + pip install "qiskit-terra[csp-layout]" diff --git a/requirements-dev.txt b/requirements-dev.txt index 318396886095..460f476abf8e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,7 @@ setuptools-rust coverage>=4.4.0 hypothesis>=4.24.3 +python-constraint>=1.4 ipython<7.22.0 ipykernel<5.5.2 ipywidgets>=7.3.0 diff --git a/requirements.txt b/requirements.txt index b5e020bfb9b3..a7f1c84d2145 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,6 @@ psutil>=5 scipy>=1.5 sympy>=1.3 dill>=0.3 -python-constraint>=1.4 python-dateutil>=2.8.0 stevedore>=3.0.0 symengine>=0.9 ; platform_machine == 'x86_64' or platform_machine == 'aarch64' or platform_machine == 'ppc64le' or platform_machine == 'amd64' or platform_machine == 'arm64' diff --git a/setup.py b/setup.py index 3a475e0de88b..a9616d5301c2 100755 --- a/setup.py +++ b/setup.py @@ -33,9 +33,6 @@ ) -csplayout_requirements = [ - "python-constraint>=1.4", -] visualization_extras = [ "matplotlib>=3.3", "ipywidgets>=7.3.0", @@ -49,6 +46,7 @@ "z3-solver>=4.7", ] bip_requirements = ["cplex", "docplex"] +csp_requirements = ["python-constraint>=1.4"] toqm_requirements = ["qiskit-toqm>=0.0.4"] setup( @@ -85,11 +83,11 @@ "visualization": visualization_extras, "bip-mapper": bip_requirements, "crosstalk-pass": z3_requirements, - "csp-layout-pass": csplayout_requirements, + "csp-layout-pass": csp_requirements, "toqm": toqm_requirements, # Note: 'all' only includes extras that are stable and work on the majority of Python # versions and OSes supported by Terra. You have to ask for anything else explicitly. - "all": visualization_extras + z3_requirements + csplayout_requirements, + "all": visualization_extras + z3_requirements + csp_requirements, }, project_urls={ "Bug Tracker": "https://github.com/Qiskit/qiskit-terra/issues", diff --git a/test/python/circuit/test_diagonal_gate.py b/test/python/circuit/test_diagonal_gate.py index 4f6bc5eb1cce..f12d4ab92258 100644 --- a/test/python/circuit/test_diagonal_gate.py +++ b/test/python/circuit/test_diagonal_gate.py @@ -18,8 +18,10 @@ from qiskit import QuantumCircuit, QuantumRegister, BasicAer, execute +from qiskit import QiskitError from qiskit.test import QiskitTestCase from qiskit.compiler import transpile +from qiskit.extensions.quantum_initializer import DiagonalGate from qiskit.quantum_info.operators.predicates import matrix_equal @@ -54,6 +56,13 @@ def test_diag_gate(self): unitary_desired = _get_diag_gate_matrix(diag) self.assertTrue(matrix_equal(unitary, unitary_desired, ignore_phase=False)) + def test_mod1_entries(self): + """Test that diagonal raises if entries do not have modules of 1.""" + from qiskit.quantum_info.operators.predicates import ATOL_DEFAULT, RTOL_DEFAULT + + with self.assertRaises(QiskitError): + DiagonalGate([1, 1 - 2 * ATOL_DEFAULT - RTOL_DEFAULT]) + def _get_diag_gate_matrix(diag): return np.diagflat(diag) diff --git a/test/python/transpiler/test_passmanager_config.py b/test/python/transpiler/test_passmanager_config.py index 9873a2e805fd..a03f6f8bb876 100644 --- a/test/python/transpiler/test_passmanager_config.py +++ b/test/python/transpiler/test_passmanager_config.py @@ -17,6 +17,7 @@ from qiskit.test import QiskitTestCase from qiskit.test.mock import FakeMelbourne from qiskit.test.mock.backends.almaden.fake_almaden import FakeAlmaden +from qiskit.test.mock.backends import FakeArmonk from qiskit.transpiler.coupling import CouplingMap from qiskit.transpiler.passmanager_config import PassManagerConfig @@ -68,3 +69,114 @@ def test_invalid_user_option(self): """Test from_backend() with an invalid user option.""" with self.assertRaises(TypeError): PassManagerConfig.from_backend(FakeMelbourne(), invalid_option=None) + + def test_str(self): + """Test string output.""" + pm_config = PassManagerConfig.from_backend(FakeArmonk()) + # For testing remove instruction schedule map it's str output is non-deterministic + # based on hash seed + pm_config.inst_map = None + str_out = str(pm_config) + expected = """Pass Manager Config: + initial_layout: None + basis_gates: ['id', 'rz', 'sx', 'x'] + inst_map: None + coupling_map: + layout_method: None + routing_method: None + translation_method: None + scheduling_method: None + instruction_durations: id(0,): 7.111111111111111e-08 s + rz(0,): 0.0 s + sx(0,): 7.111111111111111e-08 s + x(0,): 7.111111111111111e-08 s + measure(0,): 4.977777777777777e-06 s + + backend_properties: {'backend_name': 'ibmq_armonk', + 'backend_version': '2.4.3', + 'gates': [{'gate': 'id', + 'name': 'id0', + 'parameters': [{'date': datetime.datetime(2021, 3, 15, 0, 38, 15, tzinfo=tzoffset(None, -14400)), + 'name': 'gate_error', + 'unit': '', + 'value': 0.00019769550670970334}, + {'date': datetime.datetime(2021, 3, 15, 0, 40, 24, tzinfo=tzoffset(None, -14400)), + 'name': 'gate_length', + 'unit': 'ns', + 'value': 71.11111111111111}], + 'qubits': [0]}, + {'gate': 'rz', + 'name': 'rz0', + 'parameters': [{'date': datetime.datetime(2021, 3, 15, 0, 40, 24, tzinfo=tzoffset(None, -14400)), + 'name': 'gate_error', + 'unit': '', + 'value': 0}, + {'date': datetime.datetime(2021, 3, 15, 0, 40, 24, tzinfo=tzoffset(None, -14400)), + 'name': 'gate_length', + 'unit': 'ns', + 'value': 0}], + 'qubits': [0]}, + {'gate': 'sx', + 'name': 'sx0', + 'parameters': [{'date': datetime.datetime(2021, 3, 15, 0, 38, 15, tzinfo=tzoffset(None, -14400)), + 'name': 'gate_error', + 'unit': '', + 'value': 0.00019769550670970334}, + {'date': datetime.datetime(2021, 3, 15, 0, 40, 24, tzinfo=tzoffset(None, -14400)), + 'name': 'gate_length', + 'unit': 'ns', + 'value': 71.11111111111111}], + 'qubits': [0]}, + {'gate': 'x', + 'name': 'x0', + 'parameters': [{'date': datetime.datetime(2021, 3, 15, 0, 38, 15, tzinfo=tzoffset(None, -14400)), + 'name': 'gate_error', + 'unit': '', + 'value': 0.00019769550670970334}, + {'date': datetime.datetime(2021, 3, 15, 0, 40, 24, tzinfo=tzoffset(None, -14400)), + 'name': 'gate_length', + 'unit': 'ns', + 'value': 71.11111111111111}], + 'qubits': [0]}], + 'general': [], + 'last_update_date': datetime.datetime(2021, 3, 15, 0, 40, 24, tzinfo=tzoffset(None, -14400)), + 'qubits': [[{'date': datetime.datetime(2021, 3, 15, 0, 36, 17, tzinfo=tzoffset(None, -14400)), + 'name': 'T1', + 'unit': 'us', + 'value': 182.6611165336624}, + {'date': datetime.datetime(2021, 3, 14, 0, 33, 45, tzinfo=tzoffset(None, -18000)), + 'name': 'T2', + 'unit': 'us', + 'value': 237.8589220110257}, + {'date': datetime.datetime(2021, 3, 15, 0, 40, 24, tzinfo=tzoffset(None, -14400)), + 'name': 'frequency', + 'unit': 'GHz', + 'value': 4.971852852405576}, + {'date': datetime.datetime(2021, 3, 15, 0, 40, 24, tzinfo=tzoffset(None, -14400)), + 'name': 'anharmonicity', + 'unit': 'GHz', + 'value': -0.34719293148282626}, + {'date': datetime.datetime(2021, 3, 15, 0, 35, 20, tzinfo=tzoffset(None, -14400)), + 'name': 'readout_error', + 'unit': '', + 'value': 0.02400000000000002}, + {'date': datetime.datetime(2021, 3, 15, 0, 35, 20, tzinfo=tzoffset(None, -14400)), + 'name': 'prob_meas0_prep1', + 'unit': '', + 'value': 0.0234}, + {'date': datetime.datetime(2021, 3, 15, 0, 35, 20, tzinfo=tzoffset(None, -14400)), + 'name': 'prob_meas1_prep0', + 'unit': '', + 'value': 0.024599999999999955}, + {'date': datetime.datetime(2021, 3, 15, 0, 35, 20, tzinfo=tzoffset(None, -14400)), + 'name': 'readout_length', + 'unit': 'ns', + 'value': 4977.777777777777}]]} + approximation_degree: None + seed_transpiler: None + timing_constraints: None + unitary_synthesis_method: default + unitary_synthesis_plugin_config: None + target: None +""" + self.assertEqual(str_out, expected)