def test_opaque_instruction_with_label(self): """Test that custom opaque instruction is correctly serialized with a label""" custom_gate = Instruction("black_box", 1, 0, []) custom_gate.label = "My Special Black Box Instruction" 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 _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 circuit_to_instruction(circuit): """Build an ``Instruction`` object from a ``QuantumCircuit``. The instruction is anonymous (not tied to a named quantum register), and so can be inserted into another circuit. The instruction will have the same string name as the circuit. Args: circuit (QuantumCircuit): the input circuit. Return: Instruction: an instruction equivalent to the action of the input circuit. Upon decomposition, this instruction will yield the components comprising the original circuit. """ instruction = Instruction( name=circuit.name, num_qubits=sum([qreg.size for qreg in circuit.qregs]), num_clbits=sum([creg.size for creg in circuit.cregs]), params=[]) instruction.control = 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[0], QuantumRegister): ordered_regs = circuit.qregs else: ordered_regs = circuit.cregs reg_index = ordered_regs.index(bit[0]) return sum([reg.size for reg in ordered_regs[:reg_index]]) + bit[1] definition = circuit.data.copy() if instruction.num_qubits > 0: q = QuantumRegister(instruction.num_qubits, 'q') if instruction.num_clbits > 0: c = ClassicalRegister(instruction.num_clbits, 'c') definition = list( map( lambda x: (x[0], list(map(lambda y: (q, find_bit_position(y)), x[1])), list(map(lambda y: (c, find_bit_position(y)), x[2]))), definition)) instruction.definition = definition return instruction
def test_custom_instruction(self): """Test that custom instruction is correctly serialized""" custom_gate = Instruction("black_box", 1, 0, []) 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 to_instruction(self): """Convert to a Kraus or UnitaryGate circuit instruction. If the channel is unitary it will be added as a unitary gate, otherwise it will be added as a kraus simulator instruction. Returns: qiskit.circuit.Instruction: A kraus instruction for the channel. Raises: QiskitError: if input data is not an N-qubit CPTP quantum channel. """ from qiskit.circuit.instruction import Instruction # Check if input is an N-qubit CPTP channel. num_qubits = int(np.log2(self._input_dim)) if self._input_dim != self._output_dim or 2**num_qubits != self._input_dim: raise QiskitError( 'Cannot convert QuantumChannel to Instruction: channel is not an N-qubit channel.' ) if not self.is_cptp(): raise QiskitError( 'Cannot convert QuantumChannel to Instruction: channel is not CPTP.' ) # Next we convert to the Kraus representation. Since channel is CPTP we know # that there is only a single set of Kraus operators kraus, _ = _to_kraus(self._channel_rep, self._data, *self.dim) # If we only have a single Kraus operator then the channel is # a unitary channel so can be converted to a UnitaryGate. We do this by # converting to an Operator and using its to_instruction method if len(kraus) == 1: return Operator(kraus[0]).to_instruction() return Instruction('kraus', num_qubits, 0, kraus)
def test_custom_instruction_with_label(self): """Test that custom instruction is correctly serialized with a label""" custom_gate = Instruction("black_box", 1, 0, []) 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 Instruction 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] 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_empty_tuple_param(self): """Test qpy with an instruction that contains an empty tuple.""" inst = Instruction("empty_tuple_test", 1, 0, [tuple()]) qc = QuantumCircuit(1) qc.append(inst, [0]) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) new_circuit = load(qpy_file)[0] self.assertEqual(qc, new_circuit)
def test_nested_tuple_param(self): """Test qpy with an instruction that contains nested tuples.""" inst = Instruction("tuple_test", 1, 0, [((((0, 1), (0, 1)), 2, 3), ("A", "B", "C"))]) qc = QuantumCircuit(1) qc.append(inst, [0]) qpy_file = io.BytesIO() dump(qc, qpy_file) qpy_file.seek(0) new_circuit = load(qpy_file)[0] self.assertEqual(qc, new_circuit)
def test_opaque_instruction(self): """Test that custom opaque instruction is correctly serialized""" custom_gate = Instruction("black_box", 1, 0, []) 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 _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 _create_op(self, name, params): if name in self.standard_extension: op = self.standard_extension[name](*params) elif name in self.gates: if self.gates[name]['opaque']: # call an opaque gate op = Gate(name=name, num_qubits=self.gates[name]['n_bits'], params=params) else: # call a custom gate op = Instruction(name=name, num_qubits=self.gates[name]['n_bits'], num_clbits=0, params=params) op.definition = self._gate_rules_to_qiskit_circuit(self.gates[name], params=params) else: raise QiskitError("unknown operation for ast node name %s" % name) return op
def test_custom_instruction_with_noop_definition(self): """Test that a custom instruction whose definition contains no elements is serialized with a proper definition. Regression test of gh-7429.""" empty = QuantumCircuit(1, name="empty").to_instruction() opaque = Instruction("opaque", 1, 0, []) 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 _copy_mutable_properties(self, instruction: Instruction) -> Instruction: """Copy mutable properties from ourselves onto a non-placeholder instruction. The mutable properties are expected to be things like ``condition``, added onto a placeholder by the :meth:`c_if` method. This mutates ``instruction``, and returns the same instance that was passed. This is mostly intended to make writing concrete versions of :meth:`.concrete_instruction` easy. The complete list of mutations is: * ``condition``, added by :meth:`c_if`. Args: instruction: the concrete instruction instance to be mutated. Returns: The same instruction instance that was passed, but mutated to propagate the tracked changes to this class. """ # In general the tuple creation should be a no-op, because ``tuple(t) is t`` for tuples. instruction.condition = None if self.condition is None else tuple(self.condition) return instruction
def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None): """Build an ``Instruction`` object from a ``QuantumCircuit``. The instruction is anonymous (not tied to a named quantum register), and so can be inserted into another circuit. The instruction 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 instruction. If None, existing circuit parameters will also parameterize the instruction. equivalence_library (EquivalenceLibrary): Optional equivalence library where the converted instruction will be registered. Raises: QiskitError: if parameter_map is not compatible with circuit Return: qiskit.circuit.Instruction: an instruction equivalent to the action of the input circuit. Upon decomposition, this instruction will yield the components comprising the original circuit. Example: .. jupyter-execute:: from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.converters import circuit_to_instruction %matplotlib inline q = QuantumRegister(3, 'q') c = ClassicalRegister(3, 'c') circ = QuantumCircuit(q, c) circ.h(q[0]) circ.cx(q[0], q[1]) circ.measure(q[0], c[0]) circ.rz(0.5, q[1]).c_if(c, 2) circuit_to_instruction(circ) """ 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)) instruction = Instruction( name=circuit.name, num_qubits=sum([qreg.size for qreg in circuit.qregs]), num_clbits=sum([creg.size for creg in circuit.cregs]), params=sorted(parameter_dict.values(), key=lambda p: p.name)) instruction.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) if equivalence_library is not None: equivalence_library.add_equivalence(instruction, target) definition = target.data if instruction.num_qubits > 0: q = QuantumRegister(instruction.num_qubits, 'q') if instruction.num_clbits > 0: c = ClassicalRegister(instruction.num_clbits, 'c') definition = list( map( lambda x: (x[0], list(map(lambda y: q[find_bit_position(y)], x[1])), list(map(lambda y: c[find_bit_position(y)], x[2]))), definition)) instruction.definition = definition return instruction
def circuit_to_instruction(circuit, parameter_map=None): """Build an ``Instruction`` object from a ``QuantumCircuit``. The instruction is anonymous (not tied to a named quantum register), and so can be inserted into another circuit. The instruction 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 instruction. If None, existing circuit parameters will also parameterize the instruction. Raises: QiskitError: if parameter_map is not compatible with circuit Return: Instruction: an instruction equivalent to the action of the input circuit. Upon decomposition, this instruction will yield the components comprising the original circuit. """ if parameter_map is None: parameter_map = {p: p for p in circuit.parameters} if parameter_map.keys() != circuit.parameters: raise QiskitError(('parameter_map should map all circuit parameters. ' 'Circuit parameters: {}, parameter_map: {}').format( circuit.parameters, parameter_map)) instruction = Instruction(name=circuit.name, num_qubits=sum([qreg.size for qreg in circuit.qregs]), num_clbits=sum([creg.size for creg in circuit.cregs]), params=sorted(parameter_map.values(), key=lambda p: p.name)) instruction.control = 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[0], QuantumRegister): ordered_regs = circuit.qregs else: ordered_regs = circuit.cregs reg_index = ordered_regs.index(bit[0]) return sum([reg.size for reg in ordered_regs[:reg_index]]) + bit[1] target = circuit.copy() target._substitute_parameters(parameter_map) definition = target.data if instruction.num_qubits > 0: q = QuantumRegister(instruction.num_qubits, 'q') if instruction.num_clbits > 0: c = ClassicalRegister(instruction.num_clbits, 'c') definition = list(map(lambda x: (x[0], list(map(lambda y: (q, find_bit_position(y)), x[1])), list(map(lambda y: (c, find_bit_position(y)), x[2]))), definition)) instruction.definition = definition return instruction
def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None, label=None): """Build an ``Instruction`` object from a ``QuantumCircuit``. The instruction is anonymous (not tied to a named quantum register), and so can be inserted into another circuit. The instruction 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 instruction. If None, existing circuit parameters will also parameterize the instruction. equivalence_library (EquivalenceLibrary): Optional equivalence library where the converted instruction will be registered. label (str): Optional instruction label. Raises: QiskitError: if parameter_map is not compatible with circuit Return: qiskit.circuit.Instruction: an instruction equivalent to the action of the input circuit. Upon decomposition, this instruction will yield the components comprising the original circuit. Example: .. jupyter-execute:: from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.converters import circuit_to_instruction %matplotlib inline q = QuantumRegister(3, 'q') c = ClassicalRegister(3, 'c') circ = QuantumCircuit(q, c) circ.h(q[0]) circ.cx(q[0], q[1]) circ.measure(q[0], c[0]) circ.rz(0.5, q[1]).c_if(c, 2) circuit_to_instruction(circ) """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit 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) ) instruction = Instruction( name=circuit.name, num_qubits=sum(qreg.size for qreg in circuit.qregs), num_clbits=sum(creg.size for creg in circuit.cregs), params=[*parameter_dict.values()], label=label, ) instruction.condition = None target = circuit.assign_parameters(parameter_dict, inplace=False) if equivalence_library is not None: equivalence_library.add_equivalence(instruction, target) definition = target.data regs = [] if instruction.num_qubits > 0: q = QuantumRegister(instruction.num_qubits, "q") regs.append(q) if instruction.num_clbits > 0: c = ClassicalRegister(instruction.num_clbits, "c") regs.append(c) qubit_map = {bit: q[idx] for idx, bit in enumerate(circuit.qubits)} clbit_map = {bit: c[idx] for idx, bit in enumerate(circuit.clbits)} definition = [ (inst, [qubit_map[y] for y in qargs], [clbit_map[y] for y in cargs]) for inst, qargs, cargs in definition ] # fix condition for rule in definition: condition = rule[0].condition if condition: reg, val = condition if isinstance(reg, Clbit): idx = 0 for creg in circuit.cregs: if reg not in creg: idx += creg.size else: cond_reg = creg break rule[0].condition = (c[idx + list(cond_reg).index(reg)], val) elif reg.size == c.size: rule[0].condition = (c, val) else: raise QiskitError( "Cannot convert condition in circuit with " "multiple classical registers to instruction" ) qc = QuantumCircuit(*regs, name=instruction.name) for instr, qargs, cargs in definition: qc._append(instr, qargs, cargs) if circuit.global_phase: qc.global_phase = circuit.global_phase instruction.definition = qc return instruction
def _experiments_to_circuits(qobj): """Return a list of QuantumCircuit object(s) from a qobj. Args: qobj (Qobj): The Qobj object to convert to QuantumCircuits Returns: list: A list of QuantumCircuit objects from the qobj """ if not qobj.experiments: return None circuits = [] for exp in qobj.experiments: quantum_registers = [QuantumRegister(i[1], name=i[0]) for i in exp.header.qreg_sizes] classical_registers = [ClassicalRegister(i[1], name=i[0]) for i in exp.header.creg_sizes] circuit = QuantumCircuit(*quantum_registers, *classical_registers, name=exp.header.name) qreg_dict = collections.OrderedDict() creg_dict = collections.OrderedDict() for reg in quantum_registers: qreg_dict[reg.name] = reg for reg in classical_registers: creg_dict[reg.name] = reg conditional = {} for i in exp.instructions: name = i.name qubits = [] params = getattr(i, "params", []) try: for qubit in i.qubits: qubit_label = exp.header.qubit_labels[qubit] qubits.append(qreg_dict[qubit_label[0]][qubit_label[1]]) except Exception: # pylint: disable=broad-except pass clbits = [] try: for clbit in i.memory: clbit_label = exp.header.clbit_labels[clbit] clbits.append(creg_dict[clbit_label[0]][clbit_label[1]]) except Exception: # pylint: disable=broad-except pass if hasattr(circuit, name): instr_method = getattr(circuit, name) if i.name in ["snapshot"]: _inst = instr_method( i.label, snapshot_type=i.snapshot_type, qubits=qubits, params=params ) elif i.name == "initialize": _inst = instr_method(params, qubits) elif i.name == "isometry": _inst = instr_method(*params, qubits, clbits) elif i.name in ["mcx", "mcu1", "mcp"]: _inst = instr_method(*params, qubits[:-1], qubits[-1], *clbits) else: _inst = instr_method(*params, *qubits, *clbits) elif name == "bfunc": conditional["value"] = int(i.val, 16) full_bit_size = sum(creg_dict[x].size for x in creg_dict) mask_map = {} raw_map = {} raw = [] for creg in creg_dict: size = creg_dict[creg].size reg_raw = [1] * size if not raw: raw = reg_raw else: for pos, val in enumerate(raw): if val == 1: raw[pos] = 0 raw = reg_raw + raw mask = [0] * (full_bit_size - len(raw)) + raw raw_map[creg] = mask mask_map[int("".join(str(x) for x in mask), 2)] = creg creg = mask_map[int(i.mask, 16)] conditional["register"] = creg_dict[creg] val = int(i.val, 16) mask = raw_map[creg] for j in reversed(mask): if j == 0: val = val >> 1 else: conditional["value"] = val break else: _inst = temp_opaque_instruction = Instruction( name=name, num_qubits=len(qubits), num_clbits=len(clbits), params=params ) circuit.append(temp_opaque_instruction, qubits, clbits) if conditional and name != "bfunc": _inst.c_if(conditional["register"], conditional["value"]) conditional = {} pulse_lib = qobj.config.pulse_library if hasattr(qobj.config, "pulse_library") else [] parametric_pulses = ( qobj.config.parametric_pulses if hasattr(qobj.config, "parametric_pulses") else [] ) # The dict update method did not work here; could investigate in the future if hasattr(qobj.config, "calibrations"): circuit.calibrations = dict( **circuit.calibrations, **_qobj_to_circuit_cals(qobj, pulse_lib, parametric_pulses) ) if hasattr(exp.config, "calibrations"): circuit.calibrations = dict( **circuit.calibrations, **_qobj_to_circuit_cals(exp, pulse_lib, parametric_pulses) ) circuits.append(circuit) return circuits
def _experiments_to_circuits(qobj): """Return a list of QuantumCircuit object(s) from a qobj Args: qobj (Qobj): The Qobj object to convert to QuantumCircuits Returns: list: A list of QuantumCircuit objects from the qobj """ if qobj.experiments: circuits = [] for x in qobj.experiments: quantum_registers = [ QuantumRegister(i[1], name=i[0]) for i in x.header.qreg_sizes ] classical_registers = [ ClassicalRegister(i[1], name=i[0]) for i in x.header.creg_sizes ] circuit = QuantumCircuit(*quantum_registers, *classical_registers, name=x.header.name) qreg_dict = {} creg_dict = {} for reg in quantum_registers: qreg_dict[reg.name] = reg for reg in classical_registers: creg_dict[reg.name] = reg for i in x.instructions: name = i.name if i.name == 'id': name = 'iden' qubits = [] params = getattr(i, 'params', []) try: for qubit in i.qubits: qubit_label = x.header.qubit_labels[qubit] qubits.append( qreg_dict[qubit_label[0]][qubit_label[1]]) except Exception: # pylint: disable=broad-except pass clbits = [] try: for clbit in i.memory: clbit_label = x.header.clbit_labels[clbit] clbits.append( creg_dict[clbit_label[0]][clbit_label[1]]) except Exception: # pylint: disable=broad-except pass if hasattr(circuit, name): instr_method = getattr(circuit, name) if i.name in ['snapshot']: instr_method(i.label, snapshot_type=i.snapshot_type, qubits=qubits, params=params) elif i.name == 'initialize': instr_method(params, qubits) else: instr_method(*params, *qubits, *clbits) else: temp_opaque_instruction = Instruction( name=name, num_qubits=len(qubits), num_clbits=len(clbits), params=params) circuit.append(temp_opaque_instruction, qubits, clbits) circuits.append(circuit) return circuits return None
def run(self, dag): """Return a new circuit that has been optimized.""" runs = dag.collect_runs(["u1", "u2", "u3", "id"]) for run in runs: run_qarg = dag.node(run[0])["qargs"][0] right_name = "u1" right_parameters = (0, 0, 0) # (theta, phi, lambda) for current_node in run: node = dag.node(current_node) left_name = node["name"] if (node["condition"] is not None or len(node["qargs"]) != 1 or node["qargs"][0] != run_qarg or left_name not in ["u1", "u2", "u3", "id"]): raise MapperError("internal error") if left_name == "u1": left_parameters = (0, 0, node["op"].params[0]) elif left_name == "u2": left_parameters = (np.pi / 2, node["op"].params[0], node["op"].params[1]) elif left_name == "u3": left_parameters = tuple(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 data of the first node in the run new_op = Instruction("", [], [], []) if right_name == "u1": new_op = U1Gate(right_parameters[2], run_qarg) if right_name == "u2": new_op = U2Gate(right_parameters[1], right_parameters[2], run_qarg) if right_name == "u3": new_op = U3Gate(*right_parameters, run_qarg) dag.node(run[0])['name'] = right_name dag.node(run[0])['op'] = new_op # 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 _experiments_to_circuits(qobj): """Return a list of QuantumCircuit object(s) from a qobj. Args: qobj (Qobj): The Qobj object to convert to QuantumCircuits Returns: list: A list of QuantumCircuit objects from the qobj """ if qobj.experiments: circuits = [] for x in qobj.experiments: quantum_registers = [ QuantumRegister(i[1], name=i[0]) for i in x.header.qreg_sizes ] classical_registers = [ ClassicalRegister(i[1], name=i[0]) for i in x.header.creg_sizes ] circuit = QuantumCircuit(*quantum_registers, *classical_registers, name=x.header.name) qreg_dict = collections.OrderedDict() creg_dict = collections.OrderedDict() for reg in quantum_registers: qreg_dict[reg.name] = reg for reg in classical_registers: creg_dict[reg.name] = reg conditional = {} for i in x.instructions: name = i.name qubits = [] params = getattr(i, 'params', []) try: for qubit in i.qubits: qubit_label = x.header.qubit_labels[qubit] qubits.append( qreg_dict[qubit_label[0]][qubit_label[1]]) except Exception: # pylint: disable=broad-except pass clbits = [] try: for clbit in i.memory: clbit_label = x.header.clbit_labels[clbit] clbits.append( creg_dict[clbit_label[0]][clbit_label[1]]) except Exception: # pylint: disable=broad-except pass if hasattr(circuit, name): instr_method = getattr(circuit, name) if i.name in ['snapshot']: _inst = instr_method(i.label, snapshot_type=i.snapshot_type, qubits=qubits, params=params) elif i.name == 'initialize': _inst = instr_method(params, qubits) elif i.name == 'isometry': _inst = instr_method(*params, qubits, clbits) else: _inst = instr_method(*params, *qubits, *clbits) elif name == 'bfunc': conditional['value'] = int(i.val, 16) full_bit_size = sum([creg_dict[x].size for x in creg_dict]) mask_map = {} raw_map = {} raw = [] for creg in creg_dict: size = creg_dict[creg].size reg_raw = [1] * size if not raw: raw = reg_raw else: for pos, val in enumerate(raw): if val == 1: raw[pos] = 0 raw = reg_raw + raw mask = [0] * (full_bit_size - len(raw)) + raw raw_map[creg] = mask mask_map[int("".join(str(x) for x in mask), 2)] = creg creg = mask_map[int(i.mask, 16)] conditional['register'] = creg_dict[creg] val = int(i.val, 16) mask = raw_map[creg] for j in reversed(mask): if j == 0: val = val >> 1 else: conditional['value'] = val break else: _inst = temp_opaque_instruction = Instruction( name=name, num_qubits=len(qubits), num_clbits=len(clbits), params=params) circuit.append(temp_opaque_instruction, qubits, clbits) if conditional and name != 'bfunc': _inst.c_if(conditional['register'], conditional['value']) conditional = {} circuits.append(circuit) return circuits return None
def optimize_1q_gates(circuit): """Simplify runs of single qubit gates in the QX basis. Return a new circuit that has been optimized. """ from qiskit.transpiler.passes.mapping.unroller import Unroller qx_basis = ["u1", "u2", "u3", "cx", "id"] unrolled = Unroller(qx_basis).run(circuit) runs = unrolled.collect_runs(["u1", "u2", "u3", "id"]) for run in runs: run_qarg = unrolled.multi_graph.node[run[0]]["qargs"][0] right_name = "u1" right_parameters = (N(0), N(0), N(0)) # (theta, phi, lambda) for current_node in run: nd = unrolled.multi_graph.node[current_node] left_name = nd["name"] if (nd["condition"] is not None or len(nd["qargs"]) != 1 or nd["qargs"][0] != run_qarg or left_name not in ["u1", "u2", "u3", "id"]): raise MapperError("internal error") if left_name == "u1": left_parameters = (N(0), N(0), nd["op"].param[0]) elif left_name == "u2": left_parameters = (sympy.pi / 2, nd["op"].param[0], nd["op"].param[1]) elif left_name == "u3": left_parameters = tuple(nd["op"].param) else: left_name = "u1" # replace id with u1 left_parameters = (N(0), N(0), N(0)) # Compose gates name_tuple = (left_name, right_name) if name_tuple == ("u1", "u1"): # u1(lambda1) * u1(lambda2) = u1(lambda1 + lambda2) right_parameters = (N(0), N(0), right_parameters[2] + left_parameters[2]) elif name_tuple == ("u1", "u2"): # u1(lambda1) * u2(phi2, lambda2) = u2(phi2 + lambda1, lambda2) right_parameters = (sympy.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 = (sympy.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 = (sympy.pi - left_parameters[2] - right_parameters[1], left_parameters[1] + sympy.pi / 2, right_parameters[2] + sympy.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 left_parameters = tuple( map(lambda x: x.evalf(), list(left_parameters))) right_parameters = tuple( map(lambda x: x.evalf(), list(right_parameters))) right_parameters = 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 (right_parameters[0] % (2 * sympy.pi)).is_zero \ 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 ((right_parameters[0] - sympy.pi / 2) % (2 * sympy.pi)).is_zero: right_name = "u2" right_parameters = (sympy.pi / 2, right_parameters[1], right_parameters[2] + (right_parameters[0] - sympy.pi / 2)) # theta = -pi/2 + 2*k*pi if ((right_parameters[0] + sympy.pi / 2) % (2 * sympy.pi)).is_zero: right_name = "u2" right_parameters = (sympy.pi / 2, right_parameters[1] + sympy.pi, right_parameters[2] - sympy.pi + (right_parameters[0] + sympy.pi / 2)) # u1 and lambda is 0 mod 2*pi so gate is nop (up to a global phase) if right_name == "u1" and (right_parameters[2] % (2 * sympy.pi)).is_zero: right_name = "nop" # Simplify the symbolic parameters right_parameters = tuple( map(sympy.simplify, list(right_parameters))) # Replace the data of the first node in the run new_op = Instruction("", [], [], []) if right_name == "u1": new_op = U1Gate(right_parameters[2], run_qarg) if right_name == "u2": new_op = U2Gate(right_parameters[1], right_parameters[2], run_qarg) if right_name == "u3": new_op = U3Gate(*right_parameters, run_qarg) nx.set_node_attributes(unrolled.multi_graph, name='name', values={run[0]: right_name}) nx.set_node_attributes(unrolled.multi_graph, name='op', values={run[0]: new_op}) # Delete the other nodes in the run for current_node in run[1:]: unrolled._remove_op_node(current_node) if right_name == "nop": unrolled._remove_op_node(run[0]) return unrolled