Skip to content

Commit

Permalink
Merge branch 'main' of github.com:Qiskit/qiskit-terra into fix/missin…
Browse files Browse the repository at this point in the history
…g-instmap-v2-backend
  • Loading branch information
nkanazawa1989 committed Mar 13, 2023
2 parents d10fcf9 + c2affb1 commit 066d9d8
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 17 deletions.
9 changes: 4 additions & 5 deletions qiskit/circuit/parameterexpression.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,11 +535,10 @@ def is_real(self):
# expression's is_real attribute returns false that we have a
# non-zero imaginary
if _optionals.HAS_SYMENGINE:
if symbol_expr.imag != 0.0:
return False
else:
return False
return True
if symbol_expr.imag == 0.0:
return True
return False
return symbol_expr.is_real

def sympify(self):
"""Return symbolic expression as a raw Sympy or Symengine object.
Expand Down
3 changes: 0 additions & 3 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1623,7 +1623,6 @@ def qasm(
"sx",
"sxdg",
"cz",
"ccz",
"cy",
"swap",
"ch",
Expand All @@ -1636,8 +1635,6 @@ def qasm(
"cp",
"cu3",
"csx",
"cs",
"csdg",
"cu",
"rxx",
"rzz",
Expand Down
16 changes: 16 additions & 0 deletions qiskit/transpiler/coupling.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,22 @@ def __str__(self):
string += "]"
return string

def __eq__(self, other):
"""Check if the graph in ``other`` has the same node labels and edges as the graph in
``self``.
This function assumes that the graphs in :class:`.CouplingMap` instances are connected.
Args:
other (CouplingMap): The other coupling map.
Returns:
bool: Whether or not other is isomorphic to self.
"""
if not isinstance(other, CouplingMap):
return False
return set(self.graph.edge_list()) == set(other.graph.edge_list())

def draw(self):
"""Draws the coupling map.
Expand Down
10 changes: 8 additions & 2 deletions qiskit/transpiler/passes/synthesis/unitary_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,10 @@ def is_controlled(gate):
if props is None:
basis_2q_fidelity = 1.0
else:
basis_2q_fidelity = 1 - getattr(props, "error", 0.0)
error = getattr(props, "error", 0.0)
if error is None:
error = 0.0
basis_2q_fidelity = 1 - error
if approximation_degree is not None:
basis_2q_fidelity *= approximation_degree
decomposer = TwoQubitBasisDecomposer(
Expand All @@ -682,7 +685,10 @@ def is_controlled(gate):
if props is None:
basis_2q_fidelity[strength] = 1.0
else:
basis_2q_fidelity[strength] = 1 - getattr(props, "error", 0.0)
error = getattr(props, "error", 0.0)
if error is None:
error = 0.0
basis_2q_fidelity[strength] = 1 - error
# rewrite XX of the same strength in terms of it
embodiment = XXEmbodiments[type(v)]
if len(embodiment.parameters) == 1:
Expand Down
4 changes: 4 additions & 0 deletions qiskit/transpiler/passes/utils/gate_direction.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ def _run_coupling_map(self, dag, wire_map, edges=None):
continue
if len(node.qargs) != 2:
continue
if dag.has_calibration_for(node):
continue
qargs = (wire_map[node.qargs[0]], wire_map[node.qargs[1]])
if qargs not in edges and (qargs[1], qargs[0]) not in edges:
raise TranspilerError(
Expand Down Expand Up @@ -209,6 +211,8 @@ def _run_target(self, dag, wire_map):
continue
if len(node.qargs) != 2:
continue
if dag.has_calibration_for(node):
continue
qargs = (wire_map[node.qargs[0]], wire_map[node.qargs[1]])
swapped = (qargs[1], qargs[0])
if node.name in self._static_replacements:
Expand Down
8 changes: 8 additions & 0 deletions releasenotes/notes/coupling-map-eq-b0507b703d62a5f3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
upgrade:
- |
The :meth:`.CouplingMap.__eq__`` method has been updated to check that the edge lists of the
underlying graphs contain the same elements. Under the assumption that the underlying graphs are
connected, this check additionally ensures that the graphs have the same number of nodes with
the same labels. Any code using ``CouplingMap() == CouplingMap()`` to check object equality
should be updated to ``CouplingMap() is CouplingMap()``.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
fixes:
- |
The :class:`.GateDirection` transpiler pass will no longer reject gates that have been given
explicit calibrations, but do not exist in the generic coupling map or target.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
fixes:
- |
Fixed a bug where :meth:`.Parameter.is_real` did not return ``None`` when
the parameter is not bound.
Fixed `#8619 <https://github.com/Qiskit/qiskit-terra/issues/8619>`__.
8 changes: 8 additions & 0 deletions releasenotes/notes/fix_9559-ec05304e52ff841f.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
fixes:
- |
The Qiskit gates :class:`~.CCZGate`, :class:`~.CSGate`, :class:`~.CSdgGate` are not defined in
``qelib1.inc`` and, therefore, when dump as OpenQASM 2.0, their definition should be inserted in the file.
Fixes `#9559 <https://github.com/Qiskit/qiskit-terra/issues/9559>`__,
`#9721 <https://github.com/Qiskit/qiskit-terra/issues/9721>`__, and
`#9722 <https://github.com/Qiskit/qiskit-terra/issues/9722>`__.
62 changes: 58 additions & 4 deletions test/python/circuit/test_circuit_qasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Test Qiskit's QuantumCircuit class."""
"""Test Qiskit's gates in QASM2."""

import unittest
from math import pi
Expand All @@ -19,15 +19,15 @@
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.test import QiskitTestCase
from qiskit.circuit import Parameter, Qubit, Clbit, Gate
from qiskit.circuit.library import C3SXGate
from qiskit.circuit.library import C3SXGate, CCZGate, CSGate, CSdgGate, RZXGate
from qiskit.qasm.exceptions import QasmError

# Regex pattern to match valid OpenQASM identifiers
VALID_QASM2_IDENTIFIER = re.compile("[a-z][a-zA-Z_0-9]*")


class TestCircuitQasm(QiskitTestCase):
"""QuantumCircuit Qasm tests."""
"""QuantumCircuit QASM2 tests."""

def test_circuit_qasm(self):
"""Test circuit qasm() method."""
Expand Down Expand Up @@ -249,7 +249,9 @@ def test_circuit_qasm_with_composite_circuit_with_many_params_and_qubits(self):
self.assertEqual(original_str, qc.qasm())

def test_c3sxgate_roundtrips(self):
"""Test that C3SXGate correctly round trips. Qiskit gives this gate a different name
"""Test that C3SXGate correctly round trips.
Qiskit gives this gate a different name
('c3sx') to the name in Qiskit's version of qelib1.inc ('c3sqrtx') gate, which can lead to
resolution issues."""
qc = QuantumCircuit(4)
Expand All @@ -264,6 +266,58 @@ def test_c3sxgate_roundtrips(self):
parsed = QuantumCircuit.from_qasm_str(qasm)
self.assertIsInstance(parsed.data[0].operation, C3SXGate)

def test_cczgate_qasm(self):
"""Test that CCZ dumps definition as a non-qelib1 gate."""
qc = QuantumCircuit(3)
qc.append(CCZGate(), qc.qubits, [])
qasm = qc.qasm()
expected = """OPENQASM 2.0;
include "qelib1.inc";
gate ccz q0,q1,q2 { h q2; ccx q0,q1,q2; h q2; }
qreg q[3];
ccz q[0],q[1],q[2];
"""
self.assertEqual(qasm, expected)

def test_csgate_qasm(self):
"""Test that CS dumps definition as a non-qelib1 gate."""
qc = QuantumCircuit(2)
qc.append(CSGate(), qc.qubits, [])
qasm = qc.qasm()
expected = """OPENQASM 2.0;
include "qelib1.inc";
gate cs q0,q1 { p(pi/4) q0; cx q0,q1; p(-pi/4) q1; cx q0,q1; p(pi/4) q1; }
qreg q[2];
cs q[0],q[1];
"""
self.assertEqual(qasm, expected)

def test_csdggate_qasm(self):
"""Test that CSdg dumps definition as a non-qelib1 gate."""
qc = QuantumCircuit(2)
qc.append(CSdgGate(), qc.qubits, [])
qasm = qc.qasm()
expected = """OPENQASM 2.0;
include "qelib1.inc";
gate csdg q0,q1 { p(-pi/4) q0; cx q0,q1; p(pi/4) q1; cx q0,q1; p(-pi/4) q1; }
qreg q[2];
csdg q[0],q[1];
"""
self.assertEqual(qasm, expected)

def test_rzxgate_qasm(self):
"""Test that RZX dumps definition as a non-qelib1 gate."""
qc = QuantumCircuit(2)
qc.append(RZXGate(0), qc.qubits, [])
qasm = qc.qasm()
expected = """OPENQASM 2.0;
include "qelib1.inc";
gate rzx(param0) q0,q1 { h q1; cx q0,q1; rz(0) q1; cx q0,q1; h q1; }
qreg q[2];
rzx(0) q[0],q[1];
"""
self.assertEqual(qasm, expected)

def test_unbound_circuit_raises(self):
"""Test circuits with unbound parameters raises."""
qc = QuantumCircuit(1)
Expand Down
16 changes: 15 additions & 1 deletion test/python/circuit/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -1759,10 +1759,24 @@ def test_parameter_expression_grad(self):
def test_bound_expression_is_real(self):
"""Test is_real on bound parameters."""
x = Parameter("x")
self.assertEqual(x.is_real(), None)
self.assertEqual((1j * x).is_real(), None)

expr = 1j * x
bound = expr.bind({x: 2})
self.assertEqual(bound.is_real(), False)

bound = x.bind({x: 0 + 0j})
self.assertEqual(bound.is_real(), True)

bound = x.bind({x: 0 + 1j})
self.assertEqual(bound.is_real(), False)

bound = x.bind({x: 1 + 0j})
self.assertEqual(bound.is_real(), True)

self.assertFalse(bound.is_real())
bound = x.bind({x: 1 + 1j})
self.assertEqual(bound.is_real(), False)


class TestParameterEquality(QiskitTestCase):
Expand Down
19 changes: 19 additions & 0 deletions test/python/transpiler/test_coupling.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,25 @@ def test_implements_iter(self):
expected = [(0, 1), (1, 0), (1, 2), (2, 1)]
self.assertEqual(sorted(coupling), expected)

def test_equality(self):
"""Test that equality checks that the graphs have the same nodes, node labels, and edges."""

# two coupling maps with 4 nodes and the same edges
coupling0 = CouplingMap([(0, 1), (0, 2), (2, 3)])
coupling1 = CouplingMap([(0, 1), (0, 2), (2, 3)])
self.assertEqual(coupling0, coupling1)

# coupling map with 5 nodes not equal to the previous 2
coupling2 = CouplingMap([(0, 1), (0, 2), (2, 4)])
self.assertNotEqual(coupling0, coupling2)

# coupling map isomorphic to coupling0, but with cyclically shifted labels
coupling3 = CouplingMap([(1, 2), (1, 3), (3, 0)])
self.assertNotEqual(coupling0, coupling3)

# additional test for comparison to a non-CouplingMap object
self.assertNotEqual(coupling0, 1)


class CouplingVisualizationTest(QiskitVisualizationTestCase):
@unittest.skipUnless(optionals.HAS_GRAPHVIZ, "Graphviz not installed")
Expand Down
31 changes: 29 additions & 2 deletions test/python/transpiler/test_gate_direction.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

import ddt

from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
from qiskit.circuit import Parameter
from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit, pulse
from qiskit.circuit import Parameter, Gate
from qiskit.circuit.library import (
CXGate,
CZGate,
Expand Down Expand Up @@ -439,6 +439,33 @@ def test_target_control_flow(self):
pass_ = GateDirection(None, target)
self.assertEqual(pass_(circuit), expected)

def test_allows_calibrated_gates_coupling_map(self):
"""Test that the gate direction pass allows a gate that's got a calibration to pass through
without error."""
cm = CouplingMap([(1, 0)])

gate = Gate("my_2q_gate", 2, [])
circuit = QuantumCircuit(2)
circuit.append(gate, (0, 1))
circuit.add_calibration(gate, (0, 1), pulse.ScheduleBlock())

pass_ = GateDirection(cm)
self.assertEqual(pass_(circuit), circuit)

def test_allows_calibrated_gates_target(self):
"""Test that the gate direction pass allows a gate that's got a calibration to pass through
without error."""
target = Target(num_qubits=2)
target.add_instruction(CXGate(), properties={(0, 1): None})

gate = Gate("my_2q_gate", 2, [])
circuit = QuantumCircuit(2)
circuit.append(gate, (0, 1))
circuit.add_calibration(gate, (0, 1), pulse.ScheduleBlock())

pass_ = GateDirection(None, target)
self.assertEqual(pass_(circuit), circuit)


if __name__ == "__main__":
unittest.main()

0 comments on commit 066d9d8

Please sign in to comment.