Skip to content

Commit

Permalink
Merge branch 'main' of github.com:Qiskit/qiskit-terra into feature/to…
Browse files Browse the repository at this point in the history
…qm-optional
  • Loading branch information
kevinhartman committed May 31, 2022
2 parents b26c1fb + b8cb957 commit b2f6016
Show file tree
Hide file tree
Showing 23 changed files with 292 additions and 91 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion qiskit/extensions/quantum_initializer/diagonal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
16 changes: 8 additions & 8 deletions qiskit/providers/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
23 changes: 16 additions & 7 deletions qiskit/quantum_info/operators/symplectic/base_pauli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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"):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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)
3 changes: 2 additions & 1 deletion qiskit/quantum_info/operators/symplectic/clifford.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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):
Expand Down
10 changes: 4 additions & 6 deletions qiskit/quantum_info/operators/symplectic/pauli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
11 changes: 6 additions & 5 deletions qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,):
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion qiskit/quantum_info/states/densitymatrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/quantum_info/states/statevector.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
63 changes: 63 additions & 0 deletions qiskit/transpiler/passes/layout/_csp_custom_solver.py
Original file line number Diff line number Diff line change
@@ -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
)
46 changes: 5 additions & 41 deletions qiskit/transpiler/passes/layout/csp_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

Expand Down Expand Up @@ -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())
Expand Down
Loading

0 comments on commit b2f6016

Please sign in to comment.