def test_indexing(): program = (Program(Declare("ro", "BIT"), H(0), Y(1), CNOT(0, 1)).measure( 0, MemoryReference("ro", 0)).if_then(MemoryReference("ro", 0), Program(X(0)), Program())) assert program[1] == H(0) for ii, instr in enumerate(program.instructions): assert program[ii] == instr
def test_measurement_calls(): p = Program() p.inst(Declare('ro', 'BIT', 2), MEASURE(0, MemoryReference('ro', 1)), MEASURE(0, MemoryReference('ro', 1))) assert p.out() == ('DECLARE ro BIT[2]\n' 'MEASURE 0 ro[1]\n' 'MEASURE 0 ro[1]\n')
def estimate_assignment_probs( q: int, trials: int, cxn: Union["QVMConnection", "QPUConnection"], p0: Optional["Program"] = None, ) -> np.ndarray: """ Estimate the readout assignment probabilities for a given qubit ``q``. The returned matrix is of the form:: [[p00 p01] [p10 p11]] :param q: The index of the qubit. :param trials: The number of samples for each state preparation. :param cxn: The quantum abstract machine to sample from. :param p0: A header program to prepend to the state preparation programs. :return: The assignment probability matrix """ from pyquil.quil import Program if p0 is None: # pragma no coverage p0 = Program() results_i = np.sum( cxn.run(p0 + Program(I(q), MEASURE(q, MemoryReference("ro", 0))), [0], trials)) results_x = np.sum( cxn.run(p0 + Program(X(q), MEASURE(q, MemoryReference("ro", 0))), [0], trials)) p00 = 1.0 - results_i / float(trials) p11 = results_x / float(trials) return np.array([[p00, 1 - p11], [1 - p00, p11]])
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 test_warn_on_pragma_with_trailing_measures(): "Check that to_latex warns when measurement alignment conflicts with gate group pragma." with pytest.warns(UserWarning): _ = to_latex(Program(Declare('ro', 'BIT'), Pragma("LATEX_GATE_GROUP"), MEASURE(0, MemoryReference('ro')), Pragma("END_LATEX_GATE_GROUP"), MEASURE(1, MemoryReference('ro'))))
def test_measurement_calls(): p = Program() p.inst( Declare("ro", "BIT", 2), MEASURE(0, MemoryReference("ro", 1)), MEASURE(0, MemoryReference("ro", 1)), ) assert p.out() == ("DECLARE ro BIT[2]\nMEASURE 0 ro[1]\nMEASURE 0 ro[1]\n")
def test_unary_classicals(): p = Program() p.inst( MOVE(MemoryReference("ro", 0), 1), MOVE(MemoryReference("ro", 1), 0), NOT(MemoryReference("ro", 2)), NEG(MemoryReference("ro", 3)), ) assert p.out() == "MOVE ro[0] 1\nMOVE ro[1] 0\nNOT ro[2]\nNEG ro[3]\n"
def _addr(classical): # type: (QuilParser.AddrContext) -> MemoryReference if classical.IDENTIFIER() is not None: if classical.INT() is not None: return MemoryReference(str(classical.IDENTIFIER()), int(classical.INT().getText())) else: return MemoryReference(str(classical.IDENTIFIER()), 0) else: return Addr(int(classical.INT().getText()))
def test_split_measures(): """Check that we can split terminal measurements.""" prog = Program(Declare('ro', 'BIT'), X(0), MEASURE(0, MemoryReference('ro')), X(1), MEASURE(1, MemoryReference('ro'))) meas, instr = split_on_terminal_measures(prog) assert len(meas) == 2 assert len(instr) == 3 assert all(isinstance(instr, Measurement) for instr in meas)
def test_unary_classicals(): p = Program() p.inst(TRUE(MemoryReference("ro", 0)), FALSE(MemoryReference("ro", 1)), NOT(MemoryReference("ro", 2)), NEG(MemoryReference("ro", 3))) assert p.out() == 'MOVE ro[0] 1\n' \ 'MOVE ro[1] 0\n' \ 'NOT ro[2]\n' \ 'NEG ro[3]\n'
def test_parsing_raw_capture(): parse_equals( "DECLARE iqs REAL[200000]\n" 'RAW-CAPTURE 0 "ro_rx" 0.001 iqs', Declare("iqs", "REAL", 200000), RawCapture(Frame([Qubit(0)], "ro_rx"), 0.001, MemoryReference("iqs")), ) parse_equals( "DECLARE iqs REAL[200000]\n" 'NONBLOCKING RAW-CAPTURE 0 "ro_rx" 0.001 iqs', Declare("iqs", "REAL", 200000), RawCapture(Frame([Qubit(0)], "ro_rx"), 0.001, MemoryReference("iqs"), nonblocking=True), )
def parse_mref(val: str) -> MemoryReference: """ Parse a memory reference from its string representation. """ val = val.strip() try: if val[-1] == "]": name, offset = val.split("[") return MemoryReference(name, int(offset[:-1])) else: return MemoryReference(val) except Exception: raise ValueError(f"Unable to parse memory reference {val}.")
def test_memory_commands(): parse_equals("DECLARE mem OCTET[32] SHARING mem2 OFFSET 16 REAL OFFSET 32 REAL", Declare("mem", "OCTET", 32, shared_region="mem2", offsets=[(16, "REAL"), (32, "REAL")])) parse_equals("STORE mem ro[2] ro[0]", STORE("mem", MemoryReference("ro", 2), MemoryReference("ro", 0))) parse_equals("LOAD ro[8] mem mem[4]", LOAD(MemoryReference("ro", 8), "mem", MemoryReference("mem", 4))) parse_equals("CONVERT ro[1] ro[2]", CONVERT(MemoryReference("ro", 1), MemoryReference("ro", 2))) parse_equals("EXCHANGE ro[0] ro[1]", EXCHANGE(MemoryReference("ro", 0), MemoryReference("ro", 1))) parse_equals("MOVE mem[2] 4", MOVE(MemoryReference("mem", 2), 4))
def test_parsing_capture(): wf = FlatWaveform(duration=1.0, iq=1.0) parse_equals( "DECLARE iq REAL[2]\n" 'CAPTURE 0 "ro_rx" flat(duration: 1.0, iq: 1.0) iq', Declare("iq", "REAL", 2), Capture(Frame([Qubit(0)], "ro_rx"), wf, MemoryReference("iq")), ) parse_equals( "DECLARE iq REAL[2]\n" 'NONBLOCKING CAPTURE 0 "ro_rx" flat(duration: 1.0, iq: 1.0) iq', Declare("iq", "REAL", 2), Capture(Frame([Qubit(0)], "ro_rx"), wf, MemoryReference("iq"), nonblocking=True), )
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 test_halt(): prog = Program(Declare("ro", "BIT"), X(0), MEASURE(0, MemoryReference("ro", 0))) prog.inst(HALT) prog.inst(X(0), MEASURE(0, MemoryReference("ro", 0))) qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(prog) # HALT should stop execution; measure should give 1 assert qam.ram["ro"][0] == 1 prog = Program(Declare("ro", "BIT"), X(0)).inst(X(0)).inst(MEASURE(0, MemoryReference("ro", 0))) qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(prog) assert qam.ram["ro"][0] == 0
def test_if_option(): p = (Program(Declare("ro", "BIT", 1), X(0)).measure( 0, MemoryReference("ro", 0)).if_then(MemoryReference("ro", 0), Program(X(1)))) assert p.out() == ("DECLARE ro BIT[1]\n" "X 0\n" "MEASURE 0 ro[0]\n" "JUMP-WHEN @THEN1 ro[0]\n" "JUMP @END2\n" "LABEL @THEN1\n" "X 1\n" "LABEL @END2\n") assert isinstance(p.instructions[3], JumpWhen)
def test_construction_syntax(): p = (Program().inst(Declare("ro", "BIT", 2), X(0), Y(1), Z(0)).measure(0, MemoryReference("ro", 1))) assert p.out() == ("DECLARE ro BIT[2]\nX 0\nY 1\nZ 0\nMEASURE 0 ro[1]\n") p = (Program().inst(Declare("ro", "BIT", 3), X(0)).inst(Y(1)).measure( 0, MemoryReference("ro", 1)).inst(MEASURE(1, MemoryReference("ro", 2)))) assert p.out() == ( "DECLARE ro BIT[3]\nX 0\nY 1\nMEASURE 0 ro[1]\nMEASURE 1 ro[2]\n") p = (Program().inst(Declare("ro", "BIT", 2), X(0)).measure(0, MemoryReference("ro", 1)).inst( Y(1), X(0)).measure(0, MemoryReference("ro", 0))) assert p.out() == ( "DECLARE ro BIT[2]\nX 0\nMEASURE 0 ro[1]\nY 1\nX 0\nMEASURE 0 ro[0]\n")
def declare(self, name, memory_type='BIT', memory_size=1, shared_region=None, offsets=None): """DECLARE a quil variable This adds the declaration to the current program and returns a MemoryReference to the base (offset = 0) of the declared memory. .. note:: This function returns a MemoryReference and cannot be chained like some of the other Program methods. Consider using ``inst(DECLARE(...))`` if you would like to chain methods, but please be aware that you must create your own MemoryReferences later on. :param name: Name of the declared variable :param memory_type: Type of the declared memory: 'BIT', 'REAL', 'OCTET' or 'INTEGER' :param memory_size: Number of array elements in the declared memory. :param shared_region: You can declare a variable that shares its underlying memory with another region. This allows aliasing. For example, you can interpret an array of measured bits as an integer. :param offsets: If you are using ``shared_region``, this allows you to share only a part of the parent region. The offset is given by an array type and the number of elements of that type. For example, ``DECLARE target-bit BIT SHARING real-region OFFSET 1 REAL 4 BIT`` will let you use target-bit to poke into the fourth bit of the second real from the leading edge of real-region. :return: a MemoryReference to the start of the declared memory region, ie a memory reference to ``name[0]``. """ self.inst(Declare(name=name, memory_type=memory_type, memory_size=memory_size, shared_region=shared_region, offsets=offsets)) return MemoryReference(name=name, declared_size=memory_size)
def to_pyquil(e: Expr) -> Union[float, Expression]: if isinstance(e, Number): return float(e) elif isinstance(e, Symbol): return MemoryReference(str(e)) elif isinstance(e, sin): return quil_sin(to_pyquil(e)) elif isinstance(e, cos): return quil_cos(to_pyquil(e)) elif isinstance(e, Add): args = [to_pyquil(a) for a in e.args] acc = args[0] for a in args[1:]: acc += a return acc elif isinstance(e, Mul): args = [to_pyquil(a) for a in e.args] acc = args[0] for a in args[1:]: acc *= a return acc elif isinstance(e, Pow): args = Pow_(to_pyquil(e.base), to_pyquil(e.exp)) # type: ignore elif e == pi: return math.pi else: raise NotImplementedError( "Sympy expression could not be converted to a Quil expression: " + str(e))
def augment_program_with_memory_values(quil_program, memory_map): """ This function allocates the classical memory values (gate angles) to a parametric quil program in order to use it on a Numpy-based simulator :param Program quil_program: parametric quil program which would require classical memory allocation :param Dict memory_map: dictionary with as keys the MemoryReference or String descrbing the classical memory, and with items() an array of values for that classical memory :return: quil program with gate angles from memory_map allocated to the (originally parametric) program :rtype: Program """ p = Program() # this function allocates the memory values for a parametric program correctly... if len(memory_map.keys()) == 0: return quil_program elif isinstance(list(memory_map.keys())[0], MemoryReference): for k, v in memory_map.items(): p += MOVE(k, v) elif isinstance(list(memory_map.keys())[0], str): for name, arr in memory_map.items(): for index, value in enumerate(arr): p += MOVE(MemoryReference(name, offset=index), value) else: raise TypeError( "Bad memory_map type; expected Dict[str, List[Union[int, float]]]." ) p += quil_program return percolate_declares(p)
def test_control_flows_2(): # create a program that branches based on the value of a classical register x_prog = Program(X(0)) z_prog = Program() branch = Program(H(1)).measure(1, MemoryReference("ro", 1)) \ .if_then(MemoryReference("ro", 1), x_prog, z_prog) \ .measure(0, MemoryReference("ro", 0)) assert branch.out() == ('DECLARE ro BIT[2]\n' 'H 1\n' 'MEASURE 1 ro[1]\n' 'JUMP-WHEN @THEN1 ro[1]\n' 'JUMP @END2\n' 'LABEL @THEN1\n' 'X 0\n' 'LABEL @END2\n' 'MEASURE 0 ro[0]\n')
def test_if_then_inherits_defined_gates(): p1 = Program() p1.inst(H(0)) p1.measure(0, MemoryReference("ro", 0)) p2 = Program() p2.defgate("A", np.array([[1.0, 0.0], [0.0, 1.0]])) p2.inst(("A", 0)) p3 = Program() p3.defgate("B", np.array([[0.0, 1.0], [1.0, 0.0]])) p3.inst(("B", 0)) p1.if_then(MemoryReference("ro", 0), p2, p3) assert p2.defined_gates[0] in p1.defined_gates assert p3.defined_gates[0] in p1.defined_gates
def test_dagger(): p = Program(X(0), H(0)) assert p.dagger().out() == "DAGGER H 0\nDAGGER X 0\n" p = Program(X(0), MEASURE(0, MemoryReference("ro", 0))) with pytest.raises(ValueError): p.dagger().out() # ensure that modifiers are preserved https://github.com/rigetti/pyquil/pull/914 p = Program() control = 0 target = 1 cnot_control = 2 p += X(target).controlled(control) p += Y(target).controlled(control) p += Z(target).controlled(control) p += H(target).controlled(control) p += S(target).controlled(control) p += T(target).controlled(control) p += PHASE(pi, target).controlled(control) p += CNOT(cnot_control, target).controlled(control) for instr, instr_dagger in zip(reversed(p._instructions), p.dagger()._instructions): assert "DAGGER " + instr.out() == instr_dagger.out()
def augment_program_with_memory_values( quil_program: Program, memory_map: Union[Dict[str, List[Union[int, float]]], Dict[MemoryReference, Any]], ) -> Program: p = Program() # we stupidly allowed memory_map to be of type Dict[MemoryReference, Any], whereas qc.run # takes a memory initialization argument of type Dict[str, List[Union[int, float]]. until # we are in a position to remove this, we support both styles of input. if len(memory_map.keys()) == 0: return quil_program elif isinstance(list(memory_map.keys())[0], MemoryReference): warn( "Use of memory_map values of type Dict[MemoryReference, Any] have been " "deprecated. Please use Dict[str, List[Union[int, float]]], as with " "QuantumComputer.run .") for k, v in memory_map.items(): p += MOVE(k, v) elif isinstance(list(memory_map.keys())[0], str): for name, arr in memory_map.items(): for index, value in enumerate(arr): p += MOVE(MemoryReference(cast(str, name), offset=index), value) else: raise TypeError( "Bad memory_map type; expected Dict[str, List[Union[int, float]]]." ) p += quil_program return percolate_declares(p)
def test_control_flows_2(): # create a program that branches based on the value of a classical register x_prog = Program(X(0)) z_prog = Program() branch = (Program(Declare("ro", "BIT", 2), H(1)).measure(1, MemoryReference("ro", 1)).if_then( MemoryReference("ro", 1), x_prog, z_prog).measure(0, MemoryReference("ro", 0))) assert branch.out() == ("DECLARE ro BIT[2]\n" "H 1\n" "MEASURE 1 ro[1]\n" "JUMP-WHEN @THEN1 ro[1]\n" "JUMP @END2\n" "LABEL @THEN1\n" "X 0\n" "LABEL @END2\n" "MEASURE 0 ro[0]\n")
def augment_program_with_memory_values(self, quil_program: Program) -> Program: p = Program() for k, v in self._variables_shim.items(): p += MOVE(MemoryReference(name=k.name, offset=k.index), v) p += quil_program return percolate_declares(p)
def test_is_protoquil(): prog = Program(Declare('ro', 'BIT'), MEASURE(1, MemoryReference("ro", 0)), H(1), RESET()) validate_protoquil(prog) assert prog.is_protoquil() prog = Program(Declare('ro', 'BIT'), H(0), Y(1), CNOT(0, 1)) \ .measure(0, MemoryReference("ro", 0)) \ .if_then(MemoryReference("ro", 0), Program(X(0)), Program()) with pytest.raises(ValueError): validate_protoquil(prog) assert not prog.is_protoquil() prog = Program(Declare('ro', 'BIT'), ClassicalNot(MemoryReference("ro", 0))) with pytest.raises(ValueError): validate_protoquil(prog) assert not prog.is_protoquil()
def test_classical_regs_implicit_ro(): p = Program() p.inst(Declare("reg", "BIT", 2), X(0)).measure(0, MemoryReference("reg", 1)) assert p.out() == "DECLARE reg BIT[2]\nX 0\nMEASURE 0 reg[1]\n" assert p.declarations == { "ro": Declare("ro", "BIT", 1), "reg": Declare("reg", "BIT", 2), }
def test_get_qubits(): pq = Program(Declare('ro', 'BIT'), X(0), CNOT(0, 4), MEASURE(5, MemoryReference("ro", 0))) assert pq.get_qubits() == {0, 4, 5} q = [QubitPlaceholder() for _ in range(6)] pq = Program(Declare('ro', 'BIT'), X(q[0]), CNOT(q[0], q[4]), MEASURE(q[5], MemoryReference("ro", 0))) qq = QubitPlaceholder() pq.inst(Y(q[2]), X(qq)) assert address_qubits(pq).get_qubits() == {0, 1, 2, 3, 4} qubit_index = 1 p = Program(("H", qubit_index)) assert p.get_qubits() == {qubit_index} q1 = QubitPlaceholder() q2 = QubitPlaceholder() p.inst(("CNOT", q1, q2)) with pytest.raises(ValueError) as e: _ = address_qubits(p).get_qubits() assert e.match('Your program mixes instantiated qubits with placeholders')