<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Installation and Getting Started
Environment Setup
Prerequisites
Installation
Connecting to a Rigetti QVM
Basic Usage
Some Program Construction Features
Fixing a Mistaken Instruction
The Standard Gate Set
Defining New Gates
Advanced Usage
Quantum Fourier Transform (QFT)
Classical Control Flow
Parametric Depolarizing Noise
Pauli Operator Algebra
Exercises
Exercise 1 - Quantum Dice
Exercise 2 - Controlled Gates
Exercise 3 - Grover's Algorithm
This toolkit provides some simple libraries for writing quantum programs using the quantum instruction language Quil. pyQuil is part of the Forest suite of tools for quantum programming and is currently in private beta.
python
import pyquil.quil as pq
import pyquil.forest as forest
from pyquil.gates import *
qvm = forest.Connection()
p = pq.Program()
p.inst(H(0), CNOT(0, 1))
    &lt;pyquil.pyquil.Program object at 0x101ebfb50&gt;
qvm.wavefunction(p)
    [(0.7071067811865475+0j), 0j, 0j, (0.7071067811865475+0j)]
It comes with a few parts
Step1:  Next, we want to open a connection to the QVM.
Step2:  Now we can make a program by adding some Quil instruction using the inst method on a Program object.
Step3:  This program simply applies the $X$-gate to the zeroth qubit, measures that qubit, and stores the measurement result in the zeroth classical register. We can look at the Quil code that makes up this program simply by printing it.
Step4:  Most importantly, of course, we can see what happens if we run this program on the QVM
Step5:  We see that the result of this program is that the classical register [0] now stores the state of qubit 0, which should be $\left\vert 1\right\rangle$ after an $X$-gate.  We can of course ask for more classical registers
Step6:  The classical registers are initialized to zero, so registers [1] and [2] come out as zero. If we stored the measurement in a different classical register we would obtain
Step7:  We can also run programs multiple times and accumulate all the results in a single list
Step8:  Try running the above code several times. You will, with very high probability, get different results each time.
As the QVM is a virtual machine, we can also inspect the wavefunction of a program directly, even without measurements
Step9:  It is important to remember that this wavefunction method is just a useful debugging tool for small quantum systems, and it cannot be feasibly obtained on a quantum processor.
Some Program Construction Features
Multiple instructions can be applied at once or chained together. The following are all valid programs
Step10:  Fixing a Mistaken Instruction
If an instruction was appended to a program incorrectly, one can pop it off.
Step11:  The Standard Gate Set
The following gates methods come standard with Quil and gates.py
Step12:  Quil in general supports defining parametric gates, though right now only static gates are supported by pyQuil. Below we show how we can define $X_1\otimes \sqrt{X_0} $ as a single gate.
Step13:  Advanced Usage
Quantum Fourier Transform (QFT) <a id='Quantum-Fourier-Transform'></a>
Let's do an example that includes multi-qubit parameterized gates.
Here we wish to compute the discrete Fourier transform of [0, 1, 0, 0, 0, 0, 0, 0]. We do this in three steps
Step14:  There is a very important detail to recognize here
Step15:  Next, we want to prepare a state that corresponds to the sequence we want to compute the discrete Fourier transform of. Fortunately, this is easy, we just apply an $X$-gate to the zeroth qubit.
Step16:  We can verify that this works by computing its wavefunction. However, we need to add some "dummy" qubits, because otherwise wavefunction would return a two-element vector.
Step17:  If we have two quantum programs a and b, we can concatenate them by doing a + b. Using this, all we need to do is compute the QFT after state preparation to get our final result.
Step18:  We can verify this works by computing the (inverse) FFT from NumPy.
Step19:  Classical Control Flow
Here are a couple quick examples that show how much richer the classical control of a Quil program can be. In this first example, we have a register called classical_flag_register which we use for looping. Then we construct the loop in the following steps
Step20:  Notice that the init_register program applied a Quil instruction directly to a classical register. There are several classical commands that can be used in this fashion
Step21:  We can run this program a few times to see what we get in the answer_register.
Step22:  Parametric Depolarizing Noise
The Rigetti QVM has support for emulating certain types of noise models. One such model is parametric depolarizing noise, which is defined by a set of 6 probabilities
Step23:  We can test this by applying an $X$ gate and measuring. Nominally, we should always measure 1.
Step24:  Parametric Programs
A big advantage of working in pyQuil is that you are able to leverage all the functionality of Python to generate Quil programs. In quantum/classical hybrid algorithms this often leads to situations where complex classical functions are used to generate Quil programs. pyQuil provides a convenient construction to allow you to use Python functions to generate templates of Quil programs, called ParametricPrograms
Step25:  The parametric program par_p now takes the same arguments as rotator
Step26:  We can think of ParametricPrograms as a sort of template for Quil programs.  They cache computations
that happen in Python functions so that templates in Quil can be efficiently substituted.
Pauli Operator Algebra
Many algorithms require manipulating sums of Pauli combinations, such as \[\sigma = \tfrac{1}{2}I - \tfrac{3}{4}X_0Y_1Z_3 + (5-2i)Z_1X_2,\] where $G_n$ indicates the gate $G$ acting on qubit $n$. We can represent such sums by constructing PauliTerm and PauliSum. The above sum can be constructed as follows
Step27:  There are two primary things one can do with Pauli terms and sums
<ASSISTANT_TASK:>
Python Code:
import pyquil.quil as pq
import pyquil.forest as forest
from pyquil.gates import *
Explanation: Installation and Getting Started
Environment Setup
Prerequisites
Installation
Connecting to a Rigetti QVM
Basic Usage
Some Program Construction Features
Fixing a Mistaken Instruction
The Standard Gate Set
Defining New Gates
Advanced Usage
Quantum Fourier Transform (QFT)
Classical Control Flow
Parametric Depolarizing Noise
Pauli Operator Algebra
Exercises
Exercise 1 - Quantum Dice
Exercise 2 - Controlled Gates
Exercise 3 - Grover's Algorithm
This toolkit provides some simple libraries for writing quantum programs using the quantum instruction language Quil. pyQuil is part of the Forest suite of tools for quantum programming and is currently in private beta.
python
import pyquil.quil as pq
import pyquil.forest as forest
from pyquil.gates import *
qvm = forest.Connection()
p = pq.Program()
p.inst(H(0), CNOT(0, 1))
    &lt;pyquil.pyquil.Program object at 0x101ebfb50&gt;
qvm.wavefunction(p)
    [(0.7071067811865475+0j), 0j, 0j, (0.7071067811865475+0j)]
It comes with a few parts:
Quil: The Quantum Instruction Language standard. Instructions written in Quil can be executed on any implementation of a quantum abstract machine, such as the quantum virtual machine (QVM), or on a real quantum processing unit (QPU). More details regarding Quil can be found in the whitepaper.
QVM: A Quantum Virtual Machine, which is an implementation of the quantum abstract machine on classical hardware.  The QVM lets you use a regular computer to simulate a small quantum computer.  You can access the Rigetti QVM running in the cloud with your API key.  Sign up here to get your key.
pyQuil: A Python library to help write and run Quil code and quantum programs.
Environment Setup
Prerequisites
Before starting, ensure that you have an installation of Python 2.7 (version 2.7.10 or greater) and the Python package manager pip. We recommend installing Anaconda for an all-in-one installation of Python 2.7. If pip is not installed, it can be installed with easy_install pip.
Installation
After obtaining pyQuil from GitHub or from a source distribution, navigate into its directory in a terminal and run
pip install pyquil
The library will now be available globally.
Connecting to a Rigetti QVM
In order to connect to a Rigetti QVM you need to configure your pyQuil installation with your QVM API key. For permanent one-time setup, you can do this by creating a file in your home directory with the following lines:
[Rigetti Forest]
url: &lt;FOREST_URL&gt;
key: &lt;YOUR_FOREST_API_KEY&gt;
Look here to see more information about setting up your connection to Forest.
If this configuration is not set, pyQuil will default to looking for a local QVM at https://api.rigetti.com/qvm.
Basic Usage
To ensure that your installation is working correctly, try running the following Python commands interactively.  First, import the quil module (which constructs quantum programs) and the forest module (which allows connections to the Rigetti QVM). We'll also import some basic gates for pyQuil.
End of explanation
qvm = forest.Connection()
Explanation: Next, we want to open a connection to the QVM.
End of explanation
p = pq.Program()
p.inst(X(0)).measure(0, 0)
Explanation: Now we can make a program by adding some Quil instruction using the inst method on a Program object.
End of explanation
print p
Explanation: This program simply applies the $X$-gate to the zeroth qubit, measures that qubit, and stores the measurement result in the zeroth classical register. We can look at the Quil code that makes up this program simply by printing it.
End of explanation
classical_regs = [0] # A list of which classical registers to return the values of.
qvm.run(p, classical_regs)
Explanation: Most importantly, of course, we can see what happens if we run this program on the QVM:
End of explanation
qvm.run(p, [0, 1, 2])
Explanation: We see that the result of this program is that the classical register [0] now stores the state of qubit 0, which should be $\left\vert 1\right\rangle$ after an $X$-gate.  We can of course ask for more classical registers:
End of explanation
p = pq.Program()   # clear the old program
p.inst(X(0)).measure(0, 1)
qvm.run(p, [0, 1, 2])
Explanation: The classical registers are initialized to zero, so registers [1] and [2] come out as zero. If we stored the measurement in a different classical register we would obtain:
End of explanation
coin_flip = pq.Program().inst(H(0)).measure(0, 0)
num_flips = 5
qvm.run(coin_flip, [0], num_flips)
Explanation: We can also run programs multiple times and accumulate all the results in a single list:
End of explanation
coin_flip = pq.Program().inst(H(0))
qvm.wavefunction(coin_flip)
Explanation: Try running the above code several times. You will, with very high probability, get different results each time.
As the QVM is a virtual machine, we can also inspect the wavefunction of a program directly, even without measurements:
End of explanation
print "Multiple inst arguments with final measurement:"
print pq.Program().inst(X(0), Y(1), Z(0)).measure(0, 1)
print "Chained inst with explicit MEASURE instruction:"
print pq.Program().inst(X(0)).inst(Y(1)).measure(0, 1).inst(MEASURE(1, 2))
print "A mix of chained inst and measures:"
print pq.Program().inst(X(0)).measure(0, 1).inst(Y(1), X(0)).measure(0, 0)
print "A composition of two programs:"
print pq.Program(X(0)) + pq.Program(Y(0))
Explanation: It is important to remember that this wavefunction method is just a useful debugging tool for small quantum systems, and it cannot be feasibly obtained on a quantum processor.
Some Program Construction Features
Multiple instructions can be applied at once or chained together. The following are all valid programs:
End of explanation
p = pq.Program().inst(X(0))
p.inst(Y(1))
print "Oops! We have added Y 1 by accident:"
print p
print "We can fix by popping:"
p.pop()
print p
print "And then add it back:"
p += pq.Program(Y(1))
print p
Explanation: Fixing a Mistaken Instruction
If an instruction was appended to a program incorrectly, one can pop it off.
End of explanation
import numpy as np
# First we define the new gate from a matrix
x_gate_matrix = np.array(([0.0, 1.0], [1.0, 0.0]))
sqrt_x = np.array([[ 0.5+0.5j,  0.5-0.5j],
                   [ 0.5-0.5j,  0.5+0.5j]])
p = pq.Program().defgate("SQRT-X", sqrt_x)
# Then we can use the new gate,
p.inst(("SQRT-X", 0))
print p
qvm.wavefunction(p)
Explanation: The Standard Gate Set
The following gates methods come standard with Quil and gates.py:
Pauli gates  I, X, Y, Z
Hadamard gate: H
Phase gates: PHASE( $\theta$ ), S, T
Controlled phase gates: CPHASE00( $\alpha$ ), CPHASE01( $\alpha$ ), CPHASE10( $\alpha$ ), CPHASE( $\alpha$ )
Cartesian rotation gates: RX( $\theta$ ), RY( $\theta$ ), RZ( $\theta$ )
Controlled $X$ gates: CNOT, CCNOT
Swap gates: SWAP, CSWAP, ISWAP, PSWAP( $\alpha$ )
The parameterized gates take a real or complex floating point number as an argument.
Defining New Gates
New gates can be easily added inline to Quil programs. All you need is a matrix representation of the gate. For example, below we define a $\sqrt{X}$ gate.
End of explanation
# A multi-qubit defgate example
x_gate_matrix = np.array(([0.0, 1.0], [1.0, 0.0]))
sqrt_x = np.array([[ 0.5+0.5j,  0.5-0.5j],
                [ 0.5-0.5j,  0.5+0.5j]])
x_sqrt_x = np.kron(x_gate_matrix, sqrt_x)
p = pq.Program().defgate("X-SQRT-X", x_sqrt_x)
# Then we can use the new gate
p.inst(("X-SQRT-X", 0, 1))
qvm.wavefunction(p)
Explanation: Quil in general supports defining parametric gates, though right now only static gates are supported by pyQuil. Below we show how we can define $X_1\otimes \sqrt{X_0} $ as a single gate.
End of explanation
from math import pi
def qft3(q0, q1, q2):
    p = pq.Program()
    p.inst( H(q2),
            CPHASE(pi/2.0, q1, q2),
            H(q1),
            CPHASE(pi/4.0, q0, q2),
            CPHASE(pi/2.0, q0, q1),
            H(q0),
            SWAP(q0, q2) )
    return p
Explanation: Advanced Usage
Quantum Fourier Transform (QFT) <a id='Quantum-Fourier-Transform'></a>
Let's do an example that includes multi-qubit parameterized gates.
Here we wish to compute the discrete Fourier transform of [0, 1, 0, 0, 0, 0, 0, 0]. We do this in three steps:
Write a function called qft3 to make a 3-qubit QFT quantum program.
Write a state preparation quantum program.
Execute state preparation followed by the QFT on the QVM.
First we define a function to make a 3-qubit QFT quantum program. This is a mix of Hadamard and CPHASE gates, with a final bit reversal correction at the end consisting of a single SWAP gate.
End of explanation
print qft3(0, 1, 2)
Explanation: There is a very important detail to recognize here: The function qft3 doesn't compute the QFT, but rather it makes a quantum program to compute the QFT on qubits q0, q1, and q2.
We can see what this program looks like in Quil notation by doing the following:
End of explanation
state_prep = pq.Program().inst(X(0))
Explanation: Next, we want to prepare a state that corresponds to the sequence we want to compute the discrete Fourier transform of. Fortunately, this is easy, we just apply an $X$-gate to the zeroth qubit.
End of explanation
add_dummy_qubits = pq.Program().inst(I(2))
qvm.wavefunction(state_prep + add_dummy_qubits)
Explanation: We can verify that this works by computing its wavefunction. However, we need to add some "dummy" qubits, because otherwise wavefunction would return a two-element vector.
End of explanation
qvm.wavefunction(state_prep + qft3(0, 1, 2))
Explanation: If we have two quantum programs a and b, we can concatenate them by doing a + b. Using this, all we need to do is compute the QFT after state preparation to get our final result.
End of explanation
from numpy.fft import ifft
ifft([0,1,0,0,0,0,0,0], norm="ortho")
Explanation: We can verify this works by computing the (inverse) FFT from NumPy.
End of explanation
# Name our classical registers:
classical_flag_register = 2
# Write out the loop initialization and body programs:
init_register = pq.Program(TRUE([classical_flag_register]))
loop_body = pq.Program(X(0), H(0)).measure(0, classical_flag_register)
# Put it all together in a loop program:
loop_prog = init_register.while_do(classical_flag_register, loop_body)
print loop_prog
Explanation: Classical Control Flow
Here are a couple quick examples that show how much richer the classical control of a Quil program can be. In this first example, we have a register called classical_flag_register which we use for looping. Then we construct the loop in the following steps:
We first initialize this register to 1 with the init_register program so our while loop will execute. This is often called the loop preamble or loop initialization.
Next, we write body of the loop in a program itself. This will be a program that computes an $X$ followed by an $H$ on our qubit.
Lastly, we put it all together using the while_do method.
End of explanation
# Name our classical registers:
test_register = 1
answer_register = 0
# Construct each branch of our if-statement. We can have empty branches
# simply by having empty programs.
then_branch = pq.Program(X(0))
else_branch = pq.Program()
# Make a program that will put a 0 or 1 in test_register with 50% probability:
branching_prog = pq.Program(H(1)).measure(1, test_register)
# Add the conditional branching:
branching_prog.if_then(test_register, then_branch, else_branch)
# Measure qubit 0 into our answer register:
branching_prog.measure(0, answer_register)
print branching_prog
Explanation: Notice that the init_register program applied a Quil instruction directly to a classical register. There are several classical commands that can be used in this fashion:
TRUE which sets a single classical bit to be 1
FALSE which sets a single classical bit to be 0
NOT which flips a classical bit
AND which operates on two classical bits
OR which operates on two classical bits
MOVE which moves the value of a classical bit at one classical address into another
EXCHANGE which swaps the value of two classical bits
In this next example, we show how to do conditional branching in the form of the traditional if construct as in many programming languages. Much like the last example, we construct programs for each branch of the if, and put it all together by using the if_then method.
End of explanation
qvm.run(branching_prog, [answer_register], 10)
Explanation: We can run this program a few times to see what we get in the answer_register.
End of explanation
# 20% chance of a X gate being applied after gate applications and before measurements.
gate_noise_probs = [0.2, 0.0, 0.0]
meas_noise_probs = [0.2, 0.0, 0.0]
noisy_qvm = forest.Connection(gate_noise=gate_noise_probs, measurement_noise=meas_noise_probs)
Explanation: Parametric Depolarizing Noise
The Rigetti QVM has support for emulating certain types of noise models. One such model is parametric depolarizing noise, which is defined by a set of 6 probabilities:
The probabilities $P_X$, $P_Y$, and $P_Z$ which define respectively the probability of a Pauli $X$, $Y$, or $Z$ gate getting applied to each qubit after every gate application. These probabilities are called the gate noise probabilities.
The probabilities $P_X'$, $P_Y'$, and $P_Z'$ which define respectively the probability of a Pauli $X$, $Y$, or $Z$ gate getting applied to the qubit being measured before it is measured. These probabilities are called the measurement noise probabilities.
We can instantiate a noisy QVM by creating a new connection with these probabilities specified.
End of explanation
p = pq.Program().inst(X(0)).measure(0, 0)
print "Without Noise:", qvm.run(p, [0], 10)
print "With Noise   :", noisy_qvm.run(p, [0], 10)
Explanation: We can test this by applying an $X$ gate and measuring. Nominally, we should always measure 1.
End of explanation
# This function returns a quantum circuit with different rotation angles on a gate on qubit 0
def rotator(angle):
    return pq.Program(RX(angle, 0))
from pyquil.parametric import ParametricProgram
par_p = ParametricProgram(rotator) # This produces a new type of parameterized program object
Explanation: Parametric Programs
A big advantage of working in pyQuil is that you are able to leverage all the functionality of Python to generate Quil programs. In quantum/classical hybrid algorithms this often leads to situations where complex classical functions are used to generate Quil programs. pyQuil provides a convenient construction to allow you to use Python functions to generate templates of Quil programs, called ParametricPrograms:
End of explanation
print par_p(0.5)
Explanation: The parametric program par_p now takes the same arguments as rotator:
End of explanation
from pyquil.paulis import ID, sX, sY, sZ
# Pauli term takes an operator "X", "Y", "Z", or "I"; a qubit to act on, and
# an optional coefficient.
a = 0.5 * ID
b = -0.75 * sX(0) * sY(1) * sZ(3)
c = (5-2j) * sZ(1) * sX(2)
# Construct a sum of Pauli terms.
sigma = a + b + c
print "sigma =", sigma
Explanation: We can think of ParametricPrograms as a sort of template for Quil programs.  They cache computations
that happen in Python functions so that templates in Quil can be efficiently substituted.
Pauli Operator Algebra
Many algorithms require manipulating sums of Pauli combinations, such as \[\sigma = \tfrac{1}{2}I - \tfrac{3}{4}X_0Y_1Z_3 + (5-2i)Z_1X_2,\] where $G_n$ indicates the gate $G$ acting on qubit $n$. We can represent such sums by constructing PauliTerm and PauliSum. The above sum can be constructed as follows:
End of explanation
import pyquil.paulis as pl
# Simplification
sigma_cubed = sigma * sigma * sigma
print "Simplified  :", sigma_cubed
print
#Produce Quil code to compute exp[iX]
H = -1.0 * sX(0)
print "Quil to compute exp[iX] on qubit 0:"
print pl.exponential_map(H)(1.0)
Explanation: There are two primary things one can do with Pauli terms and sums:
A Pauli sum's fully "tensored up" form can be computed with the tensor_up function.
Quil code can be generated to compute the exponentiation of a Pauli term, i.e., $\exp[-i\sigma]$.
When arithmetic is done with Pauli sums, simplification is automatically done.
The following shows an instructive example of all three.
End of explanation
<END_TASK>