from __future__ import division, print_function, absolute_import, unicode_literals
import numpy as np
from scipy.linalg import expm
import pygsti
from pygsti.construction import std1Q_XYI as std
from pygsti.construction import std2Q_XYICNOT as std2Q
from pygsti.extras import newrb as prb
WARNING: yacc table file version is out of date
# A list of standard hard-coded gate labels
gllist = ['Gi','Gh','Gp','Gcnot']
# The number of qubits
n = 4
# The availiability of gates that are not available to all qubit / qubit pairs
availability = {}
a = [(i,i+1) for i in range(0,n-1)] + [(n-1,0)]
availability={'Gcnot':a}
pspec = pygsti.obj.ProcessorSpec(n, gllist, availability=availability, verbosity=0)
# Add a target dm model
pspec.add_std_model('dm_target', parameterization="static", sim_type="dmmap")
# Create a fairly trivial circuit
c = pygsti.objects.Circuit(gatestring=[('Gp',0),('Gi',0),('Gh',1),('Gp',3),('Gcnot',1,2)],num_lines=n)
print(c)
Qubit 0 ---|Gp |-|Gi |-| |--- Qubit 1 ---| |-|Gh |-Gcnot:1:2|-|-- Qubit 2 ---| |-| |-Gcnot:1:2|-|-- Qubit 3 ---| |-|Gp |-| |---
vs_target_out = c.simulate(pspec.models['target'])
dm_target_out = c.simulate(pspec.models['dm_target'])
for key in vs_target_out:
assert(abs(vs_target_out[key] - dm_target_out[key]) < 1e-10)
pspec.add_std_model('dmsim', parameterization="static", sim_type="dmmap")
pspec.add_std_model('svsim', parameterization="static", sim_type="svmap")
# Nothing should be different from above, but let's check.
vs_target_out = c.simulate(pspec.models['target'])
dm_target_out = c.simulate(pspec.models['dm_target'])
for key in vs_target_out:
assert(abs(vs_target_out[key] - dm_target_out[key]) < 1e-10)
# Change the Gi gates on qubit 0 to be small X rotations
theta = 0.01*np.pi
for glabel in list(pspec.models['svsim'].gates.keys()):
if glabel.name == 'Gi':
if glabel.qubits == (0,):
unitary = expm(-1j*theta*np.array([[0.,1.],[1.,0.]])/2)
pspec.models['svsim'][glabel] = unitary
pspec.models['dmsim'][glabel] = pygsti.unitary_to_pauligate(unitary)
# Create a particularly simple circuit
c2 = pygsti.objects.Circuit(gatestring=[('Gi',0),('Gi',0)],num_lines=n)
print(c2)
Qubit 0 ---|Gi |-|Gi |--- Qubit 1 ---| |-| |--- Qubit 2 ---| |-| |--- Qubit 3 ---| |-| |---
sv_out = c2.simulate(pspec.models['svsim'])
dm_out = c2.simulate(pspec.models['dmsim'])
for key in sv_out:
assert(abs(sv_out[key] - dm_out[key]) < 1e-10)
assert(abs(np.cos(2*theta/2)**2 - sv_out['0'*n]) < 1e-10)
print(np.cos(2*theta/2)**2)
print(sv_out['0'*n])
print(dm_out['0'*n])
0.999013364214 0.9990133642141359 0.999013364214136
# Let's make the special Idle be the Gi gate.
c2.replace_gatename('I','Gi')
# Nothing should be different from above, but let's check.
sv_out = c2.simulate(pspec.models['svsim'])
dm_out = c2.simulate(pspec.models['dmsim'])
for key in sv_out:
assert(abs(sv_out[key] - dm_out[key]) < 1e-10)
assert(abs(np.cos(2*theta/2)**2 - sv_out['0'*n]) < 1e-10)
# Change the Gi gates on all qubits to be small X rotations
for glabel in list(pspec.models['svsim'].gates.keys()):
if glabel.name == 'Gi':
unitary = expm(-1j*theta*np.array([[0.,1.],[1.,0.]])/2)
pspec.models['svsim'][glabel] = unitary
pspec.models['dmsim'][glabel] = pygsti.unitary_to_pauligate(unitary)
# The probability of an error-free circuit is now the nth power of the previous probability. Check that is the case
sv_out = c2.simulate(pspec.models['svsim'])
dm_out = c2.simulate(pspec.models['dmsim'])
for key in sv_out:
assert(abs(sv_out[key] - dm_out[key]) < 1e-10)
assert(abs(np.cos(2*theta/2)**(2*n) - sv_out['0'*n]) < 1e-10)
# Let's get some RB circuits to test the simulators on
sectors = [[pygsti.objects.Label('Gcnot',(0,1))],[pygsti.objects.Label('Gcnot',(1,2))]]
cprb = prb.sample_prb_circuit(pspec,10,sampler='sectors',sampler_args={'sectors':sectors,'two_qubit_prob':0.5},return_partitioned=False)
# Let's test the the target output probability is 1. for the '0000...' state
prbout_sv_target = cprb.simulate(pspec.models['target'])
prbout_dm_target = cprb.simulate(pspec.models['dm_target'])
for key in prbout_sv_target:
assert(abs(prbout_sv_target[key] - prbout_dm_target[key]) < 1e-10)
assert(abs(prbout_sv_target ['0'*n] - 1.) < 1e-10)
# Let's create a more complicated unitary error model
thetarange = np.linspace(0,theta,n)
cnottheta = theta
for glabel in list(pspec.models['svsim'].gates.keys()):
# Change the Gi and Gh gates on all qubits to have a small X rotation error, increasing with qubit number
if glabel.name == 'Gi':
unitary = expm(-1j*thetarange[glabel.qubits[0]]*np.array([[0.,1.],[1.,0.]])/2)
pspec.models['svsim'][glabel] = unitary
pspec.models['dmsim'][glabel] = pygsti.unitary_to_pauligate(unitary)
if glabel.name == 'Gh':
unitary = np.dot(expm(-1j*thetarange[glabel.qubits[0]]*np.array([[0.,1.],[1.,0.]])/2),np.array([[1.,1.],[1.,-1.]],complex)/np.sqrt(2))
pspec.models['svsim'][glabel] = unitary
pspec.models['dmsim'][glabel] = pygsti.unitary_to_pauligate(unitary)
#Change the Gp to have a small Z rotation error, decreasing with qubit number
if glabel.name == 'Gp':
unitary = expm(-1j*(thetarange[n-1-glabel.qubits[0]]+np.pi/2)*np.array([[1.,0.],[0.,-1.]])/2)
pspec.models['svsim'][glabel] = unitary
pspec.models['dmsim'][glabel] = pygsti.unitary_to_pauligate(unitary)
# CNOT gates are followd by a control Z rotation with a small rotation angle.
if glabel.name == 'Gnot':
unitary = np.dot(np.array([[1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,0.,0.,exp(cnottheta*1j)]],complex),np.array([[1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,0.,1.],[0.,0.,1.,0.]],complex))
pspec.models['svsim'][glabel] = unitary
pspec.models['dmsim'][glabel] = pygsti.unitary_to_pauligate(unitary)
# Let's replace the 'I' gates in the circuit with the 'Gi' gate
cprb.replace_gatename('I','Gi')
# Let's test it. Let's check the simulators agree and the the survival probability is plausable
prbout_sv = cprb.simulate(pspec.models['svsim'])
prbout_dm = cprb.simulate(pspec.models['dmsim'])
for key in prbout_sv:
assert(abs(prbout_sv[key] - prbout_dm[key]) < 1e-10)
# These numbers are *not* guaranteed to always be even close (because, coherent addition/cancelation can happen
# depending on the sampled circuit). But in a typical example they will likely be of the same order of magnitude.
print('The actual survival probability:', prbout_sv['0'*n])
print('An order-of-magnitude estimate of the survival probabilitiy:', np.cos(theta/2)**(2*n*cprb.depth()))
The actual survival probability: 0.8949392394072598 An order-of-magnitude estimate of the survival probabilitiy: 0.942499338239