Before reading this Jupyter notebook, we recommend that you first read the earlier notebook named
Stairs_circuit_and_its_gradients_in_native.ipynb
located in the same folder in the Qubiter repo as this notebook. In that earlier notebook, we use the Qubiter (i.e., native) simulator to evaluate the gradients of a special quantum cost function. In this notebook, we evaluate the same gradients, but, instead of the native simulator, we use the Rigetti QVM (quantum virtual machine), which is a bit more complicated than using the native simulator.
Qubiter supports, as a single gate, a general U(2) operation with any number of controls, of either the T (full circle) or F (empty circle) kind. Such gates are fundamental to the Stairs circuit that we are considering. For now at least, Rigetti doesn't support such gates in a single step, so we have figured out a work-around until they do. Programmers are masters at figuring out work-arounds.
What we do is use Qubiter's class CGateExpander
to expand all
multi-controlled U(2) gates into simple CNOTs and
single qubit rotation gates. This is a very basic set of gates that
Rigetti's real and virtual machines can handle.
(Also,
right before the expansion, we substitute each placeholder
variable (aka parameter) by its float value.)
But CGateExpander
takes as input a quantum circuit written in Qubiter's language and
returns a new,
expanded, quantum circuit also written in Qubiter's language.
So further processing is required.
We then use Qubiter's class Qubiter_to_RigettiPyQuil
to translate the expanded quantum circuit from Qubiter's to Rigetti's language.
Once we have translated all the quantum circuits
to Rigetti's language, we are home free. From there on,
we just follow very similar steps to
those performed in the earlier, all-native notebook.
This notebook demos 2 Qubiter classes:
StairsDeriv_rigetti
, evaluates the 4 derivatives of a single gate of a Stairs circuit. It uses Rigetti simulators or their real physical qc to do this.StairsAllDeriv_rigetti
, evaluates all the derivatives, for all the gates of a Stairs circuit. It uses Rigetti simulators or their real physical qc to do this.This notebook calls Rigetti's method QVMConnection() which only works if you first:
import os
import sys
print(os.getcwd())
os.chdir('../../')
print(os.getcwd())
sys.path.insert(0,os.getcwd())
/home/rrtucci/PycharmProjects/qubiter/qubiter/jupyter_notebooks /home/rrtucci/PycharmProjects/qubiter
from pyquil.quil import Program
from pyquil.api import QVMConnection
from pyquil.gates import *
from pyquil import get_qc
qvm_url = "http://localhost:5000"
compiler_server_address = "tcp://localhost:5555"
forest_url = "https://forest-server.qcs.rigetti.com"
qvm = QVMConnection(endpoint=qvm_url, compiler_endpoint=compiler_server_address)
qc = get_qc('4q-qvm')
from qubiter.adv_applications.StairsDeriv_rigetti import *
loaded OneQubitGate, WITHOUT autograd.numpy
# print docstring of the class
print(StairsDeriv_rigetti.__doc__)
This class is a child of StairsDeriv. Its main purpose is to override the method get_mean_val() of its abstract parent class StairsDeriv. In this class, the simulation necessary to evaluate the output of get_mean_val() is done by Rigetti Pyquil simulators or their physical qc device. Attributes ---------- qc : QuantumComputer returned by PyQuil method get_qc() translation_line_list : list[str] a list of lines of PyQuil code generated by the translator. The lines all start with "pg +=". translator : Qubiter_to_RigettiPyQuil
num_qbits = 4
parent_num_qbits = num_qbits - 1 # one bit for ancilla
# u2_bit_to_higher_bits = None
u2_bit_to_higher_bits = {0: [2], 1: [2], 2: []}
gate_str_to_rads_list = StairsCkt_writer.\
get_gate_str_to_rads_list(parent_num_qbits,
'#int', rads_const=np.pi/2,
u2_bit_to_higher_bits=u2_bit_to_higher_bits)
pp.pprint(gate_str_to_rads_list)
OrderedDict([('prior', ['#50', '#51', '#52', '#53']), ('2F', ['#500', '#501', '#502', '#503']), ('2T', ['#510', '#511', '#512', '#513']), ('2F1_', ['#5050', '#5051', '#5052', '#5053']), ('2T1_', ['#5150', '#5151', '#5152', '#5153'])])
deriv_gate_str = list(gate_str_to_rads_list.keys())[2]
print(deriv_gate_str)
2T
file_prefix = 'stairs_deriv_rigetti_test'
hamil = QubitOperator('X1 Y0 X1 Y1', .4) +\
QubitOperator('Y2 X1', .7)
print(hamil)
0.4 [Y0 Y1] + 0.7 [X1 Y2]
der = StairsDeriv_rigetti(qc, deriv_gate_str,
gate_str_to_rads_list, file_prefix,
parent_num_qbits, hamil)
var_num_to_rads = StairsCkt_writer.get_var_num_to_rads(
gate_str_to_rads_list, 'const', rads_const=np.pi/2)
partials_list = der.get_mean_val(var_num_to_rads)
print('partials_list=', partials_list)
partials_list= [0.0034028451206715993, -0.06295185894191425, -0.05962213592923961, -0.031804538947866376]
from qubiter.adv_applications.StairsAllDeriv_rigetti import *
# print docstring of the class
print(StairsAllDeriv_rigetti.__doc__)
This class is a child of StairsDeriv_rigetti. For the parent class, the get_mean_val() method returns a list of 4 partial derivatives belonging to a particular gate string (a gate_str is a key in gate_str_to_rads_list). For this class, get_mean_val() returns an ordered dictionary mapping each gate_str to its 4 partials. Attributes ---------- deriv_gate_str : str
num_qbits = 4
parent_num_qbits = num_qbits - 1 # one bit for ancilla
# u2_bit_to_higher_bits = None
u2_bit_to_higher_bits = {0: [2], 1: [2], 2: []}
gate_str_to_rads_list = StairsCkt_writer.\
get_gate_str_to_rads_list(parent_num_qbits,
'#int', rads_const=np.pi/2,
u2_bit_to_higher_bits=u2_bit_to_higher_bits)
pp.pprint(gate_str_to_rads_list)
file_prefix = 'stairs_all_deriv_rigetti_test'
hamil = QubitOperator('Y0 X1', .4) +\
QubitOperator('X0', .7)
der = StairsAllDeriv_rigetti(qc, gate_str_to_rads_list, file_prefix,
parent_num_qbits, hamil)
var_num_to_rads = StairsCkt_writer.get_var_num_to_rads(
gate_str_to_rads_list, 'const', rads_const=np.pi/2)
gate_str_to_partials_list = der.get_mean_val(var_num_to_rads)
pp.pprint(gate_str_to_partials_list)
OrderedDict([('prior', ['#50', '#51', '#52', '#53']), ('2F', ['#500', '#501', '#502', '#503']), ('2T', ['#510', '#511', '#512', '#513']), ('2F1_', ['#5050', '#5051', '#5052', '#5053']), ('2T1_', ['#5150', '#5151', '#5152', '#5153'])]) OrderedDict([('prior', [-0.23097482034489736, -0.08381924385888949, -0.08381924310178478, -0.08381924461599415]), ('2F', [0.03990238187610799, 0.057359948994478056, 0.07552941989528865, 0.05106475894990702]), ('2T', [-0.3022188538314251, -0.06503835637026129, -0.07075909489962387, -0.08659938315985996]), ('2F1_', [0.3487952036687368, 0.04268284645027558, 0.089594909274672, -0.039203811008462916]), ('2T1_', [-0.467144918119173, 0.17580028803003597, 0.17338374316874772, 0.1257533545324687])])