"Contains the functions needed to implement the emission circuit solving procedure"
import numpy as np
import math
from .stabilizer import *
[docs]
def rref(state: Stabilizer):
"""
Given a stabilizer state, implements the RREF gauge procedure (details in doi.org/10.1088/1367-2630/7/1/170)
:param state: Stabilizer state on which to implement the RREF gauge procedure
:type state: Stabilizer
"""
N=state.size
K=N
KU=0
NL=0
while NL<N-1 and KU<K-1:
zeroitem = True
oneitem = False
twoitem = False
r1=N
r2=N
for k in range(KU,K):
if state.tab[k,NL]!=0 or state.tab[k,NL+N]!=0:
r1 = k
zeroitem = False
oneitem = True
break
for k in range(r1,K):
if state.tab[k,NL]!=0 or state.tab[k,NL+N]!=0:
if state.tab[k,NL]!=state.tab[r1,NL] or state.tab[k,NL+N]!=state.tab[r1,NL+N]:
r2 = k
oneitem = False
twoitem = True
break
if zeroitem:
NL+=1
elif oneitem:
state.swap(KU,r1)
for i in range(KU+1,K):
if state.tab[i,NL]!=0 or state.tab[i,NL+N]!=0:
state.row_add(KU,i)
KU+=1
NL+=1
elif twoitem:
state.swap(KU,r1)
state.swap(KU+1,r2)
for i in range(KU+2,K):
if state.tab[i,NL]!=0 or state.tab[i,NL+N]!=0:
if state.tab[i,NL]==state.tab[KU,NL] and state.tab[i,NL+N]==state.tab[KU,NL+N]:
state.row_add(KU,i)
elif state.tab[i,NL]==state.tab[KU+1,NL] and state.tab[i,NL+N]==state.tab[KU+1,NL+N]:
state.row_add(KU+1,i)
else:
state.row_add(KU,i)
state.row_add(KU+1,i)
NL+=1
KU+=2
[docs]
def heightfunction(state: Stabilizer):
"""
Given a stabilizer state, finds the height function (bipartite entanglement entropy) of the state (formula in doi.org/10.1038/s41534-022-00522-6, equation 1)
:param state: Stabilizer state from which to calculate the height function
:type state: Stabilizer
:return: The outputs of the height function as a list
:rtype: list
"""
state.rref()
N=state.size
leftmost = []
for i in range(state.size):
for j in range(state.size):
if state.tab[i,j]!=0 or state.tab[i,j+N]:
leftmost.append(j+1)
break
height = []
for i in range(state.size+1):
count = sum(j > i for j in leftmost)
height.append(state.size-i-count)
return height
def partial_height(state: Stabilizer,index: int):
"""
Given a stabilizer state, finds the height function (bipartite entanglement entropy) of the state (formula in doi.org/10.1038/s41534-022-00522-6, equation 1) at a specified index
:param state: Stabilizer state from which to calculate the height function
:type state: Stabilizer
:param index: The index at which to evaluate the height function
:type index: int
:return: The outputs of the height function at the index as an integer
:rtype: int
"""
state.rref()
N=state.size
leftmost = []
for i in range(state.size):
for j in range(state.size):
if state.tab[i,j]!=0 or state.tab[i,j+N]:
leftmost.append(j+1)
break
return state.size-index-sum(j > index for j in leftmost)
[docs]
def plot_height(state: Stabilizer):
"""
Given a stabilizer state, finds the height function (bipartite entanglement entropy) of the state (formula in doi.org/10.1038/s41534-022-00522-6, equation 1) and plots it (need matplotlib installed)
:param state: Stabilizer state from which to calculate the height function
:type state: Stabilizer
"""
try:
import matplotlib.pyplot as plt
except:
raise ImportError('Matplotlib.pyplot not present')
try:
height = heightfunction(state)
x_val = [i for i in range(state.size+1)]
tickers = range(math.floor(min(height)), math.ceil(max(height))+1)
plt.grid(color = 'blue', linewidth = 0.5)
plt.plot(x_val,height,color='blue')
plt.scatter(x_val,height,color='blue')
plt.yticks(tickers)
plt.title('Target Height Function')
plt.xlabel('x')
plt.ylabel('h(x)')
except:
pass
[docs]
def num_emitters(state: Stabilizer):
"""
Given a graph state with a certain fixed ordering, finds the minimal number of emitters required to emit that state
:param state: Target photonic stabilizer state
:type state: Stabilizer
:return: Minimal number of photons needed to emit the input states
:rtype: int
"""
height = heightfunction(state)
emitters = max(height)
return emitters
[docs]
def circuit_solver(state: Stabilizer):
"""
Given a stabilizer state, finds a circuit to generate it from a minimal set of emitters (details in doi.org/10.1038/s41534-022-00522-6)
:param state: Stabilizer state from which to calculate the height function
:type state: Stabilizer
:return: Procedure to generate the state, of the form ['Action', q1, q2 (if applicable)]. 'Action' can be 'X', 'Z', 'H', 'S', 'CNOT', 'Measure', or 'Emission'. For 'CNOT', q1 is the control and q2 is the target. For emission and measure, q1 is the emitter and q2 is the photon.
:rtype: list
"""
n_e = num_emitters(state)
n_p = state.size
N = n_e+n_p
target_state = Stabilizer(N)
for i in range(n_p):
target_state.signvector[i] = state.signvector[i]
for j in range(n_p):
target_state.tab[i,j] = state.tab[i,j]
target_state.tab[i,j+N] = state.tab[i,j+n_p]
protocol = []
for h in range(n_p,0,-1):
photonindex = h-1
d = partial_height(target_state,h)-partial_height(target_state,h-1)
if d<0:
rows = []
for i in range(N):
toggler = True
for j in range(n_p):
if target_state.tab[i,j]!=0 or target_state.tab[i,j+N]!=0:
toggler = False
break
if toggler:
rows.append(i)
sums=[]
for row in rows:
sum=0
for j in range(n_p,N):
if target_state.tab[row,j]!=0 or target_state.tab[row,j+N]!=0:
sum+=1
sums.append(sum)
row = rows[sums.index(min(sums))]
for i in range(n_p,N):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
emit = i
if target_state.tab[row,i]==1 and target_state.tab[row,i+N]==0:
protocol.append(['H',i])
target_state.clifford('H',i)
elif target_state.tab[row,i]==1 and target_state.tab[row,i+N]==1:
protocol.append(['S',i])
target_state.clifford('S',i)
target_state.clifford('Z',i)
protocol.append(['H',i])
target_state.clifford('H',i)
break
for i in range(emit+1,N):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
if target_state.tab[row,i]==1 and target_state.tab[row,i+N]==0:
protocol.append(['H',i])
target_state.clifford('H',i)
elif target_state.tab[row,i]==1 and target_state.tab[row,i+N]==1:
protocol.append(['S',i])
target_state.clifford('S',i)
target_state.clifford('Z',i)
protocol.append(['H',i])
target_state.clifford('H',i)
protocol.append(['CNOT',i,emit])
target_state.clifford('CNOT',i,emit)
if target_state.signvector[row]==1:
target_state.clifford('X',emit)
protocol.append(['X',emit])
target_state.clifford('H',emit)
target_state.clifford('CNOT',emit,photonindex)
protocol.append(['Measure',emit,photonindex])
for i in range(N):
toggler = True
if target_state.tab[i,photonindex]==0 and target_state.tab[i,photonindex+N]==0:
toggler = False
if toggler:
for j in range(photonindex):
if target_state.tab[i,j]!=0 or target_state.tab[i,j+N]!=0:
toggler = False
break
if toggler:
row = i
break
emit = -1
if target_state.tab[row,photonindex]==1 and target_state.tab[row,photonindex+N]==0:
protocol.append(['H',photonindex])
target_state.clifford('H',photonindex)
elif target_state.tab[row,photonindex]==1 and target_state.tab[row,photonindex+N]==1:
protocol.append(['S',photonindex])
target_state.clifford('S',photonindex)
target_state.clifford('Z',photonindex)
protocol.append(['H',photonindex])
target_state.clifford('H',photonindex)
for i in range(n_p,N):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
emit = i
if target_state.tab[row,i]==1 and target_state.tab[row,i+N]==0:
protocol.append(['H',i])
target_state.clifford('H',i)
elif target_state.tab[row,i]==1 and target_state.tab[row,i+N]==1:
protocol.append(['S',i])
target_state.clifford('S',i)
target_state.clifford('Z',i)
protocol.append(['H',i])
target_state.clifford('H',i)
break
if emit!= -1:
for i in range(emit+1,N):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
if target_state.tab[row,i]==1 and target_state.tab[row,i+N]==0:
protocol.append(['H',i])
target_state.clifford('H',i)
elif target_state.tab[row,i]==1 and target_state.tab[row,i+N]==1:
protocol.append(['S',i])
target_state.clifford('S',i)
target_state.clifford('Z',i)
protocol.append(['H',i])
target_state.clifford('H',i)
protocol.append(['CNOT',i,emit])
target_state.clifford('CNOT',i,emit)
if target_state.signvector[row]==1:
target_state.clifford('X',photonindex)
protocol.append(['X',photonindex])
target_state.clifford('CNOT',emit,photonindex)
protocol.append(['Emission',emit,photonindex])
for i in range(N):
if target_state.tab[i,photonindex+N]!=0 or target_state.tab[i,photonindex]!=0:
if i!=row:
target_state.row_add(row,i)
for i in range(n_p,N):
target_state.clifford('swap',i,N-1)
target_state.rref(n_p,n_p)
target_state.clifford('swap',i,N-1)
target_state.rref()
sums = []
for i in range(n_p,N):
sum=0
for j in range(n_p,N):
if target_state.tab[i,j]!=0 or target_state.tab[i,j+N]!=0:
sum+=1
sums.append(sum)
if max(sums)==1:
decoupled = True
else:
decoupled = False
while not decoupled:
minimum = min(i for i in sums if i > 1)
row = n_p+sums.index(minimum)
for i in range(n_p,N):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
emit = i
if target_state.tab[row,i]==1 and target_state.tab[row,i+N]==0:
protocol.append(['H',emit])
target_state.clifford('H',emit)
elif target_state.tab[row,i]==1 and target_state.tab[row,i+N]==1:
protocol.append(['S',emit])
target_state.clifford('S',emit)
target_state.clifford('Z',emit)
protocol.append(['H',emit])
target_state.clifford('H',emit)
break
for i in range(emit+1,N):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
if target_state.tab[row,i]==1 and target_state.tab[row,i+N]==0:
protocol.append(['H',i])
target_state.clifford('H',i)
elif target_state.tab[row,i]==1 and target_state.tab[row,i+N]==1:
protocol.append(['S',i])
target_state.clifford('S',i)
target_state.clifford('Z',i)
protocol.append(['H',i])
target_state.clifford('H',i)
target_state.clifford('CNOT',i,emit)
protocol.append(['CNOT',i,emit])
for i in range(n_p,N):
if target_state.tab[i,emit]!=0 or target_state.tab[i,emit+N]!=0:
if i!= row:
target_state.row_add(row,i)
sums = []
for i in range(n_p,N):
sum=0
for j in range(n_p,N):
if target_state.tab[i,j]!=0 or target_state.tab[i,j+N]!=0:
sum+=1
sums.append(sum)
if max(sums)==1:
decoupled = True
else:
decoupled = False
for emitter in range(n_p,N):
for row in range(N):
toggler = True
colcheck = True
if target_state.tab[row,emitter]==0 and target_state.tab[row,emitter+N]==0:
toggler = False
colcheck = False
if colcheck:
for i in range(emitter):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
toggler = False
break
if toggler:
target_state.swap(row,emitter)
break
for i in range(n_p,N):
if target_state.tab[i,i]!=0:
if target_state.tab[i,i]==1 and target_state.tab[i,i+N]==0:
protocol.append(['H',i])
target_state.clifford('H',i)
elif target_state.tab[i,i]==1 and target_state.tab[i,i+N]==1:
protocol.append(['S',i])
target_state.clifford('S',i)
target_state.clifford('Z',i)
protocol.append(['H',i])
target_state.clifford('H',i)
for i in range(n_p,N):
if target_state.signvector[i]==1:
target_state.clifford('X',i)
protocol.append(['X',i])
checker_state = Stabilizer(N)
try:
assert np.array_equal(checker_state.tab,target_state.tab) and np.array_equal(checker_state.signvector,target_state.signvector)
except:
raise AssertionError("Final state not computational zero state, here's what returned "+str(target_state.stabilizers()))
protocol = protocol[::-1]
return protocol
[docs]
def qiskit_circuit_solver(state: Stabilizer, simple: bool = False):
"""
Given a stabilizer state, creates a qiskit circuit to generate it from a minimal set of emitters.
:param state: Stabilizer state from which to calculate the height function
:type state: Stabilizer
:param simple: Uses one register for both photons and emitters instead of seperate registers. Defaults to false
:type simple: bool (optional, defaults to False)
:return: Qiskit circuit corresponding to the protocol
:rtype: QuantumCircuit
"""
try:
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
except:
ImportError('Qiskit failed to import')
n_e = num_emitters(state)
n_p = state.size
N = n_e+n_p
if simple:
q = QuantumRegister(N, 'q')
c = ClassicalRegister(N, 'c')
qc = QuantumCircuit(q,c)
else:
p = QuantumRegister(n_p, 'p')
e = QuantumRegister(n_e, 'e')
c = ClassicalRegister(N, 'c')
qc = QuantumCircuit(p,e,c)
target_state = Stabilizer(N)
for i in range(n_p):
target_state.signvector[i] = state.signvector[i]
for j in range(n_p):
target_state.tab[i,j] = state.tab[i,j]
target_state.tab[i,j+N] = state.tab[i,j+n_p]
for h in range(n_p,0,-1):
photonindex = h-1
d = partial_height(target_state,h)-partial_height(target_state,h-1)
if d<0:
rows = []
for i in range(N):
toggler = True
for j in range(n_p):
if target_state.tab[i,j]!=0 or target_state.tab[i,j+N]!=0:
toggler = False
break
if toggler:
rows.append(i)
sums=[]
for row in rows:
sum=0
for j in range(n_p,N):
if target_state.tab[row,j]!=0 or target_state.tab[row,j+N]!=0:
sum+=1
sums.append(sum)
row = rows[sums.index(min(sums))]
for i in range(n_p,N):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
emit = i
if target_state.tab[row,i]==1 and target_state.tab[row,i+N]==0:
qc.h(i)
target_state.clifford('H',i)
elif target_state.tab[row,i]==1 and target_state.tab[row,i+N]==1:
qc.s(i)
target_state.clifford('S',i)
target_state.clifford('Z',i)
qc.h(i)
target_state.clifford('H',i)
break
for i in range(emit+1,N):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
if target_state.tab[row,i]==1 and target_state.tab[row,i+N]==0:
qc.h(i)
target_state.clifford('H',i)
elif target_state.tab[row,i]==1 and target_state.tab[row,i+N]==1:
qc.s(i)
target_state.clifford('S',i)
target_state.clifford('Z',i)
qc.h(i)
target_state.clifford('H',i)
qc.cx(i,emit)
target_state.clifford('CNOT',i,emit)
if target_state.signvector[row]==1:
target_state.clifford('X',emit)
qc.x(emit)
target_state.clifford('H',emit)
target_state.clifford('CNOT',emit,photonindex)
qc.reset(emit)
with qc.if_test((c, 1)):
qc.x(photonindex)
qc.measure(emit,c[0])
for i in range(N):
toggler = True
if target_state.tab[i,photonindex]==0 and target_state.tab[i,photonindex+N]==0:
toggler = False
if toggler:
for j in range(photonindex):
if target_state.tab[i,j]!=0 or target_state.tab[i,j+N]!=0:
toggler = False
break
if toggler:
row = i
break
emit = -1
if target_state.tab[row,photonindex]==1 and target_state.tab[row,photonindex+N]==0:
qc.h(photonindex)
target_state.clifford('H',photonindex)
elif target_state.tab[row,photonindex]==1 and target_state.tab[row,photonindex+N]==1:
qc.s(photonindex)
target_state.clifford('S',photonindex)
target_state.clifford('Z',photonindex)
qc.h(photonindex)
target_state.clifford('H',photonindex)
for i in range(n_p,N):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
emit = i
if target_state.tab[row,i]==1 and target_state.tab[row,i+N]==0:
qc.h(i)
target_state.clifford('H',i)
elif target_state.tab[row,i]==1 and target_state.tab[row,i+N]==1:
qc.s(i)
target_state.clifford('S',i)
target_state.clifford('Z',i)
qc.h(i)
target_state.clifford('H',i)
break
if emit!= -1:
for i in range(emit+1,N):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
if target_state.tab[row,i]==1 and target_state.tab[row,i+N]==0:
qc.h(i)
target_state.clifford('H',i)
elif target_state.tab[row,i]==1 and target_state.tab[row,i+N]==1:
qc.s(i)
target_state.clifford('S',i)
target_state.clifford('Z',i)
qc.h(i)
target_state.clifford('H',i)
qc.cx(i,emit)
target_state.clifford('CNOT',i,emit)
if target_state.signvector[row]==1:
target_state.clifford('X',photonindex)
qc.x(photonindex)
target_state.clifford('CNOT',emit,photonindex)
qc.cx(emit, photonindex)
for i in range(N):
if target_state.tab[i,photonindex+N]!=0 or target_state.tab[i,photonindex]!=0:
if i!=row:
target_state.row_add(row,i)
for i in range(n_p,N):
target_state.clifford('swap',i,N-1)
target_state.rref(n_p,n_p)
target_state.clifford('swap',i,N-1)
target_state.rref()
sums = []
for i in range(n_p,N):
sum=0
for j in range(n_p,N):
if target_state.tab[i,j]!=0 or target_state.tab[i,j+N]!=0:
sum+=1
sums.append(sum)
if max(sums)==1:
decoupled = True
else:
decoupled = False
while not decoupled:
for i in range(2,N):
if i in sums:
minimum = i
break
row = n_p+sums.index(minimum)
for i in range(n_p,N):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
emit = i
if target_state.tab[row,i]==1 and target_state.tab[row,i+N]==0:
qc.h(emit)
target_state.clifford('H',emit)
elif target_state.tab[row,i]==1 and target_state.tab[row,i+N]==1:
qc.s(emit)
target_state.clifford('S',emit)
target_state.clifford('Z',emit)
qc.h(emit)
target_state.clifford('H',emit)
break
for i in range(emit+1,N):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
if target_state.tab[row,i]==1 and target_state.tab[row,i+N]==0:
qc.h(i)
target_state.clifford('H',i)
elif target_state.tab[row,i]==1 and target_state.tab[row,i+N]==1:
qc.s(i)
target_state.clifford('S',i)
target_state.clifford('Z',i)
qc.h(i)
target_state.clifford('H',i)
target_state.clifford('CNOT',i,emit)
qc.cx(i,emit)
for i in range(n_p,N):
if target_state.tab[i,emit]!=0 or target_state.tab[i,emit+N]!=0:
if i!= row:
target_state.row_add(row,i)
sums = []
for i in range(n_p,N):
sum=0
for j in range(n_p,N):
if target_state.tab[i,j]!=0 or target_state.tab[i,j+N]!=0:
sum+=1
sums.append(sum)
if max(sums)==1:
decoupled = True
else:
decoupled = False
for emitter in range(n_p,N):
for row in range(N):
toggler = True
colcheck = True
if target_state.tab[row,emitter]==0 and target_state.tab[row,emitter+N]==0:
toggler = False
colcheck = False
if colcheck:
for i in range(emitter):
if target_state.tab[row,i]!=0 or target_state.tab[row,i+N]!=0:
toggler = False
break
if toggler:
target_state.swap(row,emitter)
break
for i in range(n_p,N):
if target_state.tab[i,i]!=0:
if target_state.tab[i,i]==1 and target_state.tab[i,i+N]==0:
qc.h(i)
target_state.clifford('H',i)
elif target_state.tab[i,i]==1 and target_state.tab[i,i+N]==1:
qc.s(i)
target_state.clifford('S',i)
target_state.clifford('Z',i)
qc.h(i)
target_state.clifford('H',i)
for i in range(n_p,N):
if target_state.signvector[i]==1:
target_state.clifford('X',i)
qc.x(i)
checker_state = Stabilizer(N)
if np.array_equal(checker_state.tab,target_state.tab) and np.array_equal(checker_state.signvector,target_state.signvector):
qc.data = qc.data[::-1]
return qc
else:
print('Something went wrong')
print(target_state.stabilizers())
return None
[docs]
def qiskit_circuit_solver_alternate(state: Stabilizer, simple: bool = False):
"""
Given a stabilizer state, creates a qiskit circuit to generate it from a minimal set of emitters. Uses circuit_solver to generate the protocol instead of doing it simultaneously.
:param state: Stabilizer state from which to calculate the height function
:type state: Stabilizer
:param simple: Uses one register for both photons and emitters instead of separate ones
:type simple: bool (optional, defaults to False)
:return: Qiskit circuit corresponding to the protocol
:rtype: QuantumCircuit
"""
try:
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
except:
ImportError('Qiskit failed to import')
photons = state.size
emitters = num_emitters(state)
if simple:
q = QuantumRegister(photons+emitters, 'q')
c = ClassicalRegister(photons+emitters, 'c')
qc = QuantumCircuit(q,c)
else:
p = QuantumRegister(photons, 'p')
e = QuantumRegister(emitters,'e')
c = ClassicalRegister(photons+emitters, 'c')
qc = QuantumCircuit(p,e,c)
operations = circuit_solver(state)
for operation in operations:
if operation[0]=='H':
qc.h(operation[1])
elif operation[0] == 'Emission':
qc.cx(operation[1],operation[2])
elif operation[0]=='Z':
qc.z(operation[1])
elif operation[0]=='X':
qc.x(operation[1])
elif operation[0]=='Y':
qc.y(operation[1])
elif operation[0]=='S':
qc.s(operation[1])
elif operation[0]=='CNOT':
qc.cx(operation[1],operation[2])
elif operation[0]=='Measure':
qc.measure(operation[1],c[0])
with qc.if_test((c, 1)):
qc.x(operation[2])
qc.reset(operation[1])
return qc
[docs]
def num_cnots(state: Stabilizer):
"""
Given a graph state with a certain fixed ordering, finds the number of emitters-emitter CNOT gates required to emit that state using this implementation of the solving algorithm
:param state: Target photonic stabilizer state
:type state: Stabilizer
:return: Number of emitter-emitter CNOT gates needed to emit the input state using this version of the algorithm
:rtype: int
"""
procedure = circuit_solver(state)
cnots = 0
for i in range(len(procedure)):
if procedure[i][0] == 'CNOT':
cnots+=1
return cnots
[docs]
def emitter_cnot(state: Stabilizer):
"""
Given a stabilizer state, finds the number of emitters and the number of emitter-emitter CNOT gates needed to implement the circuit
:param state: Stabilizer state from which to calculate the height function
:type state: Stabilizer
:return: A list of the form [emitters,cnots]
:rtype: list
"""
emitter = num_emitters(state)
procedure = circuit_solver(state)
cnots = 0
for i in range(len(procedure)):
if procedure[i][0] == 'CNOT':
cnots+=1
data = [emitter,cnots]
return data
if __name__ == "__main__":
# Do something if this file is invoked on its own
state = Stabilizer(edgelist = [[0,1],[1,2],[2,3],[3,4],[4,5],[5,0]])