def test_opaque_gate_with_label(self): """Test that custom opaque gate is correctly serialized with a label""" custom_gate = Gate("black_box", 1, []) custom_gate.label = "My Special Black Box" qc = QuantumCircuit(1) qc.append(custom_gate, [0]) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) new_circ = load(qpy_file)[0] self.assertEqual(qc, new_circ) self.assertEqual([x[0].label for x in qc.data], [x[0].label for x in new_circ.data])
def test_compose_calibrations(self): """Test that compose carries over the calibrations.""" dag_cal = QuantumCircuit(1) dag_cal.append(Gate('', 1, []), qargs=[0]) dag_cal.add_calibration(Gate('', 1, []), [0], Schedule()) empty_dag = circuit_to_dag(QuantumCircuit(1)) calibrated_dag = circuit_to_dag(dag_cal) composed_dag = empty_dag.compose(calibrated_dag, inplace=False) cal = {'': {((0, ), ()): Schedule(name="sched0")}} self.assertEqual(composed_dag.calibrations, cal) self.assertEqual(calibrated_dag.calibrations, cal)
def inverse(self): """Return the inverse. Note that the resulting gate has an empty ``params`` property. """ inverse_gate = Gate( name=self.name + "_dg", num_qubits=self.num_qubits, params=[]) # removing the params because arrays are deprecated definition = QuantumCircuit(*self.definition.qregs) for inst in reversed(self._definition): definition._append( inst.replace(operation=inst.operation.inverse())) inverse_gate.definition = definition return inverse_gate
def inverse(self): """Return the inverse. This does not re-compute the decomposition for the multiplexer with the inverse of the gates but simply inverts the existing decomposition. """ inverse_gate = Gate(name=self.name + '_dg', num_qubits=self.num_qubits, params=[]) # remove parameters since array is deprecated as parameter inverse_gate.definition = QuantumCircuit(*self.definition.qregs) inverse_gate.definition._data = [(inst.inverse(), qargs, []) for inst, qargs, _ in reversed(self._definition)] return inverse_gate
def inverse(self): """Return the inverse. Note that the resulting gate has an empty ``params`` property. """ inverse_gate = Gate( name=self.name + "_dg", num_qubits=self.num_qubits, params=[] ) # removing the params because arrays are deprecated inverse_gate.definition = QuantumCircuit(*self.definition.qregs) inverse_gate.definition._data = [ (inst.inverse(), qargs, []) for inst, qargs, _ in reversed(self._definition) ] return inverse_gate
def test_custom_gate(self): """Test that custom gate is correctly serialized""" custom_gate = Gate("black_box", 1, []) custom_definition = QuantumCircuit(1) custom_definition.h(0) custom_definition.rz(1.5, 0) custom_definition.sdg(0) custom_gate.definition = custom_definition qc = QuantumCircuit(1) qc.append(custom_gate, [0]) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) new_circ = load(qpy_file)[0] self.assertEqual(qc, new_circ) self.assertEqual(qc.decompose(), new_circ.decompose())
def test_nested_controlled_gate(self): """Test a custom nested controlled gate.""" custom_gate = Gate("black_box", 1, []) custom_definition = QuantumCircuit(1) custom_definition.h(0) custom_definition.rz(1.5, 0) custom_definition.sdg(0) custom_gate.definition = custom_definition qc = QuantumCircuit(3) qc.append(custom_gate, [0]) controlled_gate = custom_gate.control(2) qc.append(controlled_gate, [0, 1, 2]) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) new_circ = load(qpy_file)[0] self.assertEqual(qc, new_circ) self.assertEqual(qc.decompose(), new_circ.decompose())
def inverse(self): """Return the inverse. This does not re-compute the decomposition for the multiplexer with the inverse of the gates but simply inverts the existing decomposition. """ inverse_gate = Gate( name=self.name + "_dg", num_qubits=self.num_qubits, params=[]) # removing the params because arrays are deprecated definition = QuantumCircuit(*self.definition.qregs) for inst in reversed(self._definition): definition._append( inst.replace(operation=inst.operation.inverse())) definition.global_phase = -self.definition.global_phase inverse_gate.definition = definition return inverse_gate
def test_opaque_gate(self): """Test that custom opaque gate is correctly serialized""" custom_gate = Gate("black_box", 1, []) qc = QuantumCircuit(1) qc.append(custom_gate, [0]) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) new_circ = load(qpy_file)[0] self.assertEqual(qc, new_circ)
def test_custom_gate_with_label(self): """Test that custom gate is correctly serialized with a label""" custom_gate = Gate("black_box", 1, []) custom_definition = QuantumCircuit(1) custom_definition.h(0) custom_definition.rz(1.5, 0) custom_definition.sdg(0) custom_gate.definition = custom_definition custom_gate.label = "My special black box with a definition" qc = QuantumCircuit(1) qc.append(custom_gate, [0]) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) new_circ = load(qpy_file)[0] self.assertEqual(qc, new_circ) self.assertEqual(qc.decompose(), new_circ.decompose()) self.assertEqual([x[0].label for x in qc.data], [x[0].label for x in new_circ.data])
def _parse_custom_instruction(custom_instructions, gate_name, params): (type_str, num_qubits, num_clbits, definition) = custom_instructions[gate_name] if type_str == "i": inst_obj = Instruction(gate_name, num_qubits, num_clbits, params) if definition: inst_obj.definition = definition elif type_str == "g": inst_obj = Gate(gate_name, num_qubits, params) inst_obj.definition = definition else: raise ValueError("Invalid custom instruction type '%s'" % type_str) return inst_obj
def generate_open_controlled_gates(): """Test QPY serialization with custom ControlledGates with open controls.""" circuits = [] qc = QuantumCircuit(3) controlled_gate = DCXGate().control(1, ctrl_state=0) qc.append(controlled_gate, [0, 1, 2]) circuits.append(qc) custom_gate = Gate("black_box", 1, []) custom_definition = QuantumCircuit(1) custom_definition.h(0) custom_definition.rz(1.5, 0) custom_definition.sdg(0) custom_gate.definition = custom_definition nested_qc = QuantumCircuit(3) nested_qc.append(custom_gate, [0]) controlled_gate = custom_gate.control(2, ctrl_state=1) nested_qc.append(controlled_gate, [0, 1, 2]) nested_qc.measure_all() circuits.append(nested_qc) return circuits
def _parse_custom_operation(custom_operations, gate_name, params, version, vectors, registers): if version >= 5: ( type_str, num_qubits, num_clbits, definition, num_ctrl_qubits, ctrl_state, base_gate_raw, ) = custom_operations[gate_name] else: type_str, num_qubits, num_clbits, definition = custom_operations[ gate_name] type_key = type_keys.CircuitInstruction(type_str) if type_key == type_keys.CircuitInstruction.INSTRUCTION: inst_obj = Instruction(gate_name, num_qubits, num_clbits, params) if definition is not None: inst_obj.definition = definition return inst_obj if type_key == type_keys.CircuitInstruction.GATE: inst_obj = Gate(gate_name, num_qubits, params) inst_obj.definition = definition return inst_obj if version >= 5 and type_key == type_keys.CircuitInstruction.CONTROLLED_GATE: with io.BytesIO(base_gate_raw) as base_gate_obj: base_gate = _read_instruction(base_gate_obj, None, registers, custom_operations, version, vectors) if ctrl_state < 2**num_ctrl_qubits - 1: # If open controls, we need to discard the control suffix when setting the name. gate_name = gate_name.rsplit("_", 1)[0] inst_obj = ControlledGate( gate_name, num_qubits, params, num_ctrl_qubits=num_ctrl_qubits, ctrl_state=ctrl_state, base_gate=base_gate, ) inst_obj.definition = definition return inst_obj if type_key == type_keys.CircuitInstruction.PAULI_EVOL_GATE: return definition raise ValueError("Invalid custom instruction type '%s'" % type_str)
def _parse_custom_instruction(custom_instructions, gate_name, params): type_str, num_qubits, num_clbits, definition = custom_instructions[gate_name] type_key = common.CircuitInstructionTypeKey(type_str) if type_key == common.CircuitInstructionTypeKey.INSTRUCTION: inst_obj = Instruction(gate_name, num_qubits, num_clbits, params) if definition is not None: inst_obj.definition = definition return inst_obj if type_key == common.CircuitInstructionTypeKey.GATE: inst_obj = Gate(gate_name, num_qubits, params) inst_obj.definition = definition return inst_obj if type_key == common.CircuitInstructionTypeKey.PAULI_EVOL_GATE: return definition raise ValueError("Invalid custom instruction type '%s'" % type_str)
def test_custom_gate_with_noop_definition(self): """Test that a custom gate whose definition contains no elements is serialized with a proper definition. Regression test of gh-7429.""" empty = QuantumCircuit(1, name="empty").to_gate() opaque = Gate("opaque", 1, []) qc = QuantumCircuit(2) qc.append(empty, [0], []) qc.append(opaque, [1], []) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) new_circ = load(qpy_file)[0] self.assertEqual(qc, new_circ) self.assertEqual(qc.decompose(), new_circ.decompose()) self.assertEqual(len(new_circ), 2) self.assertIsInstance(new_circ.data[0][0].definition, QuantumCircuit) self.assertIs(new_circ.data[1][0].definition, None)
def generate_calibrated_circuits(): """Test for QPY serialization with calibrations.""" from qiskit.pulse import builder, Constant, DriveChannel circuits = [] # custom gate mygate = Gate("mygate", 1, []) qc = QuantumCircuit(1) qc.append(mygate, [0]) with builder.build() as caldef: builder.play(Constant(100, 0.1), DriveChannel(0)) qc.add_calibration(mygate, (0, ), caldef) circuits.append(qc) # override instruction qc = QuantumCircuit(1) qc.x(0) with builder.build() as caldef: builder.play(Constant(100, 0.1), DriveChannel(0)) qc.add_calibration("x", (0, ), caldef) circuits.append(qc) return circuits
def run(self, dag): """Return a new circuit that has been optimized.""" runs = dag.collect_runs(["u1", "u2", "u3", "id"]) for run in runs: right_name = "u1" right_parameters = (0, 0, 0) # (theta, phi, lambda) for current_node in run: left_name = current_node.name if (current_node.condition is not None or len(current_node.qargs) != 1 or left_name not in ["u1", "u2", "u3", "id"]): raise MapperError("internal error") if left_name == "u1": left_parameters = (0, 0, current_node.op.params[0]) elif left_name == "u2": left_parameters = (np.pi / 2, current_node.op.params[0], current_node.op.params[1]) elif left_name == "u3": left_parameters = tuple(current_node.op.params) else: left_name = "u1" # replace id with u1 left_parameters = (0, 0, 0) # If there are any sympy objects coming from the gate convert # to numpy. left_parameters = tuple([float(x) for x in left_parameters]) # Compose gates name_tuple = (left_name, right_name) if name_tuple == ("u1", "u1"): # u1(lambda1) * u1(lambda2) = u1(lambda1 + lambda2) right_parameters = (0, 0, right_parameters[2] + left_parameters[2]) elif name_tuple == ("u1", "u2"): # u1(lambda1) * u2(phi2, lambda2) = u2(phi2 + lambda1, lambda2) right_parameters = (np.pi / 2, right_parameters[1] + left_parameters[2], right_parameters[2]) elif name_tuple == ("u2", "u1"): # u2(phi1, lambda1) * u1(lambda2) = u2(phi1, lambda1 + lambda2) right_name = "u2" right_parameters = (np.pi / 2, left_parameters[1], right_parameters[2] + left_parameters[2]) elif name_tuple == ("u1", "u3"): # u1(lambda1) * u3(theta2, phi2, lambda2) = # u3(theta2, phi2 + lambda1, lambda2) right_parameters = (right_parameters[0], right_parameters[1] + left_parameters[2], right_parameters[2]) elif name_tuple == ("u3", "u1"): # u3(theta1, phi1, lambda1) * u1(lambda2) = # u3(theta1, phi1, lambda1 + lambda2) right_name = "u3" right_parameters = (left_parameters[0], left_parameters[1], right_parameters[2] + left_parameters[2]) elif name_tuple == ("u2", "u2"): # Using Ry(pi/2).Rz(2*lambda).Ry(pi/2) = # Rz(pi/2).Ry(pi-2*lambda).Rz(pi/2), # u2(phi1, lambda1) * u2(phi2, lambda2) = # u3(pi - lambda1 - phi2, phi1 + pi/2, lambda2 + pi/2) right_name = "u3" right_parameters = (np.pi - left_parameters[2] - right_parameters[1], left_parameters[1] + np.pi / 2, right_parameters[2] + np.pi / 2) elif name_tuple[1] == "nop": right_name = left_name right_parameters = left_parameters else: # For composing u3's or u2's with u3's, use # u2(phi, lambda) = u3(pi/2, phi, lambda) # together with the qiskit.mapper.compose_u3 method. right_name = "u3" # Evaluate the symbolic expressions for efficiency right_parameters = Optimize1qGates.compose_u3( left_parameters[0], left_parameters[1], left_parameters[2], right_parameters[0], right_parameters[1], right_parameters[2]) # Why evalf()? This program: # OPENQASM 2.0; # include "qelib1.inc"; # qreg q[2]; # creg c[2]; # u3(0.518016983430947*pi,1.37051598592907*pi,1.36816383603222*pi) q[0]; # u3(1.69867232277986*pi,0.371448347747471*pi,0.461117217930936*pi) q[0]; # u3(0.294319836336836*pi,0.450325871124225*pi,1.46804720442555*pi) q[0]; # measure q -> c; # took >630 seconds (did not complete) to optimize without # calling evalf() at all, 19 seconds to optimize calling # evalf() AFTER compose_u3, and 1 second to optimize # calling evalf() BEFORE compose_u3. # 1. Here down, when we simplify, we add f(theta) to lambda to # correct the global phase when f(theta) is 2*pi. This isn't # necessary but the other steps preserve the global phase, so # we continue in that manner. # 2. The final step will remove Z rotations by 2*pi. # 3. Note that is_zero is true only if the expression is exactly # zero. If the input expressions have already been evaluated # then these final simplifications will not occur. # TODO After we refactor, we should have separate passes for # exact and approximate rewriting. # Y rotation is 0 mod 2*pi, so the gate is a u1 if np.mod(right_parameters[0], (2 * np.pi)) == 0 \ and right_name != "u1": right_name = "u1" right_parameters = (0, 0, right_parameters[1] + right_parameters[2] + right_parameters[0]) # Y rotation is pi/2 or -pi/2 mod 2*pi, so the gate is a u2 if right_name == "u3": # theta = pi/2 + 2*k*pi if np.mod((right_parameters[0] - np.pi / 2), (2 * np.pi)) == 0: right_name = "u2" right_parameters = (np.pi / 2, right_parameters[1], right_parameters[2] + (right_parameters[0] - np.pi / 2)) # theta = -pi/2 + 2*k*pi if np.mod((right_parameters[0] + np.pi / 2), (2 * np.pi)) == 0: right_name = "u2" right_parameters = (np.pi / 2, right_parameters[1] + np.pi, right_parameters[2] - np.pi + (right_parameters[0] + np.pi / 2)) # u1 and lambda is 0 mod 2*pi so gate is nop (up to a global phase) if right_name == "u1" and np.mod(right_parameters[2], (2 * np.pi)) == 0: right_name = "nop" # Replace the the first node in the run with a dummy DAG which contains a dummy # qubit. The name is irrelevant, because substitute_node_with_dag will take care of # putting it in the right place. run_qarg = (QuantumRegister(1, 'q'), 0) new_op = Gate(name="", num_qubits=1, params=[]) if right_name == "u1": new_op = U1Gate(right_parameters[2]) if right_name == "u2": new_op = U2Gate(right_parameters[1], right_parameters[2]) if right_name == "u3": new_op = U3Gate(*right_parameters) if right_name != 'nop': new_dag = DAGCircuit() new_dag.add_qreg(run_qarg[0]) new_dag.apply_operation_back(new_op, [run_qarg], []) dag.substitute_node_with_dag(run[0], new_dag) # Delete the other nodes in the run for current_node in run[1:]: dag.remove_op_node(current_node) if right_name == "nop": dag.remove_op_node(run[0]) return dag
def circuit_to_gate(circuit, parameter_map=None): """Build a ``Gate`` object from a ``QuantumCircuit``. The gate is anonymous (not tied to a named quantum register), and so can be inserted into another circuit. The gate will have the same string name as the circuit. Args: circuit (QuantumCircuit): the input circuit. parameter_map (dict): For parameterized circuits, a mapping from parameters in the circuit to parameters to be used in the gate. If None, existing circuit parameters will also parameterize the Gate. Raises: QiskitError: if circuit is non-unitary or if parameter_map is not compatible with circuit Return: Gate: a Gate equivalent to the action of the input circuit. Upon decomposition, this gate will yield the components comprising the original circuit. """ for inst, _, _ in circuit.data: if not isinstance(inst, Gate): raise QiskitError('One or more instructions in this instruction ' 'cannot be converted to a gate') if parameter_map is None: parameter_dict = {p: p for p in circuit.parameters} else: parameter_dict = circuit._unroll_param_dict(parameter_map) if parameter_dict.keys() != circuit.parameters: raise QiskitError(('parameter_map should map all circuit parameters. ' 'Circuit parameters: {}, parameter_map: {}').format( circuit.parameters, parameter_dict)) gate = Gate(name=circuit.name, num_qubits=sum([qreg.size for qreg in circuit.qregs]), params=sorted(parameter_dict.values(), key=lambda p: p.name)) gate.condition = None def find_bit_position(bit): """find the index of a given bit (Register, int) within a flat ordered list of bits of the circuit """ if isinstance(bit, Qubit): ordered_regs = circuit.qregs else: ordered_regs = circuit.cregs reg_index = ordered_regs.index(bit.register) return sum([reg.size for reg in ordered_regs[:reg_index]]) + bit.index target = circuit.copy() target._substitute_parameters(parameter_dict) definition = target.data if gate.num_qubits > 0: q = QuantumRegister(gate.num_qubits, 'q') definition = list( map( lambda x: (x[0], list(map(lambda y: q[find_bit_position(y)], x[1]))), definition)) gate.definition = definition return gate
def run(self, dag): """Run the Optimize1qGates pass on `dag`. Args: dag (DAGCircuit): the DAG to be optimized. Returns: DAGCircuit: the optimized DAG. Raises: TranspilerError: if YZY and ZYZ angles do not give same rotation matrix. """ use_u = 'u' in self.basis use_p = 'p' in self.basis runs = dag.collect_runs(["u1", "u2", "u3", "u", 'p']) runs = _split_runs_on_parameters(runs) for run in runs: if use_p: right_name = "p" else: 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 or len(current_node.qargs) != 1 or left_name not in ["p", "u1", "u2", "u3", 'u', "id"]): raise TranspilerError("internal error") if left_name in ("u1", "p"): left_parameters = (0, 0, current_node.op.params[0]) elif left_name == "u2": left_parameters = (np.pi / 2, current_node.op.params[0], current_node.op.params[1]) elif left_name in ("u3", 'u'): left_parameters = tuple(current_node.op.params) else: if use_p: left_name = "p" 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]) # Compose gates name_tuple = (left_name, right_name) if name_tuple in (("u1", "u1"), ("p", "p")): # u1(lambda1) * u1(lambda2) = u1(lambda1 + lambda2) right_parameters = (0, 0, right_parameters[2] + left_parameters[2]) elif name_tuple in (("u1", "u2"), ("p", "u2")): # u1(lambda1) * u2(phi2, lambda2) = u2(phi2 + lambda1, lambda2) right_parameters = (np.pi / 2, right_parameters[1] + left_parameters[2], right_parameters[2]) elif name_tuple in (("u2", "u1"), ("u2", "p")): # u2(phi1, lambda1) * u1(lambda2) = u2(phi1, lambda1 + lambda2) right_name = "u2" right_parameters = (np.pi / 2, left_parameters[1], right_parameters[2] + left_parameters[2]) elif name_tuple in (("u1", "u3"), ("u1", "u"), ("p", "u3"), ("p", "u")): # u1(lambda1) * u3(theta2, phi2, lambda2) = # u3(theta2, phi2 + lambda1, lambda2) right_parameters = (right_parameters[0], right_parameters[1] + left_parameters[2], right_parameters[2]) elif name_tuple in (("u3", "u1"), ('u', 'u1'), ("u3", "p"), ("u", "p")): # u3(theta1, phi1, lambda1) * u1(lambda2) = # u3(theta1, phi1, lambda1 + lambda2) if use_u: right_name = 'u' else: right_name = "u3" right_parameters = (left_parameters[0], left_parameters[1], right_parameters[2] + left_parameters[2]) elif name_tuple == ("u2", "u2"): # Using Ry(pi/2).Rz(2*lambda).Ry(pi/2) = # Rz(pi/2).Ry(pi-2*lambda).Rz(pi/2), # u2(phi1, lambda1) * u2(phi2, lambda2) = # u3(pi - lambda1 - phi2, phi1 + pi/2, lambda2 + pi/2) if use_u: right_name = 'u' else: right_name = "u3" right_parameters = (np.pi - left_parameters[2] - right_parameters[1], left_parameters[1] + np.pi / 2, right_parameters[2] + np.pi / 2) elif name_tuple[1] == "nop": right_name = left_name right_parameters = left_parameters else: # For composing u3's or u2's with u3's, use # u2(phi, lambda) = u3(pi/2, phi, lambda) # together with the qiskit.mapper.compose_u3 method. if use_u: right_name = 'u' else: right_name = "u3" # Evaluate the symbolic expressions for efficiency right_parameters = Optimize1qGates.compose_u3(left_parameters[0], left_parameters[1], left_parameters[2], right_parameters[0], right_parameters[1], right_parameters[2]) # Why evalf()? This program: # OPENQASM 2.0; # include "qelib1.inc"; # qreg q[2]; # creg c[2]; # u3(0.518016983430947*pi,1.37051598592907*pi,1.36816383603222*pi) q[0]; # u3(1.69867232277986*pi,0.371448347747471*pi,0.461117217930936*pi) q[0]; # u3(0.294319836336836*pi,0.450325871124225*pi,1.46804720442555*pi) q[0]; # measure q -> c; # took >630 seconds (did not complete) to optimize without # calling evalf() at all, 19 seconds to optimize calling # evalf() AFTER compose_u3, and 1 second to optimize # calling evalf() BEFORE compose_u3. # 1. Here down, when we simplify, we add f(theta) to lambda to # correct the global phase when f(theta) is 2*pi. This isn't # necessary but the other steps preserve the global phase, so # we continue in that manner. # 2. The final step will remove Z rotations by 2*pi. # 3. Note that is_zero is true only if the expression is exactly # zero. If the input expressions have already been evaluated # then these final simplifications will not occur. # TODO After we refactor, we should have separate passes for # exact and approximate rewriting. # Y rotation is 0 mod 2*pi, so the gate is a u1 if abs(np.mod(right_parameters[0], (2 * np.pi))) < self.eps and right_name != "u1" \ and right_name != "p": if use_p: right_name = "p" else: right_name = "u1" right_parameters = (0, 0, right_parameters[1] + right_parameters[2] + right_parameters[0]) # Y rotation is pi/2 or -pi/2 mod 2*pi, so the gate is a u2 if right_name == "u3" or 'u': # theta = pi/2 + 2*k*pi right_angle = right_parameters[0] - np.pi / 2 if abs(right_angle) < self.eps: right_angle = 0 if abs(np.mod((right_angle), 2 * np.pi)) < self.eps: right_name = "u2" right_parameters = (np.pi / 2, right_parameters[1], right_parameters[2] + (right_parameters[0] - np.pi / 2)) # theta = -pi/2 + 2*k*pi right_angle = right_parameters[0] + np.pi / 2 if abs(right_angle) < self.eps: right_angle = 0 if abs(np.mod(right_angle, 2 * np.pi)) < self.eps: right_name = "u2" right_parameters = (np.pi / 2, right_parameters[1] + np.pi, right_parameters[2] - np.pi + (right_parameters[0] + np.pi / 2)) # u1 and lambda is 0 mod 2*pi so gate is nop (up to a global phase) if right_name in ("u1", "p") and abs( np.mod(right_parameters[2], 2 * np.pi)) < self.eps: right_name = "nop" if right_name == "u2" and "u2" not in self.basis: if use_u: right_name = 'u' else: right_name = "u3" if right_name in ("u1", "p") and right_name not in self.basis: if use_u: right_name = 'u' else: right_name = "u3" new_op = Gate(name="", num_qubits=1, params=[]) if right_name == "u1": new_op = U1Gate(right_parameters[2]) if right_name == "p": new_op = PhaseGate(right_parameters[2]) if right_name == "u2": new_op = U2Gate(right_parameters[1], right_parameters[2]) if right_name == "u": if "u" in self.basis: new_op = UGate(*right_parameters) if right_name == "u3": if "u3" in self.basis: new_op = U3Gate(*right_parameters) 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) # Delete the other nodes in the run for current_node in run[1:]: dag.remove_op_node(current_node) if right_name == "nop": dag.remove_op_node(run[0]) return dag
def circuit_to_gate(circuit, parameter_map=None): """Build a ``Gate`` object from a ``QuantumCircuit``. The gate is anonymous (not tied to a named quantum register), and so can be inserted into another circuit. The gate will have the same string name as the circuit. Args: circuit (QuantumCircuit): the input circuit. parameter_map (dict): For parameterized circuits, a mapping from parameters in the circuit to parameters to be used in the gate. If None, existing circuit parameters will also parameterize the Gate. Raises: QiskitError: if circuit is non-unitary or if parameter_map is not compatible with circuit Return: Gate: a Gate equivalent to the action of the input circuit. Upon decomposition, this gate will yield the components comprising the original circuit. """ if circuit.clbits: raise QiskitError('Circuit with classical bits cannot be converted ' 'to gate.') for inst, _, _ in circuit.data: if not isinstance(inst, Gate): raise QiskitError( ('One or more instructions cannot be converted to' ' a gate. "{}" is not a gate instruction').format(inst.name)) if parameter_map is None: parameter_dict = {p: p for p in circuit.parameters} else: parameter_dict = circuit._unroll_param_dict(parameter_map) if parameter_dict.keys() != circuit.parameters: raise QiskitError(('parameter_map should map all circuit parameters. ' 'Circuit parameters: {}, parameter_map: {}').format( circuit.parameters, parameter_dict)) gate = Gate(name=circuit.name, num_qubits=sum([qreg.size for qreg in circuit.qregs]), params=sorted(parameter_dict.values(), key=lambda p: p.name)) gate.condition = None def find_bit_position(bit): """find the index of a given bit (Register, int) within a flat ordered list of bits of the circuit """ if isinstance(bit, Qubit): ordered_regs = circuit.qregs else: ordered_regs = circuit.cregs reg_index = ordered_regs.index(bit.register) return sum([reg.size for reg in ordered_regs[:reg_index]]) + bit.index target = circuit.assign_parameters(parameter_dict, inplace=False) # pylint: disable=cyclic-import from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel # pylint: enable=cyclic-import sel.add_equivalence(gate, target) definition = target.data if gate.num_qubits > 0: q = QuantumRegister(gate.num_qubits, 'q') # The 3rd parameter in the output tuple) is hard coded to [] because # Gate objects do not have cregs set and we've verified that all # instructions are gates definition = list( map( lambda x: (x[0], list(map(lambda y: q[find_bit_position(y)], x[1])), []), definition)) gate.definition = definition return gate
def convert_to_target(conf_dict: dict, props_dict: dict = None, defs_dict: dict = None) -> Target: """Uses configuration, properties and pulse defaults dicts to construct and return Target class. """ name_mapping = { "id": IGate(), "sx": SXGate(), "x": XGate(), "cx": CXGate(), "rz": RZGate(Parameter("λ")), "reset": Reset(), } custom_gates = {} qubit_props = None if props_dict: qubit_props = qubit_props_from_props(props_dict) target = Target(qubit_properties=qubit_props) # Parse from properties if it exsits if props_dict is not None: # Parse instructions gates = {} for gate in props_dict["gates"]: name = gate["gate"] if name in name_mapping: if name not in gates: gates[name] = {} elif name not in custom_gates: custom_gate = Gate(name, len(gate["qubits"]), []) custom_gates[name] = custom_gate gates[name] = {} qubits = tuple(gate["qubits"]) gate_props = {} for param in gate["parameters"]: if param["name"] == "gate_error": gate_props["error"] = param["value"] if param["name"] == "gate_length": gate_props["duration"] = apply_prefix( param["value"], param["unit"]) gates[name][qubits] = InstructionProperties(**gate_props) for gate, props in gates.items(): if gate in name_mapping: inst = name_mapping.get(gate) else: inst = custom_gates[gate] target.add_instruction(inst, props) # Create measurement instructions: measure_props = {} count = 0 for qubit in props_dict["qubits"]: qubit_prop = {} for prop in qubit: if prop["name"] == "readout_length": qubit_prop["duration"] = apply_prefix( prop["value"], prop["unit"]) if prop["name"] == "readout_error": qubit_prop["error"] = prop["value"] measure_props[(count, )] = InstructionProperties(**qubit_prop) count += 1 target.add_instruction(Measure(), measure_props) # Parse from configuration because properties doesn't exist else: for gate in conf_dict["gates"]: name = gate["name"] gate_props = {tuple(x): None for x in gate["coupling_map"]} if name in name_mapping: target.add_instruction(name_mapping[name], gate_props) else: custom_gate = Gate(name, len(gate["coupling_map"][0]), []) target.add_instruction(custom_gate, gate_props) measure_props = {(n, ): None for n in range(conf_dict["n_qubits"])} target.add_instruction(Measure(), measure_props) # parse global configuration properties dt = conf_dict.get("dt") if dt: target.dt = dt * 1e-9 if "timing_constraints" in conf_dict: target.granularity = conf_dict["timing_constraints"].get("granularity") target.min_length = conf_dict["timing_constraints"].get("min_length") target.pulse_alignment = conf_dict["timing_constraints"].get( "pulse_alignment") target.aquire_alignment = conf_dict["timing_constraints"].get( "acquire_alignment") # If pulse defaults exists use that as the source of truth if defs_dict is not None: # TODO remove the usage of PulseDefaults as it will be deprecated in the future pulse_defs = PulseDefaults.from_dict(defs_dict) inst_map = pulse_defs.instruction_schedule_map for inst in inst_map.instructions: for qarg in inst_map.qubits_with_instruction(inst): sched = inst_map.get(inst, qarg) if inst in target: try: qarg = tuple(qarg) except TypeError: qarg = (qarg, ) if inst == "measure": for qubit in qarg: target[inst][(qubit, )].calibration = sched else: target[inst][qarg].calibration = sched target.add_instruction(Delay(Parameter("t")), {(bit, ): None for bit in range(target.num_qubits)}) return target
def _return_repeat(self, exponent: float) -> "Gate": return Gate(name=f"{self.name}*{exponent}", num_qubits=self.num_qubits, params=[])
def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None, label=None): """Build a ``Gate`` object from a ``QuantumCircuit``. The gate is anonymous (not tied to a named quantum register), and so can be inserted into another circuit. The gate will have the same string name as the circuit. Args: circuit (QuantumCircuit): the input circuit. parameter_map (dict): For parameterized circuits, a mapping from parameters in the circuit to parameters to be used in the gate. If None, existing circuit parameters will also parameterize the Gate. equivalence_library (EquivalenceLibrary): Optional equivalence library where the converted gate will be registered. label (str): Optional gate label. Raises: QiskitError: if circuit is non-unitary or if parameter_map is not compatible with circuit Return: Gate: a Gate equivalent to the action of the input circuit. Upon decomposition, this gate will yield the components comprising the original circuit. """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit if circuit.clbits: raise QiskitError("Circuit with classical bits cannot be converted " "to gate.") for inst, _, _ in circuit.data: if not isinstance(inst, Gate): raise QiskitError( ( "One or more instructions cannot be converted to" ' a gate. "{}" is not a gate instruction' ).format(inst.name) ) if parameter_map is None: parameter_dict = {p: p for p in circuit.parameters} else: parameter_dict = circuit._unroll_param_dict(parameter_map) if parameter_dict.keys() != circuit.parameters: raise QiskitError( ( "parameter_map should map all circuit parameters. " "Circuit parameters: {}, parameter_map: {}" ).format(circuit.parameters, parameter_dict) ) gate = Gate( name=circuit.name, num_qubits=sum([qreg.size for qreg in circuit.qregs]), params=[*parameter_dict.values()], label=label, ) gate.condition = None target = circuit.assign_parameters(parameter_dict, inplace=False) if equivalence_library is not None: equivalence_library.add_equivalence(gate, target) rules = target.data if gate.num_qubits > 0: q = QuantumRegister(gate.num_qubits, "q") qubit_map = {bit: q[idx] for idx, bit in enumerate(circuit.qubits)} # The 3rd parameter in the output tuple) is hard coded to [] because # Gate objects do not have cregs set and we've verified that all # instructions are gates rules = [(inst, [qubit_map[y] for y in qargs], []) for inst, qargs, _ in rules] qc = QuantumCircuit(q, name=gate.name, global_phase=target.global_phase) for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) gate.definition = qc return gate
def convert_to_target( configuration: BackendConfiguration, properties: BackendProperties = None, defaults: PulseDefaults = None, ) -> Target: """Uses configuration, properties and pulse defaults to construct and return Target class. """ name_mapping = { "id": IGate(), "sx": SXGate(), "x": XGate(), "cx": CXGate(), "rz": RZGate(Parameter("λ")), "reset": Reset(), } custom_gates = {} target = None # Parse from properties if it exsits if properties is not None: qubit_properties = qubit_props_list_from_props(properties=properties) target = Target( num_qubits=configuration.n_qubits, qubit_properties=qubit_properties ) # Parse instructions gates: Dict[str, Any] = {} for gate in properties.gates: name = gate.gate if name in name_mapping: if name not in gates: gates[name] = {} elif name not in custom_gates: custom_gate = Gate(name, len(gate.qubits), []) custom_gates[name] = custom_gate gates[name] = {} qubits = tuple(gate.qubits) gate_props = {} for param in gate.parameters: if param.name == "gate_error": gate_props["error"] = param.value if param.name == "gate_length": gate_props["duration"] = apply_prefix(param.value, param.unit) gates[name][qubits] = InstructionProperties(**gate_props) for gate, props in gates.items(): if gate in name_mapping: inst = name_mapping.get(gate) else: inst = custom_gates[gate] target.add_instruction(inst, props) # Create measurement instructions: measure_props = {} for qubit, _ in enumerate(properties.qubits): measure_props[(qubit,)] = InstructionProperties( duration=properties.readout_length(qubit), error=properties.readout_error(qubit), ) target.add_instruction(Measure(), measure_props) # Parse from configuration because properties doesn't exist else: target = Target(num_qubits=configuration.n_qubits) for gate in configuration.gates: name = gate.name gate_props = ( {tuple(x): None for x in gate.coupling_map} # type: ignore[misc] if hasattr(gate, "coupling_map") else {None: None} ) gate_len = len(gate.coupling_map[0]) if hasattr(gate, "coupling_map") else 0 if name in name_mapping: target.add_instruction(name_mapping[name], gate_props) else: custom_gate = Gate(name, gate_len, []) target.add_instruction(custom_gate, gate_props) target.add_instruction(Measure()) # parse global configuration properties if hasattr(configuration, "dt"): target.dt = configuration.dt if hasattr(configuration, "timing_constraints"): target.granularity = configuration.timing_constraints.get("granularity") target.min_length = configuration.timing_constraints.get("min_length") target.pulse_alignment = configuration.timing_constraints.get("pulse_alignment") target.aquire_alignment = configuration.timing_constraints.get( "acquire_alignment" ) # If a pulse defaults exists use that as the source of truth if defaults is not None: inst_map = defaults.instruction_schedule_map for inst in inst_map.instructions: for qarg in inst_map.qubits_with_instruction(inst): sched = inst_map.get(inst, qarg) if inst in target: try: qarg = tuple(qarg) except TypeError: qarg = (qarg,) if inst == "measure": for qubit in qarg: target[inst][(qubit,)].calibration = sched else: target[inst][qarg].calibration = sched if "delay" not in target: target.add_instruction( Delay(Parameter("t")), {(bit,): None for bit in range(target.num_qubits)} ) return target