Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a54c1ad
basicaer handling of global_phase.
ewinston Aug 11, 2020
fd52d5d
Merge branch 'master' into basicaer_phase
ewinston Aug 25, 2020
379cc2e
catch non-float global_phase
ewinston Aug 25, 2020
7380aea
update state vector simulator
ewinston Aug 25, 2020
b6e9664
linting
ewinston Aug 25, 2020
28ef827
blank line needed
ewinston Aug 25, 2020
db0afea
Merge branch 'master' into basicaer_phase
ewinston Aug 25, 2020
9e8fb4d
update Decompose pass for global phase
ewinston Aug 30, 2020
0355e32
Merge branch 'master' into basicaer_phase
ewinston Aug 31, 2020
81dbfc3
Merge branch 'master' into basicaer_phase
ewinston Aug 31, 2020
5b5b35e
update transpiler passes which create new dagcircuits.
ewinston Sep 3, 2020
c12ddba
Drop support for python 3.5 (#4926)
mtreinish Sep 1, 2020
ea618cb
Explicity set encoding of release notes via reno config (#5013)
mtreinish Sep 2, 2020
c8b5357
Fix parameterized Gate.definition to have valid ParameterTable. (#4945)
kdk Sep 2, 2020
5338dbf
Allow ParameterExpressions to be cast to ints (#5001)
menehune23 Sep 2, 2020
319f527
Add tutorials job to CI (#4907)
mtreinish Sep 2, 2020
503eece
Fix/5015 QuantumCircuit __eq__ should always return a Bool (#5016)
raulotaolea Sep 2, 2020
9977e7f
fixed issue 4936 by updating the docstring (#5014)
tgag17 Sep 3, 2020
5a2d78e
Merge branch 'master' into basicaer_phase
ewinston Sep 9, 2020
b38471f
Merge branch 'master' into basicaer_phase
ewinston Sep 15, 2020
c595cda
fix bux in optimize_1q_gates
ewinston Sep 22, 2020
3a4df9d
Merge branch 'master' into basicaer_phase
ewinston Sep 22, 2020
b0cdc4c
minor linting
ewinston Sep 22, 2020
6d3c050
fix test
ewinston Sep 23, 2020
5ee260b
Merge branch 'master' into basicaer_phase
kdk Sep 23, 2020
665ea91
Merge branch 'master' into basicaer_phase
mergify[bot] Sep 23, 2020
c96cb07
Merge branch 'master' into basicaer_phase
mergify[bot] Sep 24, 2020
7600c72
Merge branch 'master' into basicaer_phase
mergify[bot] Sep 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion qiskit/assembler/assemble_circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def _assemble_circuit(
memory_slots=memory_slots,
creg_sizes=creg_sizes,
name=circuit.name,
global_phase=circuit.global_phase)
global_phase=float(circuit.global_phase))

# TODO: why do we need n_qubits and memory_slots in both the header and the config
config = QasmQobjExperimentConfig(n_qubits=num_qubits, memory_slots=memory_slots)
Expand Down Expand Up @@ -113,6 +113,9 @@ def _assemble_circuit(
# measurement result may be needed for a conditional gate.
if instruction.name == "measure" and is_conditional_experiment:
instruction.register = clbit_indices
if op_context[0].definition is not None and op_context[0].definition.global_phase:
# pylint: disable=no-member
header.global_phase += float(op_context[0].definition.global_phase)

# To convert to a qobj-style conditional, insert a bfunc prior
# to the conditional instruction to map the creg ?= val condition
Expand Down
16 changes: 16 additions & 0 deletions qiskit/dagcircuit/dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,19 @@ def _add_op_node(self, op, qargs, cargs):
new_node._node_id = node_index
return node_index

def _copy_circuit_metadata(self):
"""Return a copy of source_dag with metadata but empty."""
target_dag = DAGCircuit()
target_dag.name = self.name
target_dag._global_phase = self._global_phase

for qreg in self.qregs.values():
target_dag.add_qreg(qreg)
for creg in self.cregs.values():
target_dag.add_creg(creg)

return target_dag

def apply_operation_back(self, op, qargs=None, cargs=None, condition=None):
"""Apply an operation to the output of the circuit.

Expand Down Expand Up @@ -883,6 +896,9 @@ def substitute_node_with_dag(self, node, input_dag, wires=None):
in_dag.apply_operation_back(replay_node.op, replay_node.qargs,
replay_node.cargs)

if in_dag.global_phase:
self.global_phase += in_dag.global_phase

if wires is None:
wires = in_dag.wires

Expand Down
3 changes: 3 additions & 0 deletions qiskit/providers/basicaer/qasm_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ def run_experiment(self, experiment):
self._classical_memory = 0
self._classical_register = 0
self._sample_measure = False
global_phase = experiment.header.global_phase
# Validate the dimension of initial statevector if set
self._validate_initial_statevector()
# Get the seed looking in circuit, qobj, and then random.
Expand Down Expand Up @@ -485,6 +486,8 @@ def run_experiment(self, experiment):
shots = self._shots
for _ in range(shots):
self._initialize_statevector()
# apply global_phase
self._statevector *= np.exp(1j * global_phase)
# Initialize classical memory to all 0
self._classical_memory = 0
self._classical_register = 0
Expand Down
4 changes: 4 additions & 0 deletions qiskit/providers/basicaer/unitary_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ def __init__(self, configuration=None, provider=None):
self._number_of_qubits = 0
self._initial_unitary = None
self._chop_threshold = 1e-15
self._global_phase = 0

def _add_unitary(self, gate, qubits):
"""Apply an N-qubit unitary matrix.
Expand Down Expand Up @@ -200,6 +201,8 @@ def _initialize_unitary(self):
def _get_unitary(self):
"""Return the current unitary"""
unitary = np.reshape(self._unitary, 2 * [2 ** self._number_of_qubits])
if self._global_phase:
unitary *= np.exp(1j * float(self._global_phase))
unitary[abs(unitary) < self._chop_threshold] = 0.0
return unitary

Expand Down Expand Up @@ -302,6 +305,7 @@ def run_experiment(self, experiment):
"""
start = time.time()
self._number_of_qubits = experiment.header.n_qubits
self._global_phase = experiment.header.global_phase

# Validate the dimension of initial unitary if set
self._validate_initial_unitary()
Expand Down
6 changes: 4 additions & 2 deletions qiskit/transpiler/passes/basis/basis_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,12 @@ def run(self, dag):
bound_target_dag = circuit_to_dag(target_circuit)
else:
bound_target_dag = target_dag

if bound_target_dag.global_phase:
dag.global_phase += bound_target_dag.global_phase
if (len(bound_target_dag.op_nodes()) == 1
and len(bound_target_dag.op_nodes()[0].qargs) == len(node.qargs)):
dag.substitute_node(node, bound_target_dag.op_nodes()[0].op, inplace=True)
dag_op = bound_target_dag.op_nodes()[0].op
dag.substitute_node(node, dag_op, inplace=True)
else:
dag.substitute_node_with_dag(node, bound_target_dag)
else:
Expand Down
2 changes: 2 additions & 0 deletions qiskit/transpiler/passes/basis/decompose.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
# opaque or built-in gates are not decomposable
if not node.op.definition:
continue
if node.op.definition.global_phase:
dag.global_phase += node.op.definition.global_phase
Comment thread
ewinston marked this conversation as resolved.
# TODO: allow choosing among multiple decomposition rules
rule = node.op.definition.data

Expand Down
1 change: 1 addition & 0 deletions qiskit/transpiler/passes/layout/apply_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,6 @@ def run(self, dag):
if node.type == 'op':
qargs = [q[layout[qarg]] for qarg in node.qargs]
new_dag.apply_operation_back(node.op, qargs, node.cargs)
new_dag._global_phase = dag._global_phase

return new_dag
7 changes: 1 addition & 6 deletions qiskit/transpiler/passes/optimization/consolidate_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@


from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.dagcircuit import DAGCircuit
from qiskit.quantum_info.operators import Operator
from qiskit.quantum_info.synthesis import TwoQubitBasisDecomposer
from qiskit.extensions import UnitaryGate
Expand Down Expand Up @@ -75,11 +74,7 @@ def run(self, dag):
if self.decomposer is None:
return dag

new_dag = DAGCircuit()
for qreg in dag.qregs.values():
new_dag.add_qreg(qreg)
for creg in dag.cregs.values():
new_dag.add_creg(creg)
new_dag = dag._copy_circuit_metadata()

# compute ordered indices for the global circuit wires
global_index_map = {wire: idx for idx, wire in enumerate(dag.qubits)}
Expand Down
7 changes: 6 additions & 1 deletion qiskit/transpiler/passes/optimization/optimize_1q_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def run(self, dag):
for run in runs:
right_name = "u1"
right_parameters = (0, 0, 0) # (theta, phi, lambda)

right_global_phase = 0
for current_node in run:
left_name = current_node.name
if (current_node.condition is not None
Expand All @@ -77,6 +77,9 @@ def run(self, dag):
else:
left_name = "u1" # replace id with u1
left_parameters = (0, 0, 0)
if (current_node.op.definition is not None and
current_node.op.definition.global_phase):
right_global_phase += current_node.op.definition.global_phase
# If there are any sympy objects coming from the gate convert
# to numpy.
left_parameters = tuple([float(x) for x in left_parameters])
Expand Down Expand Up @@ -206,6 +209,8 @@ def run(self, dag):
else:
raise TranspilerError('It was not possible to use the basis %s' % self.basis)

dag.global_phase += right_global_phase

if right_name != 'nop':
dag.substitute_node(run[0], new_op, inplace=True)

Expand Down
6 changes: 1 addition & 5 deletions qiskit/transpiler/passes/routing/basic_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ def run(self, dag):
TranspilerError: if the coupling map or the layout are not
compatible with the DAG.
"""
new_dag = DAGCircuit()
for qreg in dag.qregs.values():
new_dag.add_qreg(qreg)
for creg in dag.cregs.values():
new_dag.add_creg(creg)
new_dag = dag._copy_circuit_metadata()

if len(dag.qregs) != 1 or dag.qregs.get('q', None) is None:
raise TranspilerError('Basic swap runs on physical circuits only')
Expand Down
17 changes: 1 addition & 16 deletions qiskit/transpiler/passes/routing/sabre_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from itertools import cycle
import numpy as np

from qiskit.dagcircuit import DAGCircuit
from qiskit.circuit.library.standard_gates import SwapGate
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError
Expand Down Expand Up @@ -145,7 +144,7 @@ def run(self, dag):
rng = np.random.default_rng(self.seed)

# Preserve input DAG's name, regs, wire_map, etc. but replace the graph.
mapped_dag = _copy_circuit_metadata(dag)
mapped_dag = dag._copy_circuit_metadata()

# Assume bidirectional couplings, fixing gate direction is easy later.
self.coupling_map.make_symmetric()
Expand Down Expand Up @@ -335,20 +334,6 @@ def _score_heuristic(self, heuristic, front_layer, extended_set, layout, swap_qu
raise TranspilerError('Heuristic %s not recognized.' % heuristic)


def _copy_circuit_metadata(source_dag):
"""Return a copy of source_dag with metadata but empty.
"""
target_dag = DAGCircuit()
target_dag.name = source_dag.name

for qreg in source_dag.qregs.values():
target_dag.add_qreg(qreg)
for creg in source_dag.cregs.values():
target_dag.add_creg(creg)

return target_dag


def _transform_gate_for_layout(op_node, layout):
"""Return node implementing a virtual op on given layout."""
mapped_op_node = deepcopy(op_node)
Expand Down
7 changes: 1 addition & 6 deletions qiskit/transpiler/passes/routing/stochastic_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,12 +299,7 @@ def _mapper(self, circuit_graph, coupling_graph, trials=20):

# Construct an empty DAGCircuit with the same set of
# qregs and cregs as the input circuit
dagcircuit_output = DAGCircuit()
dagcircuit_output.name = circuit_graph.name
for qreg in circuit_graph.qregs.values():
dagcircuit_output.add_qreg(qreg)
for creg in circuit_graph.cregs.values():
dagcircuit_output.add_creg(creg)
dagcircuit_output = circuit_graph._copy_circuit_metadata()

logger.debug("trivial_layout = %s", layout)

Expand Down
8 changes: 1 addition & 7 deletions qiskit/transpiler/passes/utils/merge_adjacent_barriers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"""Return a circuit with any adjacent barriers merged together."""

from qiskit.transpiler.basepasses import TransformationPass
from qiskit.dagcircuit import DAGCircuit
from qiskit.circuit.barrier import Barrier


Expand Down Expand Up @@ -59,12 +58,7 @@ def run(self, dag):
return dag

# add the merged barriers to a new DAG
new_dag = DAGCircuit()

for qreg in dag.qregs.values():
new_dag.add_qreg(qreg)
for creg in dag.cregs.values():
new_dag.add_creg(creg)
new_dag = dag._copy_circuit_metadata()

# go over current nodes, and add them to the new dag
for node in dag.topological_op_nodes():
Expand Down
9 changes: 1 addition & 8 deletions qiskit/transpiler/passes/utils/remove_final_measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"""Remove final measurements and barriers at the end of a circuit."""

from qiskit.transpiler.basepasses import TransformationPass
from qiskit.dagcircuit import DAGCircuit


class RemoveFinalMeasurements(TransformationPass):
Expand Down Expand Up @@ -54,8 +53,6 @@ def run(self, dag):
if not final_ops:
return dag

new_dag = DAGCircuit()

for node in final_ops:
for carg in node.cargs:
# Add the clbit that was attached to the measure we are removing
Expand All @@ -75,11 +72,7 @@ def run(self, dag):
if val in cregs_to_remove and cregs_to_remove[val] == val.size:
del dag.cregs[key]

# Fill new DAGCircuit
for qreg in dag.qregs.values():
new_dag.add_qreg(qreg)
for creg in dag.cregs.values():
new_dag.add_creg(creg)
new_dag = dag._copy_circuit_metadata()

for node in dag.topological_op_nodes():
# copy the condition over too
Expand Down
31 changes: 31 additions & 0 deletions test/python/basicaer/test_statevector_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,37 @@ def test_unitary(self):
fidelity = state_fidelity(psi_target, psi_out)
self.assertGreater(fidelity, 0.999)

def test_global_phase(self):
"""Test global_phase"""
n_qubits = 4
qr = QuantumRegister(n_qubits)
circ = QuantumCircuit(qr)
circ.x(qr)
circ.global_phase = 0.5
self.circuit = circ
result = super().test_run_circuit()
actual = result.get_statevector(self.circuit)
expected = np.exp(1j * circ.global_phase) * np.repeat([[0], [1]], [n_qubits**2-1, 1])
self.assertTrue(np.allclose(actual, expected))

def test_global_phase_composite(self):
"""Test global_phase"""
n_qubits = 4
qr = QuantumRegister(n_qubits)
circ = QuantumCircuit(qr)
circ.x(qr)
circ.global_phase = 0.5
gate = circ.to_gate()

comp = QuantumCircuit(qr)
comp.append(gate, qr)
comp.global_phase = 0.1
self.circuit = comp
result = super().test_run_circuit()
actual = result.get_statevector(self.circuit)
expected = np.exp(1j * 0.6) * np.repeat([[0], [1]], [n_qubits**2-1, 1])
self.assertTrue(np.allclose(actual, expected))


if __name__ == '__main__':
unittest.main()
9 changes: 9 additions & 0 deletions test/python/circuit/test_controlled_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,15 @@ def test_open_controlled_unitary_z(self, num_ctrl_qubits, ctrl_state):
ref_mat = _compute_control_matrix(umat, num_ctrl_qubits, ctrl_state=ctrl_state)
self.assertEqual(Operator(cugate), Operator(ref_mat))

def test_controlled_controlled_rz(self):
"""Test that UnitaryGate with control returns params."""
qc = QuantumCircuit(1)
qc.rz(0.2, 0)
controlled = QuantumCircuit(2)
controlled.compose(qc.control(), inplace=True)
self.assertEqual(Operator(controlled), Operator(CRZGate(0.2)))
self.assertEqual(Operator(controlled), Operator(RZGate(0.2).control()))

def test_controlled_controlled_unitary(self):
"""Test that global phase in iso decomposition of unitary is handled."""
umat = np.array([[1, 0], [0, -1]])
Expand Down
2 changes: 1 addition & 1 deletion test/python/circuit/test_extensions_standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -1381,7 +1381,7 @@ def test_to_matrix_op(self):
from qiskit.quantum_info import Operator
from qiskit.circuit.library.standard_gates.ms import MSGate

params = [0.1 * i for i in range(10)]
params = [0.1 * i for i in range(1, 11)]
gate_class_list = Gate.__subclasses__() + ControlledGate.__subclasses__()
for gate_class in gate_class_list:
sig = signature(gate_class)
Expand Down
18 changes: 18 additions & 0 deletions test/python/transpiler/test_basis_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,3 +643,21 @@ def test_cx_bell_to_iswap(self):
expected_dag = circuit_to_dag(expected)

self.assertEqual(out_dag, expected_dag)

def test_global_phase(self):
"""Verify global phase preserved in basis translation"""
circ = QuantumCircuit(1)
gate_angle = pi / 5
circ_angle = pi / 3
circ.rz(gate_angle, 0)
circ.global_phase = circ_angle
in_dag = circuit_to_dag(circ)
out_dag = BasisTranslator(std_eqlib, ['u1']).run(in_dag)

qr = QuantumRegister(1, 'q')
expected = QuantumCircuit(qr)
expected.u1(gate_angle, qr)
expected.global_phase = circ_angle - gate_angle / 2
expected_dag = circuit_to_dag(expected)
self.assertEqual(out_dag, expected_dag)
self.assertEqual(float(out_dag.global_phase), float(expected_dag.global_phase))
Loading