def tk_to_pyquil(circ: Union[Circuit,PhysicalCircuit]) -> Program: """ Convert a :math:`\\mathrm{t|ket}\\rangle` :py:class:`Circuit` to a :py:class:`pyquil.Program` . :param circ: A circuit to be converted :return: The converted circuit """ p = Program() ro = p.declare('ro', 'BIT', circ.n_qubits) for command in circ: op = command.op optype = op.get_type() if optype == OpType.Input or optype == OpType.Output: continue elif optype == OpType.Measure: p += Measurement(Qubit(command.qubits[0]), ro[int(op.get_desc())]) continue try: gatetype = _known_quil_gate_rev[optype] except KeyError as error: raise NotImplementedError("Cannot convert tket Op to pyquil gate: " + op.get_name()) from error params = [] for par in op.get_params(): try: params.append(par.evalf()*PI) except: params.append(par*PI) g = Gate(gatetype, params, [Qubit(q) for q in command.qubits]) p += g return p
def test_exponentiate_identity(): q = QubitPlaceholder.register(11) mapping = {qp: Qubit(i) for i, qp in enumerate(q)} generator = PauliTerm("I", q[1], 0.0) para_prog = exponential_map(generator) prog = para_prog(1) result_prog = Program().inst() assert address_qubits(prog, mapping) == address_qubits(result_prog, mapping) generator = PauliTerm("I", q[1], 1.0) para_prog = exponential_map(generator) prog = para_prog(1) result_prog = Program().inst( [X(q[0]), PHASE(-1.0, q[0]), X(q[0]), PHASE(-1.0, q[0])]) assert address_qubits(prog, mapping) == address_qubits(result_prog, mapping) generator = PauliTerm("I", q[10], 0.08) para_prog = exponential_map(generator) prog = para_prog(1) result_prog = Program().inst( [X(q[0]), PHASE(-0.08, q[0]), X(q[0]), PHASE(-0.08, q[0])]) assert address_qubits(prog, mapping) == address_qubits(result_prog, mapping)
def DELAY(*args) -> Union[DelayFrames, DelayQubits]: """ Produce a DELAY instruction. Note: There are two variants of DELAY. One applies to specific frames on some qubit, e.g. `DELAY 0 "rf" "ff" 1.0` delays the `"rf"` and `"ff"` frames on 0. It is also possible to delay all frames on some qubits, e.g. `DELAY 0 1 2 1.0`. :param args: A list of delay targets, ending with a duration. :returns: A DelayFrames or DelayQubits instance. """ if len(args) < 2: raise ValueError( "Expected DELAY(t1,...,tn, duration). In particular, there " "must be at least one target, as well as a duration." ) targets, duration = args[:-1], args[-1] if not isinstance(duration, (Expression, Real)): raise TypeError("The last argument of DELAY must be a real or parametric duration.") if all(isinstance(t, Frame) for t in targets): return DelayFrames(targets, duration) elif all(isinstance(t, (int, Qubit, FormalArgument)) for t in targets): targets = [Qubit(t) if isinstance(t, int) else t for t in targets] return DelayQubits(targets, duration) else: raise TypeError( "DELAY targets must be either (i) a list of frames, or " "(ii) a list of qubits / formal arguments. " f"Got {args}." )
def test_rewrite_arithmetic_mixed_mutations(): fdefn = DefFrame( frame=Frame([Qubit(0)], "rf"), center_frequency=10.0, sample_rate=20.0, ) prog = Program( fdefn, "DECLARE theta REAL", 'SET-FREQUENCY 0 "rf" theta', 'SET-PHASE 0 "rf" theta', 'SET-SCALE 0 "rf" theta', ) response = rewrite_arithmetic(prog) assert response == RewriteArithmeticResponse( original_memory_descriptors={ "theta": ParameterSpec(length=1, type="REAL") }, recalculation_table={ ParameterAref(index=0, name="__P1"): "(theta[0] - 10.0)/20.0", ParameterAref(index=1, name="__P1"): "theta[0]/(2*pi)", ParameterAref(index=2, name="__P1"): "theta[0]/8", }, quil=Program( fdefn, "DECLARE __P1 REAL[3]", "DECLARE theta REAL[1]", 'SET-FREQUENCY 0 "rf" __P1[0]', 'SET-PHASE 0 "rf" __P1[1]', 'SET-SCALE 0 "rf" __P1[2]', ).out(), )
def FENCE(*qubits: Union[int, Qubit, FormalArgument]) -> Union[FenceAll, Fence]: """ Produce a FENCE instruction. Note: If no qubits are specified, then this is interpreted as a global FENCE. :params qubits: A list of qubits or formal arguments. :returns: A Fence or FenceAll instance. """ if qubits: return Fence([Qubit(t) if isinstance(t, int) else t for t in qubits]) else: return FenceAll()
def RESET(qubit_index=None): """ Reset all qubits or just a specific qubit at qubit_index. :param Optional[int] qubit_index: The address of the qubit to reset. If None, reset all qubits. :returns: A Reset or ResetQubit Quil AST expression corresponding to a global or targeted reset, respectively. :rtype: Union[Reset, ResetQubit] """ if qubit_index is not None: return ResetQubit(Qubit(qubit_index)) else: return Reset()
def tk_to_pyquil(tkcirc: Union[Circuit, PhysicalCircuit], active_reset: bool = False) -> Program: """ Convert a :math:`\\mathrm{t|ket}\\rangle` :py:class:`Circuit` to a :py:class:`pyquil.Program` . :param tkcirc: A circuit to be converted :return: The converted circuit """ circ = tkcirc if isinstance(tkcirc, PhysicalCircuit): circ = tkcirc._get_circuit() p = Program() if len(circ.q_regs) != 1: raise NotImplementedError( "Cannot convert circuit with multiple quantum registers to PyQuil") cregmap = {} for _, reg in circ.c_regs.items(): if reg.size() == 0: continue name = reg.name if name == 'c': name = 'ro' quil_reg = p.declare(name, 'BIT', reg.size()) cregmap.update({reg: quil_reg}) if active_reset: p.reset() for command in circ: op = command.op qubits = [Qubit(qb.index) for qb in command.qubits] optype = op.get_type() if optype == OpType.Measure: bits = [cregmap[b.reg][b.index] for b in command.bits] p += Measurement(*qubits, *bits) continue try: gatetype = _known_quil_gate_rev[optype] except KeyError as error: raise NotImplementedError( "Cannot convert tket Op to pyquil gate: " + op.get_name()) from error if len(command.controls) != 0: raise NotImplementedError( "Cannot convert conditional gates from tket to PyQuil") params = [float((p * pi).evalf()) for p in op.get_params()] g = Gate(gatetype, params, qubits) p += g return p
def get_default_qubit_mapping( program: Program) -> Dict[Union[Qubit, QubitPlaceholder], Qubit]: """ Takes a program which contains qubit placeholders and provides a mapping to the integers 0 through N-1. The output of this function is suitable for input to :py:func:`address_qubits`. :param program: A program containing qubit placeholders :return: A dictionary mapping qubit placeholder to an addressed qubit from 0 through N-1. """ fake_qubits, real_qubits, qubits = _what_type_of_qubit_does_it_use(program) if real_qubits: warnings.warn( "This program contains integer qubits, so getting a mapping doesn't make sense." ) # _what_type_of_qubit_does_it_use ensures that if real_qubits is True, then qubits contains # only real Qubits, not QubitPlaceholders. Help mypy figure this out with cast. return {q: cast(Qubit, q) for q in qubits} return {qp: Qubit(i) for i, qp in enumerate(qubits)}
def tk_to_pyquil( tkcirc: Circuit, active_reset: bool = False, return_used_bits: bool = False ) -> Union[Program, Tuple[Program, List[Bit]]]: """ Convert a tket :py:class:`Circuit` to a :py:class:`pyquil.Program` . :param tkcirc: A circuit to be converted :return: The converted circuit """ p = Program() qregs = set() for qb in tkcirc.qubits: if len(qb.index) != 1: raise NotImplementedError( "PyQuil registers must use a single index") qregs.add(qb.reg_name) if len(qregs) > 1: raise NotImplementedError( "Cannot convert circuit with multiple quantum registers to pyQuil") creg_sizes: Dict = {} for b in tkcirc.bits: if len(b.index) != 1: raise NotImplementedError( "PyQuil registers must use a single index") if (b.reg_name not in creg_sizes) or (b.index[0] >= creg_sizes[b.reg_name]): creg_sizes.update({b.reg_name: b.index[0] + 1}) cregmap = {} for reg_name, size in creg_sizes.items(): name = reg_name if name == "c": name = "ro" quil_reg = p.declare(name, "BIT", size) cregmap.update({reg_name: quil_reg}) for sym in tkcirc.free_symbols(): p.declare(str(sym), "REAL") if active_reset: p.reset() measures = [] measured_qubits: List[Qubit] = [] used_bits: List[Bit] = [] for command in tkcirc: op = command.op optype = op.type if optype == OpType.Measure: qb = Qubit_(command.args[0].index[0]) if qb in measured_qubits: raise NotImplementedError("Cannot apply gate on qubit " + qb.__repr__() + " after measurement") bit = command.args[1] b = cregmap[bit.reg_name][bit.index[0]] measures.append(Measurement(qb, b)) measured_qubits.append(qb) used_bits.append(bit) continue elif optype == OpType.Barrier: continue # pyQuil cannot handle barriers qubits = [Qubit_(qb.index[0]) for qb in command.args] for qb in qubits: if qb in measured_qubits: raise NotImplementedError("Cannot apply gate on qubit " + qb.__repr__() + " after measurement") try: gatetype = _known_quil_gate_rev[optype] except KeyError as error: raise NotImplementedError( "Cannot convert tket Op to pyQuil gate: " + op.get_name()) from error params = [param_to_pyquil(p) for p in op.params] g = Gate(gatetype, params, qubits) p += g for m in measures: p += m if return_used_bits: return p, used_bits return p
def address_qubits( program: Program, qubit_mapping: Optional[Dict[QubitPlaceholder, Union[Qubit, int]]] = None ) -> Program: """ 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: List[AbstractInstruction] = [] 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: List[Union[Qubit, int, str]] = [] 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