예제 #1
0
    def test_separate_measures(self):
        prog = Program()
        qreg = prog.qalloc(3)
        creg = prog.calloc(3)

        prog.apply(H, qreg[0])
        prog.apply(H, qreg[1])
        prog.apply(H, qreg[2])

        expected = prog.to_circ()

        result = Prg()
        cbs = result.declare("ro", "BIT", 3)
        result += pg.H(0)
        result += pg.H(1)
        result += pg.H(2)
        result += pg.MEASURE(0, cbs[0])
        result += pg.MEASURE(1, cbs[1])
        result += pg.MEASURE(2, cbs[2])

        result, to_measure = pyquil_to_qlm(result, True)
        exp_str = print_aq(expected)
        res_str = print_aq(result)
        self.assertEqual(res_str, exp_str)
        self.assertEqual(to_measure, [0, 1, 2])
예제 #2
0
    def superdense_coding_program(self, bit0: int, bit1: int):
        raw_prog = Program()
        ro = raw_prog.declare('ro', 'BIT', 2)
        # Prepare Bell pair
        raw_prog += gates.H(0)
        raw_prog += gates.CNOT(0, 1)
        # Alice controls qubit 0 and Bob controls 1
        if bit0 == 0 and bit1 == 0:
            pass
        if bit0 == 0 and bit1 == 1:
            raw_prog += gates.X(0)
        if bit0 == 1 and bit1 == 0:
            raw_prog += gates.Z(0)
        if bit0 == 1 and bit1 == 1:
            raw_prog += gates.X(0)
            raw_prog += gates.Z(0)
        # Now Alice sends qubit 0 to Bob
        # Bob rotates from Bell basis to standard basis
        raw_prog += gates.CNOT(0, 1)
        raw_prog += gates.H(0)
        # Measure qubits into Bob's registers
        raw_prog += gates.MEASURE(0, ro[0])
        raw_prog += gates.MEASURE(1, ro[1])

        new_prog = ftqc.rewrite_program(raw_prog, self.steane_7bit)

        results = self.run_program(new_prog)
        for result in results:
            self.assertEqual(result[0], bit0)
            self.assertEqual(result[1], bit1)
예제 #3
0
    def error_correct(self, prog: Program, data: CodeBlock,
                      ancilla_1: CodeBlock, ancilla_2: CodeBlock, scratch: MemoryChunk):
        """
        Extend a Quil program to perform error correction.
        """
        if data.n != self.n:
            raise ValueError("data code word is of incorrect size")
        if ancilla_1.n != self.n:
            raise ValueError("ancilla_1 code word is of incorrect size")
        if ancilla_2.n != self.n:
            raise ValueError("ancilla_2 code word is of incorrect size")
        if len(scratch) < self.error_correct_scratch_size:
            raise ValueError("scratch buffer is too small")

        # Split up the scratch buffer.
        mem = scratch[:self.n]
        correct_scratch = scratch[self.n:]

        # See section 4.4 of "An Introduction to Quantum Error Correction and Fault-Tolerant Quantum
        # Computation" by Daniel Gottesman for correction circuit.

        # Propagate X errors from data block to ancilla block, then measure in the Z basis.
        self.encode_plus(prog, ancilla_1, ancilla_2, scratch)
        prog += apply_transversally(gates.CNOT, data.qubits, ancilla_1.qubits)
        prog += (gates.MEASURE(ancilla_1.qubits[i], mem[i]) for i in range(self.n))
        quil_classical_correct(prog, mem, data.x_errors, correct_scratch,
                               self.parity_check_c2, self._c2_syndromes)

        # Propagate Z errors from data block to ancilla block, then measure in the X basis.
        self.encode_zero(prog, ancilla_1, ancilla_2, scratch)
        prog += apply_transversally(gates.CNOT, ancilla_1.qubits, data.qubits)
        prog += apply_transversally(gates.H, ancilla_1.qubits)
        prog += (gates.MEASURE(ancilla_1.qubits[i], mem[i]) for i in range(self.n))
        quil_classical_correct(prog, mem, data.z_errors, correct_scratch,
                               self.parity_check_c1, self._c1_syndromes)
예제 #4
0
def qlm_to_pyquil(qlm_circuit, program_pragma=None):
    """ Converts a QLM circuit to a pyquil circuit

    Args:
        qlm_circuit: QLM circuit to convert
    Returns:
        Pyquil circuit
    """
    if program_pragma is not None:
        p = Program(program_pragma)
    else:
        p = Program()
    creg = p.declare("ro", "BIT", qlm_circuit.nbcbits)

    for op in qlm_circuit.ops:
        if op.type == 0:
            qubits = build_qbits(op.qbits)
            p += build_gate(qlm_circuit.gateDic, op.gate, qubits)
        elif op.type == 1:
            for qb, cb in zip(op.qbits, op.cbits):
                p += pg.MEASURE(qb, creg[cb])
    # Adding measures to unify interface
    for qb, cbit in enumerate(creg):
        p += pg.MEASURE(qb, cbit)
    return p
예제 #5
0
    def test_multiple_measurements_program(self):
        raw_prog = Program()
        ro = raw_prog.declare('ro', 'BIT', 2)
        raw_prog += gates.H(0)
        raw_prog += gates.MEASURE(0, ro[0])
        raw_prog.if_then(ro[0], gates.X(0), Program())
        raw_prog += gates.MEASURE(0, ro[1])

        new_prog = ftqc.rewrite_program(raw_prog, self.steane_7bit)

        results = self.run_program(new_prog)
        for result in results:
            self.assertEqual(result[1], 0)
예제 #6
0
    def _error_detect_z(self, prog: Program, data: CodeBlock, ancilla: CodeBlock,
                        outcome: MemoryReference, scratch: MemoryChunk, include_operators: bool):
        """
        Extend a Quil program to perform detection of Z errors on a data block. This this uses noisy
        preparation of the ancilla for measurement, meaning the procedure is not totally reliable.
        """
        if len(scratch) < (self.n + self.r_1 + 2):
            raise ValueError("scratch buffer is too small")

        # Split up the scratch buffer.
        mem = scratch[:self.n]
        scratch = scratch[self.n:]

        # Prepare a noisy ancilla. If measuring X operators as well, prepare in X eigenstate,
        # otherwise prepare in Z eigenstate.
        ancilla.reset(prog)
        if include_operators:
            prog += self.noisy_encode_plus(ancilla.qubits)
        else:
            prog += self.noisy_encode_zero(ancilla.qubits)

        # Propagate Z errors from data block to ancilla block, then measure in the X basis.
        prog += apply_transversally(gates.CNOT, ancilla.qubits, data.qubits)
        prog += apply_transversally(gates.H, ancilla.qubits)
        prog += (gates.MEASURE(ancilla.qubits[i], mem[i]) for i in range(self.n))

        # Perform classical error detection with parity check matrix and maybe X operators.
        check_matrix = self.parity_check_c1
        if include_operators:
            check_matrix = np.concatenate([check_matrix, self.x_operator_matrix()], axis=0)
        quil_classical_detect(prog, mem, data.z_errors, outcome, scratch, check_matrix)
예제 #7
0
    def test_X_fidelity(self):
        raw_prog = Program()
        ro = raw_prog.declare('ro', 'BIT', 1)
        raw_prog += gates.X(0)
        raw_prog += gates.X(0)
        raw_prog += gates.X(0)
        raw_prog += gates.MEASURE(0, ro[0])

        ft_prog = ftqc.rewrite_program(raw_prog, self.steane_7bit)

        is_correct = lambda result: result[0] == 1

        trials = 100000
        correct, elapsed = self.run_and_benchmark_program(raw_prog,
                                                          trials,
                                                          is_correct,
                                                          separate=False)
        print(correct, trials, elapsed)

        trials = 20
        correct, elapsed = self.run_and_benchmark_program(ft_prog,
                                                          trials,
                                                          is_correct,
                                                          separate=True)
        print(correct, trials, elapsed)
예제 #8
0
def _initialize_memory(prog: Program, mem: MemoryReference,
                       qubits: List[QubitPlaceholder]):
    """
    The QVM has some weird behavior where classical memory registers have to be initialized with
    a MEASURE before any values can be MOVE'd to them. So for memory regions used internally,
    initialize memory by performing superfluous measurements.
    """
    prog += (gates.MEASURE(qubits[i % len(qubits)], mem[i])
             for i in range(mem.declared_size))
    prog += (gates.MOVE(mem[i], 0) for i in range(mem.declared_size))
예제 #9
0
 def reset(self, prog: Program):
     """
     Reset the physical qubits to the |0>^{\otimes n} state and the errors to 0.
     This code block must not be entangled with any other qubits in the system.
     """
     prog += (gates.MEASURE(self.qubits[i], self.x_errors[i])
              for i in range(self.n))
     for i in range(self.n):
         prog.if_then(self.x_errors[i], gates.X(self.qubits[i]))
         prog += gates.MOVE(self.x_errors[i], 0)
         prog += gates.MOVE(self.z_errors[i], 0)
예제 #10
0
    def test_single_Z_program(self):
        raw_prog = Program()
        ro = raw_prog.declare('ro', 'BIT', 1)
        raw_prog += gates.Y(0)
        raw_prog += gates.MEASURE(0, ro[0])

        new_prog = ftqc.rewrite_program(raw_prog, self.steane_7bit)

        results = self.run_program(new_prog)
        for result in results:
            self.assertEqual(result[0], 1)
예제 #11
0
    def test_measures(self):
        # Create qlm program
        prog = Program()
        qreg = prog.qalloc(3)

        prog.apply(H, qreg[0])
        prog.apply(H, qreg[1])
        prog.apply(H, qreg[2])

        result = qlm_to_pyquil(prog.to_circ())

        # Create pyquil program
        expected = Prg()
        cbs = expected.declare("ro", "BIT", 3)
        expected += pg.H(0)
        expected += pg.H(1)
        expected += pg.H(2)
        expected += pg.MEASURE(0, cbs[0])
        expected += pg.MEASURE(1, cbs[1])
        expected += pg.MEASURE(2, cbs[2])

        self.assertEqual(str(result), str(expected))
예제 #12
0
    def test_default_gates(self):
        # Create qlm program
        prog = Program()
        qreg = prog.qalloc(3)

        for op in pygates_1qb:
            prog.apply(op, qreg[0])

        for op in pygates_2qb:
            prog.apply(op, qreg[0], qreg[1])

        prog.apply(CCNOT, qreg[0], qreg[1], qreg[2])

        qlm_circuit = prog.to_circ()
        result = qlm_to_pyquil(qlm_circuit)

        # Create pyquil program
        expected = Prg()
        expected_creg = expected.declare("ro", "BIT", 3)
        for op in quil_1qb:
            expected += op(0)
        for op in quil_params:
            expected += op(3.14, 0)

        expected += pg.SWAP(0, 1)
        expected += pg.CNOT(0, 1)
        for op in quil_ctrl:
            expected += op(1).controlled(0)
        for op in quil_ctrl_prm:
            expected += op(3.14, 1).controlled(0)
        expected += pg.CCNOT(0, 1, 2)
        expected += pg.MEASURE(0, expected_creg[0])
        expected += pg.MEASURE(1, expected_creg[1])
        expected += pg.MEASURE(2, expected_creg[2])

        self.assertEqual(str(result), str(expected))
예제 #13
0
    def measure(self, prog: Program, data: CodeBlock, index: int, outcome: MemoryReference,
                ancilla_1: CodeBlock, ancilla_2: CodeBlock,
                scratch: MemoryChunk, scratch_int: MemoryChunk):
        """
        Extend a Quil program to measure the logical qubit in the Z basis. Ancilla must be in a
        logical |0> state.

        Index is the index of the logical qubit within the code block. Currently must be 0.

        This measurement is made fault tolerant by repeating a noisy measurement 2t + 1 times and
        returning a majority vote.

        This yields control after each fault tolerant operation so that a round of error correction
        may be performed globally if required.
        """
        if index != 0:
            raise ValueError("only one logical qubit per code block")
        if data.n != self.n:
            raise ValueError("data code word is of incorrect size")
        if ancilla_1.n != self.n:
            raise ValueError("ancilla_1 code word is of incorrect size")
        if ancilla_2.n != self.n:
            raise ValueError("ancilla_2 code word is of incorrect size")
        if len(scratch) < self.measure_scratch_size:
            raise ValueError("scratch buffer is too small")
        if len(scratch_int) < 1:
            raise ValueError("scratch_int buffer is too small")

        trials = 2 * self.t + 1

        # Split up the scratch buffer.
        noisy_outcomes = scratch[:trials]
        noisy_scratch = scratch[trials:]

        for i in range(trials):
            self.noisy_measure(prog, data, index, noisy_outcomes[i], ancilla_1, ancilla_2,
                               noisy_scratch)
            yield

        outcome_bit = noisy_scratch[0]
        quil_classical.majority_vote(prog, noisy_outcomes, outcome_bit, scratch_int)

        # Because of the stupid thing where the QVM relies on MEASURE to initialize classical
        # registers, do a superfluous measure here of the already trashed ancilla.
        prog += gates.MEASURE(ancilla_1.qubits[0], outcome)

        # In case outcome is not a bit reference, do a CONVERT instead of a MOVE.
        prog += gates.MOVE(outcome, outcome_bit)
예제 #14
0
    def noisy_measure(self, prog: Program, data: CodeBlock, index: int, outcome: MemoryReference,
                      ancilla_1: CodeBlock, ancilla_2: CodeBlock, scratch: MemoryChunk):
        """
        Extend a Quil program to measure the logical qubit in the Z basis. Ancilla must be in a
        logical |0> state.

        Index is the index of the logical qubit within the code block. Currently must be 0.

        This measurement is not fault tolerant and may fail if any single operation fails.
        """
        n, r_2 = self.n, self.r_2

        if index != 0:
            raise ValueError("only one logical qubit per code block")
        if data.n != n:
            raise ValueError("data code word is of incorrect size")
        if ancilla_1.n != n:
            raise ValueError("ancilla_1 code word is of incorrect size")
        if ancilla_2.n != n:
            raise ValueError("ancilla_2 code word is of incorrect size")
        if len(scratch) < self.error_correct_scratch_size:
            raise ValueError("scratch buffer is too small")

        # Reset the ancilla to |0>.
        self.encode_zero(prog, ancilla_1, ancilla_2, scratch)

        # Split up the scratch buffer.
        mem = scratch[:n]
        scratch = scratch[n:(n + r_2 + 2)]

        # Propagate each Z in the operator from data block to ancilla block for each, then measure
        # in the Z basis.
        #
        # This implements the technique from section 3 of
        # "Efficient fault-tolerant quantum computing" by Andrew M. Steane.
        prog += apply_transversally(gates.CNOT, data.qubits, ancilla_1.qubits)
        prog += (gates.MEASURE(ancilla_1.qubits[i], mem[i]) for i in range(self.n))

        # Opportunistically correct any X errors.
        quil_classical_correct(prog, mem, data.x_errors, scratch,
                               self.parity_check_c2, self._c2_syndromes)

        # Finally, compute the measurement outcome.
        z_operator = self.z_operator_matrix()[index:(index + 1), :]
        outcome_chunk = MemoryChunk(
            MemoryReference(outcome.name), outcome.offset, outcome.offset + 1
        )
        quil_classical.matmul(prog, z_operator, mem, outcome_chunk, scratch)
예제 #15
0
    def test_recursive_ctrl_and_dagger(self):
        # Create qlm program
        prog = Program()
        qreg = prog.qalloc(5)
        prog.apply(Y.ctrl().ctrl().ctrl().ctrl().dag().dag().dag(), *qreg)
        qlm_circuit = prog.to_circ()
        result = qlm_to_pyquil(qlm_circuit)

        # Create pyquil program
        expected = Prg()
        expected_creg = expected.declare("ro", "BIT", 5)
        expected += (pg.Y(4).controlled(0).controlled(1).controlled(
            2).controlled(3).dagger())
        for qbit, cbit in enumerate(expected_creg):
            expected += pg.MEASURE(qbit, cbit)

        self.assertEqual(str(result), str(expected))
예제 #16
0
def MEASURE(qubit) -> Addr:
    program_context().inst(gates.MEASURE(qubit, qubit))
    return Addr(qubit)
예제 #17
0
 def initialize_memory(self, prog, mem):
     # Need to measure a qubit to initialize memory for some reason.
     for i in range(mem.declared_size):
         prog += gates.MEASURE(0, mem[i])
         prog += gates.MOVE(mem[i], 0)
예제 #18
0
qvm = api.QVMConnection()

# =============================================================================
# teleportation circuit
# =============================================================================

# Alice wants to send |1> to Bob
qprog += gates.X(0)

# main circuit
qprog += [
    gates.H(1),
    gates.CNOT(1, 2),
    gates.CNOT(0, 1),
    gates.H(0),
    gates.MEASURE(0, creg[0]),
    gates.MEASURE(1, creg[1])
]

# conditional operations
qprog.if_then(creg[0], gates.Z(2))
qprog.if_then(creg[1], gates.X(2))

# measure qubit three
qprog.measure(2, creg[2])

# =============================================================================
# run the circuit and print the results. Note Bob always measures 1
# =============================================================================

print(qvm.run(qprog))
예제 #19
0
"""Random bit generator circuit in PyQuil 2.1.1."""

# imports
from pyquil.quil import Program
import pyquil.gates as gates
from pyquil import api

# get a program and classical memory register
qprog = Program()
creg = qprog.declare(name="ro", memory_size=1)

# REQUIRES: api key, qvm running in background ("qvm -S" in a linux terminal
# after it is installed. See Rigetti website for download instructions
# https://www.rigetti.com/forest)
qvm = api.QVMConnection()

# add instructions to the program
qprog += [gates.H(0), gates.MEASURE(0, creg[0])]

print(qprog)
print(qvm.run(qprog, trials=1))
예제 #20
0
    # Add the right rotation + measurement operators to the ansatz
    circuit = ansatz.copy()
    qubits = computer.qubits()
    to_measure = []
    for (q, p) in enumerate(squashed):
        if p in ("X", "Y", "Z"):
            to_measure.append(q)
        if p == "X":
            circuit += [gates.H(qubits[q])]
        elif p == "Y":
            circuit += [gates.S(qubits[q]), gates.H(qubits[q])]

    # Add the terminal measurements
    #  Note we do it this way since all measurements *must* be at the end of the circuit on hardware
    for q in to_measure:
        circuit += [gates.MEASURE(qubits[q], creg[q])]

    # Execute the circuit
    circuit.wrap_in_numshots_loop(shots)
    executable = computer.compile(circuit)
    res = computer.run(executable, memory_map={"theta": angles})

    # Do the postprocessing
    supports = [support(pauli) for pauli in paulis]
    total = 0.0
    for ii in range(len(supports)):
        tot = 0.0
        for vals in res:
            sliced = islice(vals, supports[ii])
            tot += (-1)**sum(sliced)
        total += coeffs[ii] * tot / shots
예제 #21
0
def expectation(angles: List[float],
                coeff: complex,
                pauli: str,
                ansatz: pyquil.Program,
                creg: pyquil.quilatom.MemoryReference,
                computer: pyquil.api.QuantumComputer,
                shots: int = 10000,
                verbose: bool = False) -> float:
    """Returns coeff * <\theta| paulii |\theta>.
    
    Args:
        angles: List of angles at which to evaluate coeff * <theta| pauli |theta>.
        coeff: Coefficient of Pauli term.
        pauli: Pauli string.
        ansatz: pyQuil program representing the ansatz state.
        creg: Classical register of ansatz to measure into.
        computer: QuantumComputer to execute the circuit on.
        shots: Number of times to execute the circuit (sampling statistics).
        verbose: Option for visualization/debugging.
    """
    if np.isclose(np.imag(coeff), 0.0):
        coeff = np.real(coeff)

    if set(pauli) == {"I"}:
        return coeff

    angles = list(angles)
    angles = deepcopy(angles)

    if verbose:
        print("DEBUG holy f**k")
        print(f"type(angles) = {type(angles)}")
        print("angles =", angles)

    # Set up the circuit
    circuit = ansatz.copy()
    qubits = computer.qubits()
    measured = []
    for (q, p) in enumerate(pauli):
        if p in ("X", "Y", "Z"):
            measured.append(qubits[q])
        if p == "X":
            circuit += [gates.H(qubits[q]), gates.MEASURE(qubits[q], creg[q])]
        elif p == "Y":
            circuit += [
                gates.S(qubits[q]),
                gates.H(qubits[q]),
                gates.MEASURE(qubits[q], creg[q])
            ]
        elif p == "Z":
            circuit += [gates.MEASURE(qubits[q], creg[q])]

    if verbose:
        print(f"Computing {coeff} x <theta|{pauli}|theta>...")
        print("\nCircuit to be executed:")
        print(circuit)
        print(f"type(angles) = {type(angles)}")

    # Execute the circuit
    circuit.wrap_in_numshots_loop(shots)
    executable = computer.compile(circuit)
    res = computer.run(executable, memory_map={"theta": angles})

    if verbose:
        print("\nResults:")
        print(f"{len(res)} total measured bit strings.")
        print(res)

    # Do the postprocessing
    tot = 0.0
    for vals in res:
        tot += (-1)**sum(vals)
    return coeff * tot / shots
예제 #22
0
def MEASURE(qubit) -> MemoryReference:
    program_context().inst(gates.MEASURE(qubit, MemoryReference("ro", qubit)))
    return MemoryReference("ro", qubit)
예제 #23
0
# =============================================================================
# circuit to test simulator
# =============================================================================

# main (arbitrary) circuit
for level in range(depth):
    for ii in range(n):
        qprog.inst(gates.H(ii),
                   gates.X(ii))
        if ii != 0:
            qprog.inst(gates.CNOT(ii, 0))
            
# measurements
for ii in range(n):
    qprog.inst(gates.MEASURE(ii, creg[ii]))

# =============================================================================
# run the circuit and print the results
# =============================================================================
    
# timing -- get the start time
start = time.time()

output = qvm.run(qprog)

# timing -- get the end time
runtime = time.time() - start

# print out the runtime
print(n, depth, runtime)