def while_do(self, classical_reg, q_program): """ While a classical register at index classical_reg is 1, loop q_program Equivalent to the following construction: .. code:: WHILE [c]: instr... => LABEL @START JUMP-UNLESS @END [c] instr... JUMP @START LABEL @END :param int classical_reg: The classical register to check :param Program q_program: The Quil program to loop. :return: The Quil Program with the loop instructions added. :rtype: Program """ label_start = LabelPlaceholder("START") label_end = LabelPlaceholder("END") self.inst(JumpTarget(label_start)) self.inst(JumpUnless(target=label_end, condition=classical_reg)) self.inst(q_program) self.inst(Jump(label_start)) self.inst(JumpTarget(label_end)) return self
def instantiate_labels(instructions): """ Takes an iterable of instructions which may contain label placeholders and assigns them all defined values. :return: list of instructions with all label placeholders assigned to real labels. """ label_i = 1 result = [] label_mapping = dict() for instr in instructions: if isinstance(instr, Jump) and isinstance(instr.target, LabelPlaceholder): new_target, label_mapping, label_i = _get_label(instr.target, label_mapping, label_i) result.append(Jump(new_target)) elif isinstance(instr, JumpConditional) and isinstance(instr.target, LabelPlaceholder): new_target, label_mapping, label_i = _get_label(instr.target, label_mapping, label_i) cls = instr.__class__ # Make the correct subclass result.append(cls(new_target, instr.condition)) elif isinstance(instr, JumpTarget) and isinstance(instr.label, LabelPlaceholder): new_label, label_mapping, label_i = _get_label(instr.label, label_mapping, label_i) result.append(JumpTarget(new_label)) else: result.append(instr) return result
def test_jumps(): parse_equals("LABEL @test_1", JumpTarget(Label("test_1"))) parse_equals("JUMP @test_1", Jump(Label("test_1"))) parse_equals("JUMP-WHEN @test_1 ro[0]", JumpWhen(Label("test_1"), MemoryReference("ro", 0))) parse_equals("JUMP-UNLESS @test_1 ro[1]", JumpUnless(Label("test_1"), MemoryReference("ro", 1)))
def if_then( self, classical_reg: MemoryReferenceDesignator, if_program: "Program", else_program: Optional["Program"] = None, ) -> "Program": """ If the classical register at index classical reg is 1, run if_program, else run else_program. Equivalent to the following construction: .. code:: IF [c]: instrA... ELSE: instrB... => JUMP-WHEN @THEN [c] instrB... JUMP @END LABEL @THEN instrA... LABEL @END :param classical_reg: The classical register to check as the condition :param if_program: A Quil program to execute if classical_reg is 1 :param else_program: A Quil program to execute if classical_reg is 0. This argument is optional and defaults to an empty Program. :returns: The Quil Program with the branching instructions added. """ else_program = else_program if else_program is not None else Program() label_then = LabelPlaceholder("THEN") label_end = LabelPlaceholder("END") self.inst( JumpWhen(target=label_then, condition=unpack_classical_reg(classical_reg))) self.inst(else_program) self.inst(Jump(label_end)) self.inst(JumpTarget(label_then)) self.inst(if_program) self.inst(JumpTarget(label_end)) return self
def test_unsupported_ops(): target = Label("target") base_prog = Program(Declare("reg1", "BIT"), Declare("reg2", "BIT"), H(0), JumpTarget(target), CNOT(0, 1)) bad_ops = [WAIT, Jump(target), MOVE(MemoryReference("reg1"), MemoryReference("reg2"))] assert to_latex(base_prog) for op in bad_ops: prog = base_prog + op with pytest.raises(ValueError): _ = to_latex(prog)
def def_label(self, label): return JumpTarget(label)
def test_jumps(): parse_equals("LABEL @test_1", JumpTarget(Label("test_1"))) parse_equals("JUMP @test_1", Jump(Label("test_1"))) parse_equals("JUMP-WHEN @test_1 ro[0]", JumpWhen(Label("test_1"), Addr(0))) parse_equals("JUMP-UNLESS @test_1 ro[1]", JumpUnless(Label("test_1"), Addr(1)))
def exitDefLabel(self, ctx): # type: (QuilParser.DefLabelContext) -> None self.result.append(JumpTarget(_label(ctx.label())))
def test_jumps(): _test("LABEL @test_1", JumpTarget(Label("test_1"))) _test("JUMP @test_1", Jump(Label("test_1"))) _test("JUMP-WHEN @test_1 [0]", JumpWhen(Label("test_1"), Addr(0))) _test("JUMP-UNLESS @test_1 [1]", JumpUnless(Label("test_1"), Addr(1)))
def rewrite_program(raw_prog: Program, qecc: QECC) -> Program: if qecc.k != 1: raise UnsupportedQECCError("code must have k = 1") if raw_prog.defined_gates: raise UnsupportedProgramError("does not support DEFGATE") # Assign indices to qubit placeholders in the raw program. raw_prog = quil.address_qubits(raw_prog) new_prog = Program() logical_qubits = { index: new_logical_qubit(new_prog, qecc, "logical_qubit_{}".format(index)) for index in raw_prog.get_qubits(indices=True) } # Construct ancilla code blocks. ancilla_1 = new_logical_qubit(new_prog, qecc, "ancilla_1") ancilla_2 = new_logical_qubit(new_prog, qecc, "ancilla_2") # Classical scratch BIT registers for gates/measurements. scratch_size = max(qecc.n, qecc.measure_scratch_size) raw_scratch = new_prog.declare('scratch', 'BIT', scratch_size) scratch = MemoryChunk(raw_scratch, 0, raw_scratch.declared_size) _initialize_memory(new_prog, raw_scratch, ancilla_1.qubits + ancilla_2.qubits) # Classical scratch INTEGER registers. raw_scratch_int = new_prog.declare('scratch_int', 'INTEGER', 2) scratch_int = MemoryChunk(raw_scratch_int, 0, raw_scratch_int.declared_size) _initialize_memory(new_prog, raw_scratch_int, ancilla_1.qubits + ancilla_2.qubits) perform_error_correction = _make_error_corrector(new_prog, qecc, ancilla_1, ancilla_2) # Reset all logical qubits. for block in logical_qubits.values(): qecc.encode_zero(new_prog, block, ancilla_1, scratch) for inst in raw_prog.instructions: if isinstance(inst, Gate): gate_qubits = [ logical_qubits[index] for index in _gate_qubits(inst) ] qecc.apply_gate(new_prog, inst.name, *gate_qubits) # Perform error correction after every logical gate. perform_error_correction(logical_qubits.values()) elif isinstance(inst, Measurement): qubit = logical_qubits[_extract_qubit_index(inst.qubit)] # This should really use its own ancilla instead of sharing with the error correction, # but we need be extremely conservative with the number of qubits. for _ in qecc.measure(new_prog, qubit, 0, inst.classical_reg, ancilla_1, ancilla_2, scratch, scratch_int): # Since measurements are taken multiple times for redundancy, we need to perform # rounds of error correction during the measurement routine. perform_error_correction(logical_qubits.values()) elif isinstance(inst, ResetQubit): raise NotImplementedError( "this instruction is not in the Quil spec") elif isinstance(inst, JumpTarget): new_prog.inst(JumpTarget(_mangle_label(inst.label))) elif isinstance(inst, JumpConditional): new_prog.inst( type(inst)(_mangle_label(inst.target), inst.condition)) elif isinstance(inst, Jump): new_prog.inst(Jump(_mangle_label(inst.target))) elif isinstance(inst, Halt): new_prog.append(inst) elif isinstance(inst, Wait): raise NotImplementedError() elif isinstance(inst, Reset): for block in logical_qubits.values(): qecc.encode_zero(new_prog, block.qubits, ancilla_1, scratch) elif isinstance(inst, Declare): new_prog.inst(inst) elif isinstance(inst, Pragma): new_prog.inst(inst) elif any( isinstance(inst, ClassicalInst) for ClassicalInst in CLASSICAL_INSTRUCTIONS): new_prog.inst(inst) else: raise UnsupportedProgramError("unsupported instruction: {}", inst) return quil.address_qubits(new_prog)