def CPHASE(angle, control, target): """Produces a controlled-phase instruction:: CPHASE(phi) = diag([1, 1, 1, exp(1j * phi)]) This gate applies to two qubit arguments to produce the variant of the controlled phase instruction that affects the state 11. Compare with the :py:func:`CPHASExx` variants. This variant is the most common and does not have a suffix, although you can think of it as ``CPHASE11`. :param angle: The input phase angle to apply when both qubits are in the ``|1>`` state. :param control: Qubit 1. :param target: Qubit 2. :returns: A Gate object. """ qubits = [unpack_qubit(q) for q in (control, target)] return Gate(name="CPHASE", params=[angle], qubits=qubits)
def gate(self, name, params, qubits): """ Add a gate to the program. .. note:: The matrix elements along each axis are ordered by bitstring. For two qubits the order is ``00, 01, 10, 11``, where the the bits **are ordered in reverse** by the qubit index, i.e., for qubits 0 and 1 the bitstring ``01`` indicates that qubit 0 is in the state 1. See also :ref:`the related documentation section in the QVM Overview <basis-ordering>`. :param string name: The name of the gate. :param list params: Parameters to send to the gate. :param list qubits: Qubits that the gate operates on. :return: The Program instance :rtype: Program """ return self.inst(Gate(name, params, [unpack_qubit(q) for q in qubits]))
def CNOT(control, target): """Produces a controlled-NOT (controlled-X) gate:: CNOT = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]] This gate applies to two qubit arguments to produce the controlled-not gate instruction. :param control: The control qubit. :param target: The target qubit. The target qubit has an X-gate applied to it if the control qubit is in the ``|1>`` state. :returns: A Gate object. """ return Gate(name="CNOT", params=[], qubits=[unpack_qubit(q) for q in (control, target)])
def ISWAP(q1, q2): """Produces an ISWAP instruction. ISWAP = [[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]] This gate swaps the state of two qubits, applying a -i phase to q1 when it is in the excited state and a -i phase to q2 when it is in the ground state. :param q1: Qubit 1. :param q2: Qubit 2. :returns: A Gate object. """ return Gate(name="ISWAP", params=[], qubits=[unpack_qubit(q) for q in (q1, q2)])
def PSWAP(angle, q1, q2): """Produces a parameterized SWAP gate:: PSWAP(phi) = [[1, 0, 0, 0], [0, 0, exp(1j * phi), 0], [0, exp(1j * phi), 0, 0], [0, 0, 0, 1]] :param angle: The angle of the phase to apply to the swapped states. This phase is applied to q1 when it is in the 1 state and to q2 when it is in the 0 state. :param q1: Qubit 1. :param q2: Qubit 2. :returns: A Gate object. """ return Gate(name="PSWAP", params=[angle], qubits=[unpack_qubit(q) for q in (q1, q2)])
def ISWAP(q1: QubitDesignator, q2: QubitDesignator) -> Gate: """Produces an ISWAP gate:: ISWAP = [[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]] This gate swaps the state of two qubits, applying a -i phase to q1 when it is in the 1 state and a -i phase to q2 when it is in the 0 state. :param q1: Qubit 1. :param q2: Qubit 2. :returns: A Gate object. """ return Gate(name="ISWAP", params=[], qubits=[unpack_qubit(q) for q in (q1, q2)])
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): if gate.name in QUANTUM_GATES: if gate.name == "S": daggered.inst(QUANTUM_GATES["PHASE"](-pi / 2, *gate.qubits)) elif gate.name == "T": daggered.inst(QUANTUM_GATES["RZ"](pi / 4, *gate.qubits)) elif gate.name == "ISWAP": daggered.inst(QUANTUM_GATES["PSWAP"](pi / 2, *gate.qubits)) else: negated_params = list(map(lambda x: -1 * x, gate.params)) daggered.inst(QUANTUM_GATES[gate.name](*(negated_params + gate.qubits))) 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] daggered.inst(Gate(gate_inv_name, gate.params, gate.qubits)) return daggered
def CZ(control, target): """Produces a CZ instruction. CZ = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]] This gate applies to two qubit arguments to produce the controlled-Z gate instruction. :param control: The control qubit. :param target: The target qubit. The target qubit has an Z-gate applied to it if the control qubit is in the excited state. :returns: A Gate object. """ return Gate(name="CZ", params=[], qubits=[unpack_qubit(q) for q in (control, target)])
def CSWAP(control, target_1, target_2): """Produces a controlled-SWAP gate. This gate conditionally swaps the state of two qubits:: CSWAP = [[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1]] :param control: The control qubit. :param target-1: The first target qubit. :param target-2: The second target qubit. The two target states are swapped if the control is in the ``|1>`` state. """ qubits = [unpack_qubit(q) for q in (control, target_1, target_2)] return Gate(name="CSWAP", params=[], qubits=qubits)
def CSWAP(control, target_1, target_2): """ CSWAP = [[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1]] Produces a CSWAP instruction. This gate swaps the state of two qubits. :param control: The control qubit. :param target-1: The first target qubit. :param target-2: The second target qubit. The two target states are swapped if the control is in the excited state. """ qubits = [unpack_qubit(q) for q in (control, target_1, target_2)] return Gate(name="CSWAP", params=[], qubits=qubits)
def apply_noise_model(prog: "Program", noise_model: NoiseModel) -> "Program": """ Apply a noise model to a program and generated a 'noisy-fied' version of the program. :param prog: A Quil Program object. :param noise_model: A NoiseModel, either generated from an ISA or from a simple decoherence model. :return: A new program translated to a noisy gateset and with noisy readout as described by the noisemodel. """ new_prog = _noise_model_program_header(noise_model) for i in prog: if isinstance(i, Gate) and noise_model.gates: try: _, new_name = get_noisy_gate(i.name, tuple(i.params)) new_prog += Gate(new_name, [], i.qubits) except NoisyGateUndefined: new_prog += i else: new_prog += i return new_prog
def _apply_noise_model(prog, noise_model): """ Apply a noise model to a program and generated a 'noisy-fied' version of the program. :param Program prog: A Quil Program object. :param NoiseModel noise_model: A NoiseModel, either generated from an ISA or from a simple decoherence model. :return: A new program translated to a noisy gateset and with noisy readout as described by the noisemodel. :rtype: Program """ gates = _get_program_gates(prog) noisy_names = _get_noisy_names(gates) new_prog = _noise_model_program_header(noise_model, lambda g: noisy_names.get(g, g)) for i in prog: if isinstance(i, Gate): new_prog += Gate(noisy_names[i], i.params, i.qubits) else: new_prog += i return new_prog
def gate( self, name: str, params: Iterable[ParameterDesignator], qubits: Iterable[Union[Qubit, QubitPlaceholder]], ) -> "Program": """ Add a gate to the program. .. note:: The matrix elements along each axis are ordered by bitstring. For two qubits the order is ``00, 01, 10, 11``, where the the bits **are ordered in reverse** by the qubit index, i.e., for qubits 0 and 1 the bitstring ``01`` indicates that qubit 0 is in the state 1. See also :ref:`the related docs in the WavefunctionSimulator Overview <basis_ordering>`. :param name: The name of the gate. :param params: Parameters to send to the gate. :param qubits: Qubits that the gate operates on. :return: The Program instance """ return self.inst(Gate(name, params, [unpack_qubit(q) for q in qubits]))
def gates_in_isa(isa): """ Generate the full gateset associated with an ISA. :param ISA isa: The instruction set architecture for a QPU. :return: A sequence of Gate objects encapsulating all gates compatible with the ISA. :rtype: Sequence[Gate] """ gates = [] for q in isa.qubits: if q.dead: # TODO: dead qubits may in the future lead to some implicit re-indexing continue if q.type in ["Xhalves"]: gates.extend([ Gate("I", [], [unpack_qubit(q.id)]), Gate("RX", [np.pi / 2], [unpack_qubit(q.id)]), Gate("RX", [-np.pi / 2], [unpack_qubit(q.id)]), Gate("RZ", [THETA], [unpack_qubit(q.id)]), ]) else: # pragma no coverage raise ValueError("Unknown qubit type: {}".format(q.type)) for e in isa.edges: if e.dead: continue targets = [unpack_qubit(t) for t in e.targets] if e.type in ["CZ", "ISWAP"]: gates.append(Gate(e.type, [], targets)) gates.append(Gate(e.type, [], targets[::-1])) elif e.type in ["CPHASE"]: gates.append(Gate(e.type, [THETA], targets)) gates.append(Gate(e.type, [THETA], targets[::-1])) else: # pragma no coverage raise ValueError("Unknown edge type: {}".format(e.type)) return gates
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: reg = op.get_desc() if str.isnumeric(reg): reg = int(reg) else: reg = command.qubits[0] p += Measurement(Qubit(command.qubits[0]), ro[reg]) 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 apply_noise_model(prog, noise_model): """ Apply a noise model to a program and generated a 'noisy-fied' version of the program. :param Program prog: A Quil Program object. :param NoiseModel noise_model: A NoiseModel, either generated from an ISA or from a simple decoherence model. :return: A new program translated to a noisy gateset and with noisy readout as described by the noisemodel. :rtype: Program """ new_prog = _noise_model_program_header(noise_model) for i in prog: if isinstance(i, Gate): key = (i.name, tuple(i.params)) if key in NOISY_GATES: _, new_name = NOISY_GATES[key] new_prog += Gate(new_name, [], i.qubits) else: new_prog += i else: new_prog += i return new_prog
def CCNOT(control1, control2, target): """Produces a CCNOT instruction. CCNOT = [[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 1, 0]] This gate applies to three qubit arguments to produce the controlled-controlled-not gate instruction. :param control1: The first control qubit. :param control2: The second control qubit. :param target: The target qubit. The target qubit has an X-gate applied to it if both control qubits are in the excited state. :returns: A Gate object. """ qubits = [unpack_qubit(q) for q in (control1, control2, target)] return Gate(name="CCNOT", params=[], qubits=qubits)
def test_gate(): tg = Gate("TEST", qubits=[Qubit(1), Qubit(2)], params=[]) assert tg.out() == "TEST 1 2"
def rewrite_arithmetic(prog: Program) -> RewriteArithmeticResponse: """Rewrite compound arithmetic expressions. The basic motivation is that a parametric program may have gates with compound arguments which cannot be evaluated natively on the underlying control hardware. The solution provided here is to translate a program like DECLARE theta REAL DECLARE beta REAL RZ(3 * theta) 0 RZ(beta+theta) 0 into something like DECLARE theta REAL DECLARE beta REAL DECLARE __P REAL[2] RZ(__P[0]) 0 RZ(__P[1]) 0 along with a "recalculation table" mapping new memory references to their corresponding arithmetic expressions, { ParameterAref('__P', 0): "((3.0)*theta[0])", ParameterAref('__P', 1): "(beta[0]+theta[0])" } When executing the parametric program with specific values for `theta` and `beta`, the PyQuil client will patch in values for `__P` by evaluating the expressions in the recalculation table. :param prog: A program. :returns: A RewriteArithmeticResponse, containing the updated program along with its memory descriptors and a recalculation table. """ def spec(inst: Declare) -> ParameterSpec: return ParameterSpec(type=inst.memory_type, length=inst.memory_size) def aref(ref: MemoryReference) -> ParameterAref: return ParameterAref(name=ref.name, index=ref.offset) updated = prog.copy_everything_except_instructions() old_descriptors = { inst.name: spec(inst) for inst in prog if isinstance(inst, Declare) } recalculation_table = {} seen_exprs = {} # generate a unique name. it's nice to do this in a deterministic fashion # rather than globbing in a UUID suffix = len(old_descriptors) while f"__P{suffix}" in old_descriptors: suffix += 1 mref_name = f"__P{suffix}" mref_idx = 0 for inst in prog: if isinstance(inst, Gate): new_params = [] for param in inst.params: if isinstance(param, (Real, MemoryReference)): new_params.append(param) elif isinstance(param, Expression): expr = str(param) if expr in seen_exprs: new_params.append(seen_exprs[expr]) else: new_mref = MemoryReference(mref_name, mref_idx) seen_exprs[expr] = new_mref mref_idx += 1 recalculation_table[aref(new_mref)] = expr new_params.append(new_mref) else: raise ValueError( f"Unknown parameter type {type(param)} in {inst}.") updated.inst(Gate(inst.name, new_params, inst.qubits)) else: updated.inst(inst) if mref_idx > 0: updated._instructions.insert(0, Declare(mref_name, "REAL", mref_idx)) return RewriteArithmeticResponse( quil=updated.out(), original_memory_descriptors=old_descriptors, recalculation_table=recalculation_table, )
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
first = DefCalibration("X", [], [Qubit(0)], ["foo"]) second = DefCalibration("X", [], [Qubit(0)], ["bar"]) prog = Program(first, second) match = prog.match_calibrations(Gate("X", [], [Qubit(0)])) assert match == CalibrationMatch(cal=second, settings={}) @pytest.mark.parametrize( "program_input,gate,program_output", [ ( Program(""" DEFCAL RZ(%theta) q: SHIFT-PHASE q "rf" -%theta """), Gate("RZ", [np.pi], [Qubit(0)]), Program('SHIFT-PHASE 0 "rf" -pi'), ), ( Program(""" DEFCAL A(%theta) q: SHIFT-PHASE q "rf" -%theta DEFCAL RZ(%theta) q: SHIFT-PHASE q "rf" -%theta A(%theta) q """), Gate("RZ", [np.pi], [Qubit(0)]), Program('SHIFT-PHASE 0 "rf" -pi', 'SHIFT-PHASE 0 "rf" -pi'), ), (
def test_program_match_last(): first = DefCalibration("X", [], [Qubit(0)], ["foo"]) second = DefCalibration("X", [], [Qubit(0)], ["bar"]) prog = Program(first, second) match = prog.match_calibrations(Gate("X", [], [Qubit(0)])) assert match == CalibrationMatch(cal=second, settings={})
def test_program_calibrate_cyclic_error(program_text): prog = Program(program_text) with pytest.raises(CalibrationError): prog.calibrate(Gate("RZ", [np.pi], [Qubit(0)]))
def test_gate(): tg = Gate("TEST", qubits=(DirectQubit(1), DirectQubit(2)), params=[]) assert tg.out() == "TEST 1 2"
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
def rewrite_arithmetic(prog: Program) -> RewriteArithmeticResponse: """Rewrite compound arithmetic expressions. The basic motivation is that a parametric program may have gates with compound arguments which cannot be evaluated natively on the underlying control hardware. The solution provided here is to translate a program like DECLARE theta REAL DECLARE beta REAL RZ(3 * theta) 0 RZ(beta+theta) 0 into something like DECLARE theta REAL DECLARE beta REAL DECLARE __P REAL[2] RZ(__P[0]) 0 RZ(__P[1]) 0 along with a "recalculation table" mapping new memory references to their corresponding arithmetic expressions, { ParameterAref('__P', 0): "((3.0)*theta[0])", ParameterAref('__P', 1): "(beta[0]+theta[0])" } When executing the parametric program with specific values for `theta` and `beta`, the PyQuil client will patch in values for `__P` by evaluating the expressions in the recalculation table. :param prog: A program. :returns: A RewriteArithmeticResponse, containing the updated program along with its memory descriptors and a recalculation table. """ def spec(inst: Declare) -> ParameterSpec: return ParameterSpec(type=inst.memory_type, length=inst.memory_size) def aref(ref: MemoryReference) -> ParameterAref: return ParameterAref(name=ref.name, index=ref.offset) updated = prog.copy_everything_except_instructions() old_descriptors = { inst.name: spec(inst) for inst in prog if isinstance(inst, Declare) } recalculation_table: Dict[ParameterAref, str] = {} seen_exprs: Dict[str, MemoryReference] = {} # generate a unique name. it's nice to do this in a deterministic fashion # rather than globbing in a UUID suffix = len(old_descriptors) while f"__P{suffix}" in old_descriptors: suffix += 1 mref_name = f"__P{suffix}" mref_idx = 0 def expr_mref(expr: object) -> MemoryReference: """ Get a suitable MemoryReference for a given expression. """ nonlocal mref_idx expr = str(expr) if expr in seen_exprs: return seen_exprs[expr] new_mref = MemoryReference(mref_name, mref_idx) seen_exprs[expr] = new_mref mref_idx += 1 recalculation_table[aref(new_mref)] = expr return new_mref for inst in prog: if isinstance(inst, Gate): new_params: List[Union[Real, MemoryReference]] = [] for param in inst.params: if isinstance(param, Real): new_params.append(param) elif isinstance(param, Expression): # Quil gate angles are in radians, # but downstream processing expects revolutions expr = str(Div(param, 2 * np.pi)) new_params.append(expr_mref(expr)) else: raise ValueError( f"Unknown parameter type {type(param)} in {inst}.") updated.inst(Gate(inst.name, new_params, inst.qubits)) elif isinstance(inst, (SetFrequency, ShiftFrequency)): if isinstance(inst.freq, Real): updated.inst(inst) continue try: fdefn = prog.frames[inst.frame] except KeyError: raise ValueError( f"Unable to rewrite {inst} without DEFFRAME {inst.frame}.") if fdefn.sample_rate is None: raise ValueError( f"Unable to rewrite {inst} on frame with undefined SAMPLE-RATE." ) if fdefn.center_frequency: expr = Sub(inst.freq, fdefn.center_frequency) else: expr = inst.freq expr = Div(expr, fdefn.sample_rate) expr = str(expr) updated.inst(inst.__class__(inst.frame, expr_mref(expr))) elif isinstance(inst, (SetPhase, ShiftPhase)): if isinstance(inst.phase, Real): updated.inst(inst) else: # Quil phases are in radians # but downstream processing expects revolutions expr = str(Div(inst.phase, 2 * np.pi)) updated.inst(inst.__class__(inst.frame, expr_mref(expr))) elif isinstance(inst, SetScale): if isinstance(inst.scale, Real): updated.inst(inst) else: # scale is in [-4,4) # binary patching assumes periodic with period 1 # so we divide by 8... expr = str(Div(inst.scale, 8)) updated.inst(SetScale(inst.frame, expr_mref(expr))) else: updated.inst(inst) if mref_idx > 0: updated._instructions.insert(0, Declare(mref_name, "REAL", mref_idx)) return RewriteArithmeticResponse( quil=updated.out(), original_memory_descriptors=old_descriptors, recalculation_table=recalculation_table, )