def dagger(self, inv_dict=None, suffix="-INV"): """ Creates the conjugate transpose of the Quil program. The program must not contain any irreversible actions (measurement, control flow, qubit allocation). :return: The Quil program's inverse :rtype: Program """ if not self.is_protoquil(): raise ValueError("Program must be valid Protoquil") daggered = Program() for gate in self._defined_gates: if inv_dict is None or gate.name not in inv_dict: if gate.parameters: raise TypeError( "Cannot auto define daggered version of parameterized gates" ) daggered.defgate(gate.name + suffix, gate.matrix.T.conj()) for gate in reversed(self._instructions): reversed_gate = None qubits = [unpack_qubit(q) for q in gate.qubits] if gate.name in QUANTUM_GATES: if gate.name == "S": reversed_gate = Gate("PHASE", [-pi / 2], qubits) elif gate.name == "T": reversed_gate = Gate("RZ", [pi / 4], qubits) elif gate.name == "ISWAP": reversed_gate = Gate("PSWAP", [pi / 2], qubits) else: negated_params = list(map(lambda x: -1 * x, gate.params)) reversed_gate = Gate(gate.name, negated_params, qubits) reversed_gate.modifiers = gate.modifiers daggered.inst(reversed_gate) else: if inv_dict is None or gate.name not in inv_dict: gate_inv_name = gate.name + suffix else: gate_inv_name = inv_dict[gate.name] reversed_gate = Gate(gate_inv_name, gate.params, qubits) reversed_gate.modifiers = gate.modifiers daggered.inst(reversed_gate) return daggered
def address_qubits(program, qubit_mapping=None): """ Takes a program which contains placeholders and assigns them all defined values. Either all qubits must be defined or all undefined. If qubits are undefined, you may provide a qubit mapping to specify how placeholders get mapped to actual qubits. If a mapping is not provided, integers 0 through N are used. This function will also instantiate any label placeholders. :param program: The program. :param qubit_mapping: A dictionary-like object that maps from :py:class:`QubitPlaceholder` to :py:class:`Qubit` or ``int`` (but not both). :return: A new Program with all qubit and label placeholders assigned to real qubits and labels. """ fake_qubits, real_qubits, qubits = _what_type_of_qubit_does_it_use(program) if real_qubits: if qubit_mapping is not None: warnings.warn( "A qubit mapping was provided but the program does not " "contain any placeholders to map!") return program if qubit_mapping is None: qubit_mapping = {qp: Qubit(i) for i, qp in enumerate(qubits)} else: if all(isinstance(v, Qubit) for v in qubit_mapping.values()): pass # we good elif all(isinstance(v, int) for v in qubit_mapping.values()): qubit_mapping = {k: Qubit(v) for k, v in qubit_mapping.items()} else: raise ValueError( "Qubit mapping must map to type Qubit or int (but not both)") result = [] for instr in program: # Remap qubits on Gate, Measurement, and ResetQubit instructions if isinstance(instr, Gate): remapped_qubits = [qubit_mapping[q] for q in instr.qubits] gate = Gate(instr.name, instr.params, remapped_qubits) gate.modifiers = instr.modifiers result.append(gate) elif isinstance(instr, Measurement): result.append( Measurement(qubit_mapping[instr.qubit], instr.classical_reg)) elif isinstance(instr, ResetQubit): result.append(ResetQubit(qubit_mapping[instr.qubit])) elif isinstance(instr, Pragma): new_args = [] for arg in instr.args: # Pragmas can have arguments that represent things besides qubits, so here we # make sure to just look up the QubitPlaceholders. if isinstance(arg, QubitPlaceholder): new_args.append(qubit_mapping[arg]) else: new_args.append(arg) result.append( Pragma(instr.command, new_args, instr.freeform_string)) # Otherwise simply add it to the result else: result.append(instr) new_program = program.copy() new_program._instructions = result return new_program