qflux 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of qflux might be problematic. Click here for more details.
- qflux/GQME/__init__.py +7 -0
- qflux/GQME/dynamics_GQME.py +438 -0
- qflux/GQME/params.py +62 -0
- qflux/GQME/readwrite.py +119 -0
- qflux/GQME/tdvp.py +233 -0
- qflux/GQME/tt_tfd.py +448 -0
- qflux/__init__.py +5 -0
- qflux/closed_systems/__init__.py +17 -0
- qflux/closed_systems/classical_methods.py +427 -0
- qflux/closed_systems/custom_execute.py +22 -0
- qflux/closed_systems/hamiltonians.py +88 -0
- qflux/closed_systems/qubit_methods.py +266 -0
- qflux/closed_systems/spin_dynamics_oo.py +371 -0
- qflux/closed_systems/spin_propagators.py +300 -0
- qflux/closed_systems/utils.py +205 -0
- qflux/open_systems/__init__.py +2 -0
- qflux/open_systems/dilation_circuit.py +183 -0
- qflux/open_systems/numerical_methods.py +303 -0
- qflux/open_systems/params.py +29 -0
- qflux/open_systems/quantum_simulation.py +360 -0
- qflux/open_systems/trans_basis.py +121 -0
- qflux/open_systems/walsh_gray_optimization.py +311 -0
- qflux/typing/__init__.py +0 -0
- qflux/typing/examples.py +24 -0
- qflux/utils/__init__.py +0 -0
- qflux/utils/io.py +16 -0
- qflux/utils/logging_config.py +61 -0
- qflux/variational_methods/__init__.py +1 -0
- qflux/variational_methods/qmad/__init__.py +0 -0
- qflux/variational_methods/qmad/ansatz.py +64 -0
- qflux/variational_methods/qmad/ansatzVect.py +61 -0
- qflux/variational_methods/qmad/effh.py +75 -0
- qflux/variational_methods/qmad/solver.py +356 -0
- qflux-0.0.1.dist-info/METADATA +144 -0
- qflux-0.0.1.dist-info/RECORD +38 -0
- qflux-0.0.1.dist-info/WHEEL +5 -0
- qflux-0.0.1.dist-info/licenses/LICENSE +674 -0
- qflux-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import scipy.fft as sfft
|
|
3
|
+
|
|
4
|
+
#the module in the current package
|
|
5
|
+
from . import params as pa
|
|
6
|
+
|
|
7
|
+
def x2k_wave(dx,psi):
|
|
8
|
+
"""
|
|
9
|
+
transform the wavefunction from x space to k space
|
|
10
|
+
dx: the interval of the x-space grid point
|
|
11
|
+
psi: the wavefunction in the x-space
|
|
12
|
+
"""
|
|
13
|
+
pre_fac = dx/(2*np.pi)**0.5
|
|
14
|
+
psik = sfft.fft(psi)*pre_fac
|
|
15
|
+
return psik
|
|
16
|
+
|
|
17
|
+
def k2x_wave(dx,psik):
|
|
18
|
+
"""
|
|
19
|
+
transform the wavefunction from x space to k space
|
|
20
|
+
dx: the interval of the x-space grid point
|
|
21
|
+
psi: the wavefunction in the k-space
|
|
22
|
+
"""
|
|
23
|
+
pre_fac = (2*np.pi)**0.5/dx
|
|
24
|
+
psix = sfft.ifft(psik.copy())*pre_fac
|
|
25
|
+
return psix
|
|
26
|
+
|
|
27
|
+
def nested_kronecker_product(pauli_str):
|
|
28
|
+
'''
|
|
29
|
+
Handles Kronecker Products for list (i.e., pauli_str = 'ZZZ' will evaluate Z Z Z).
|
|
30
|
+
Given string 'pauli_str' this evaluates the kronecker product of all elements.
|
|
31
|
+
'''
|
|
32
|
+
|
|
33
|
+
# Define a dictionary with the four Pauli matrices:
|
|
34
|
+
pms = {'I': pa.I,'X': pa.X,'Y': pa.Y,'Z': pa.Z}
|
|
35
|
+
|
|
36
|
+
result = 1
|
|
37
|
+
for i in range(len(pauli_str)):
|
|
38
|
+
result = np.kron(result,pms[pauli_str[i]])
|
|
39
|
+
return result
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def pauli_to_ham(pauli_dict, Nqb):
|
|
43
|
+
'''
|
|
44
|
+
Function that Assembles the Hamiltonian based on their Pauli string
|
|
45
|
+
|
|
46
|
+
pauli_dict: A dictionary that contain all the pauli string and the value of the Hamiltonian
|
|
47
|
+
(the key is pauli string and the value is coefficient)
|
|
48
|
+
|
|
49
|
+
Nqb: number of qubits, should match the length of the pauli string
|
|
50
|
+
|
|
51
|
+
return: Hamiltonian matrix
|
|
52
|
+
'''
|
|
53
|
+
|
|
54
|
+
Hmat = np.zeros((2**Nqb,2**Nqb),dtype=np.complex128)
|
|
55
|
+
for key in pauli_dict:
|
|
56
|
+
Hmat += pauli_dict[key]*nested_kronecker_product(key)
|
|
57
|
+
|
|
58
|
+
return Hmat
|
|
59
|
+
|
|
60
|
+
def ham_to_pauli(Ham_arr, Nqb, tol=1E-5):
|
|
61
|
+
'''
|
|
62
|
+
Function that decomposes `Ham_arr` into a sum of Pauli strings.
|
|
63
|
+
result: a dictionary with the key is pauli string and the value is coefficient
|
|
64
|
+
'''
|
|
65
|
+
import itertools
|
|
66
|
+
|
|
67
|
+
pauli_keys = ['I','X','Y','Z'] # Keys of the dictionary
|
|
68
|
+
|
|
69
|
+
if(2**Nqb != Ham_arr.shape[0]):
|
|
70
|
+
print('Nqb and Matrix size not matched!')
|
|
71
|
+
|
|
72
|
+
# Make all possible tensor products of Pauli matrices sigma
|
|
73
|
+
sigma_combinations = list(itertools.product(pauli_keys, repeat=Nqb))
|
|
74
|
+
|
|
75
|
+
result = {} # Initialize an empty dictionary to the results
|
|
76
|
+
for ii in range(len(sigma_combinations)):
|
|
77
|
+
pauli_str = ''.join(sigma_combinations[ii])
|
|
78
|
+
|
|
79
|
+
# Evaluate the Kronecker product of the matrix array
|
|
80
|
+
tmp_p_matrix = nested_kronecker_product(pauli_str)
|
|
81
|
+
|
|
82
|
+
# Compute the coefficient for each Pauli string
|
|
83
|
+
a_coeff = (1/(2**Nqb)) * np.trace(tmp_p_matrix @ Ham_arr)
|
|
84
|
+
|
|
85
|
+
# If the coefficient is non-zero, we want to use it!
|
|
86
|
+
if abs(a_coeff) > tol:
|
|
87
|
+
result[pauli_str] = a_coeff.real
|
|
88
|
+
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
def trans_basis(operator,nbasis,psi_newbasis):
|
|
92
|
+
"""
|
|
93
|
+
operator: the operator matrix in the old basis
|
|
94
|
+
nbasis: the truncation in the new basis
|
|
95
|
+
psi_newbasis: the new basis wave function expressed in the old basis
|
|
96
|
+
psi_newbasis[i,j] = <i|psi_newbasis|j>, with i is old basis, j new basis
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
operator_new = np.zeros((nbasis,nbasis),dtype=np.complex128)
|
|
100
|
+
|
|
101
|
+
for i in range(nbasis):
|
|
102
|
+
for j in range(nbasis):
|
|
103
|
+
operator_new[i,j] = np.dot(np.dot(psi_newbasis[:,i].conj(),operator),psi_newbasis[:,j])
|
|
104
|
+
|
|
105
|
+
return operator_new
|
|
106
|
+
|
|
107
|
+
def trans_basis_diag(diag_opr,nbasis,psi_newbasis):
|
|
108
|
+
"""
|
|
109
|
+
diag_opr: the array represent the diagnoal operator in the old basis
|
|
110
|
+
nbasis: the truncation in the new basis
|
|
111
|
+
psi_newbasis: the new basis wave function expressed in the old basis
|
|
112
|
+
psi_newbasis[i,j] = <i|psi_newbasis|j>, with i is old basis, j new basis
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
operator_new = np.zeros((nbasis,nbasis),dtype=np.complex128)
|
|
116
|
+
|
|
117
|
+
for i in range(nbasis):
|
|
118
|
+
for j in range(nbasis):
|
|
119
|
+
operator_new[i,j] = np.dot(np.multiply(psi_newbasis[:,i].conj(),diag_opr),psi_newbasis[:,j])
|
|
120
|
+
|
|
121
|
+
return operator_new
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from qiskit import QuantumRegister, QuantumCircuit
|
|
3
|
+
|
|
4
|
+
from .params import I,X,Y,Z
|
|
5
|
+
|
|
6
|
+
##=======the Walsh operator scheme===========
|
|
7
|
+
#1. functions for Walsh representation
|
|
8
|
+
#(a). the binary and qubit state representation of a integer
|
|
9
|
+
#(b). the function to calculate walsh coefficient
|
|
10
|
+
#this is binary representation of a integer j!
|
|
11
|
+
#j=[j1,j2,j3] for example: 3=[1,1,0]
|
|
12
|
+
def binary(j,nbits):
|
|
13
|
+
arr = np.zeros(nbits,dtype=int)
|
|
14
|
+
res = j
|
|
15
|
+
for i in range(nbits):
|
|
16
|
+
if(res>1):
|
|
17
|
+
arr[i] = res%2
|
|
18
|
+
res=res//2
|
|
19
|
+
else:
|
|
20
|
+
arr[i] = res
|
|
21
|
+
break
|
|
22
|
+
return arr
|
|
23
|
+
|
|
24
|
+
#input: array of 0 or 1
|
|
25
|
+
#output: max index of 1
|
|
26
|
+
#e.g.: j=4 binary is [0,0,1,0], max_bit is 2
|
|
27
|
+
def max_bit(arr):
|
|
28
|
+
iflag = 0
|
|
29
|
+
for i in range(len(arr)-1,-1,-1):
|
|
30
|
+
if(arr[i]==1):
|
|
31
|
+
iflag = i
|
|
32
|
+
break
|
|
33
|
+
return iflag
|
|
34
|
+
|
|
35
|
+
#j-th gray code:
|
|
36
|
+
#first do the right shift of the binary representation
|
|
37
|
+
#then do the bit wise XOR
|
|
38
|
+
def gray(j,nbits):
|
|
39
|
+
arr_n1 = binary(j//2,nbits) #
|
|
40
|
+
arr_j = binary(j,nbits) #
|
|
41
|
+
arr_res = np.zeros_like(arr_n1)
|
|
42
|
+
for i in range(nbits):
|
|
43
|
+
if(arr_n1[i]==arr_j[i]):
|
|
44
|
+
arr_res[i] = 0
|
|
45
|
+
else:
|
|
46
|
+
arr_res[i] = 1
|
|
47
|
+
return arr_res
|
|
48
|
+
|
|
49
|
+
# return the decimal of arr_j
|
|
50
|
+
# [1,1,0] is j0=1,j1=1, which is 3
|
|
51
|
+
def decimal(arr_j):
|
|
52
|
+
res = 0
|
|
53
|
+
nbits = len(arr_j)
|
|
54
|
+
for i in range(nbits):
|
|
55
|
+
res+= 2**i*arr_j[i]
|
|
56
|
+
return res
|
|
57
|
+
|
|
58
|
+
#2. The Walsh coefficient and operator
|
|
59
|
+
#input an fk array
|
|
60
|
+
#(that's the discretize array in qubit statevector) |qn,...,q1>
|
|
61
|
+
#e.g. f3 is the coefficient of |0,1,1>, which is q0=1, q1=1
|
|
62
|
+
#this just match the definition of function binary
|
|
63
|
+
#output the coefficient in the walsh function representation
|
|
64
|
+
def walsh_coef(fk,nbits):
|
|
65
|
+
nlen = 2**nbits
|
|
66
|
+
if(nlen!=len(fk)): print('walsh_coef: err in array length')
|
|
67
|
+
|
|
68
|
+
a_arr = np.zeros(nlen)
|
|
69
|
+
for i in range(nlen):
|
|
70
|
+
arr_j = binary(i,nbits)
|
|
71
|
+
for k in range(nlen):
|
|
72
|
+
fac = (-1)**(np.dot(arr_j,binary(k,nbits)))
|
|
73
|
+
a_arr[i] += fk[k]*fac
|
|
74
|
+
a_arr[i] /= nlen
|
|
75
|
+
return a_arr
|
|
76
|
+
|
|
77
|
+
#walsh operator, \Prod \otimes Z^(j)
|
|
78
|
+
#j=[1,1,0] return IZZ (q0,q1 is Z, q2 is I, in qiskit convention order)
|
|
79
|
+
def walsh_oprQ(arr_j,nbits):
|
|
80
|
+
res = 1.0
|
|
81
|
+
for i in range(nbits-1,-1,-1):
|
|
82
|
+
if(arr_j[i]==0):
|
|
83
|
+
res = np.kron(res,I)
|
|
84
|
+
elif(arr_j[i]==1):
|
|
85
|
+
res = np.kron(res,Z)
|
|
86
|
+
else:
|
|
87
|
+
print('err')
|
|
88
|
+
return res
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
#3. Walsh circuit list and Gray code optimize for diagonal unitaries
|
|
92
|
+
# functions to generate the quantum circuit of the diagnoal unitaries in the Walsh Operator representation
|
|
93
|
+
#input an walsh coefficient (arra) of f_k array,
|
|
94
|
+
#output the list that contain all the parameters to construct the circuit for U=diag(e^(i f_k))
|
|
95
|
+
def cirq_list_walsh(arra,Nqb,epsilon):
|
|
96
|
+
if(len(arra)!=2**Nqb): print('err')
|
|
97
|
+
|
|
98
|
+
U=[]
|
|
99
|
+
|
|
100
|
+
for j in range(1,2**Nqb):
|
|
101
|
+
|
|
102
|
+
#encoding: Gray Code or Binary Code
|
|
103
|
+
#arr_j = binary(j,Nqb)
|
|
104
|
+
arr_j = gray(j,Nqb)
|
|
105
|
+
j_code = decimal(gray(j,Nqb))
|
|
106
|
+
|
|
107
|
+
if(abs(arra[j_code])<epsilon): continue
|
|
108
|
+
|
|
109
|
+
max_index = max_bit(arr_j) #the R gate will located at the max index
|
|
110
|
+
|
|
111
|
+
for ibit in range(max_index):
|
|
112
|
+
if(arr_j[ibit]==1):
|
|
113
|
+
U.append(('C',ibit,max_index))
|
|
114
|
+
|
|
115
|
+
U.append(('R',max_index,-2.0*arra[j_code]))
|
|
116
|
+
|
|
117
|
+
for ibit in range(max_index-1,-1,-1):
|
|
118
|
+
if(arr_j[ibit]==1):
|
|
119
|
+
U.append(('C',ibit,max_index))
|
|
120
|
+
return U
|
|
121
|
+
|
|
122
|
+
#generate circuit from list of turple U
|
|
123
|
+
def cirq_from_U(U,Nqb):
|
|
124
|
+
nlen = len(U)
|
|
125
|
+
|
|
126
|
+
#the quantum circuit
|
|
127
|
+
qr = QuantumRegister(Nqb)
|
|
128
|
+
#cr = ClassicalRegister(1)
|
|
129
|
+
qc = QuantumCircuit(qr)
|
|
130
|
+
|
|
131
|
+
for i in range(nlen):
|
|
132
|
+
gate_str = U[i]
|
|
133
|
+
if(gate_str[0]=='C'):
|
|
134
|
+
qc.cx(gate_str[1],gate_str[2])
|
|
135
|
+
elif(gate_str[0]=='R'):
|
|
136
|
+
qc.rz(gate_str[2],gate_str[1])
|
|
137
|
+
else:
|
|
138
|
+
print('err in reading circuit')
|
|
139
|
+
return qc
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
#Optimize the quantum circuit
|
|
143
|
+
def optimize(U):
|
|
144
|
+
U0,iflag0 = scanI(U)
|
|
145
|
+
U0,iflag1 = scanRule2(U0)
|
|
146
|
+
|
|
147
|
+
#some possible exchange and check
|
|
148
|
+
i = 0
|
|
149
|
+
while(i<len(U0)-1):
|
|
150
|
+
|
|
151
|
+
if(U0[i+1][0] == 'C' and U0[i][0] =='C'):
|
|
152
|
+
|
|
153
|
+
ictrl = U0[i][1]; itarg = U0[i][2]
|
|
154
|
+
if(ictrl == U0[i+1][1] or itarg == U0[i+1][2]):
|
|
155
|
+
Utmp = U0.copy()
|
|
156
|
+
Utmp[i] = U0[i+1]
|
|
157
|
+
Utmp[i+1] = U0[i]
|
|
158
|
+
Utmp,iflag0 = scanI(Utmp)
|
|
159
|
+
Utmp,iflag1 = scanRule2(Utmp)
|
|
160
|
+
|
|
161
|
+
#if(iflag0 or iflag1): U0 = Utmp
|
|
162
|
+
U0 = Utmp
|
|
163
|
+
|
|
164
|
+
i += 1
|
|
165
|
+
return U0
|
|
166
|
+
|
|
167
|
+
#scan if there are identical adjacent CNOT gate
|
|
168
|
+
def scanI(U):
|
|
169
|
+
nlen = len(U)
|
|
170
|
+
newU = []
|
|
171
|
+
i=0
|
|
172
|
+
iflag = 0
|
|
173
|
+
while(i<nlen-1):
|
|
174
|
+
if(U[i]==U[i+1] and U[i][0] =='C'):
|
|
175
|
+
i += 2
|
|
176
|
+
iflag = 1
|
|
177
|
+
else:
|
|
178
|
+
newU.append(U[i])
|
|
179
|
+
i += 1
|
|
180
|
+
#end
|
|
181
|
+
if(i==nlen-1): newU.append(U[nlen-1])
|
|
182
|
+
return newU,iflag
|
|
183
|
+
|
|
184
|
+
#scan if there have 3 gate can reduced to:
|
|
185
|
+
#the target of one CNOT is the control of another.
|
|
186
|
+
#CijCjk = CjkCikCij
|
|
187
|
+
def scanRule2(U):
|
|
188
|
+
nlen = len(U)
|
|
189
|
+
newU = []
|
|
190
|
+
i=0
|
|
191
|
+
iflag = 0
|
|
192
|
+
while(i<nlen-2):
|
|
193
|
+
|
|
194
|
+
if(U[i][0] == U[i+1][0] == U[i+2][0] =='C'):
|
|
195
|
+
sj = U[i][1]
|
|
196
|
+
sk = U[i][2]
|
|
197
|
+
si = U[i+1][1]
|
|
198
|
+
if(sj == U[i+2][2] and si == U[i+2][1] and sk == U[i+1][2]):
|
|
199
|
+
i += 3
|
|
200
|
+
newU.append(('C',si,sj))
|
|
201
|
+
newU.append(('C',sj,sk))
|
|
202
|
+
print('Rule2')
|
|
203
|
+
iflag = 1
|
|
204
|
+
else:
|
|
205
|
+
newU.append(U[i])
|
|
206
|
+
i += 1
|
|
207
|
+
else:
|
|
208
|
+
newU.append(U[i])
|
|
209
|
+
i += 1
|
|
210
|
+
#end
|
|
211
|
+
if(i==nlen-2):
|
|
212
|
+
newU.append(U[nlen-2])
|
|
213
|
+
newU.append(U[nlen-1])
|
|
214
|
+
return newU,iflag
|
|
215
|
+
##=======end the Walsh operator scheme===========
|
|
216
|
+
|
|
217
|
+
##=======test functions for Walsh operator scheme=========
|
|
218
|
+
#extented matrix of the quantum gate
|
|
219
|
+
#note that this is the reverse order with qiskit convention
|
|
220
|
+
def ext_cx(ictrl,itarg,Nqb):
|
|
221
|
+
if(ictrl>=Nqb or itarg>=Nqb): print('err')
|
|
222
|
+
Proj00 = np.array([[1.0,0],[0,0]])
|
|
223
|
+
Proj11 = np.array([[0.0,0],[0,1]])
|
|
224
|
+
|
|
225
|
+
res00 = 1
|
|
226
|
+
res11 = 1
|
|
227
|
+
for i in range(Nqb-1,-1,-1):
|
|
228
|
+
#for i in range(Nqb):
|
|
229
|
+
if(i!=ictrl and i!=itarg):
|
|
230
|
+
res00 = np.kron(res00,I)
|
|
231
|
+
res11 = np.kron(res11,I)
|
|
232
|
+
if(i==ictrl):
|
|
233
|
+
res00 = np.kron(res00,Proj00)
|
|
234
|
+
res11 = np.kron(res11,Proj11)
|
|
235
|
+
if(i==itarg):
|
|
236
|
+
res00 = np.kron(res00,I)
|
|
237
|
+
res11 = np.kron(res11,X)
|
|
238
|
+
return res00+res11
|
|
239
|
+
|
|
240
|
+
def ext_Rz(phi,iqb,Nqb):
|
|
241
|
+
if(iqb>=Nqb): print('err')
|
|
242
|
+
mat_rz = np.array([[np.exp(-1j*phi/2),0],[0,np.exp(1j*phi/2)]])
|
|
243
|
+
res = 1
|
|
244
|
+
for i in range(Nqb-1,-1,-1):
|
|
245
|
+
#for i in range(Nqb):
|
|
246
|
+
if(i!=iqb):
|
|
247
|
+
res = np.kron(res,I)
|
|
248
|
+
if(i==iqb):
|
|
249
|
+
res = np.kron(res,mat_rz)
|
|
250
|
+
return res
|
|
251
|
+
|
|
252
|
+
def ext_Z(iqb,Nqb):
|
|
253
|
+
if(iqb>=Nqb): print('err')
|
|
254
|
+
res = 1
|
|
255
|
+
for i in range(Nqb-1,-1,-1):
|
|
256
|
+
#for i in range(Nqb):
|
|
257
|
+
if(i!=iqb):
|
|
258
|
+
res = np.kron(res,I)
|
|
259
|
+
if(i==iqb):
|
|
260
|
+
res = np.kron(res,Z)
|
|
261
|
+
return res
|
|
262
|
+
|
|
263
|
+
#generate matrix of quantum circuit from list of turple U
|
|
264
|
+
#convient for verify the result
|
|
265
|
+
def cirqmat_from_U(U,Nqb):
|
|
266
|
+
nlen = len(U)
|
|
267
|
+
|
|
268
|
+
Umat = []
|
|
269
|
+
|
|
270
|
+
for i in range(nlen):
|
|
271
|
+
gate_str = U[i]
|
|
272
|
+
if(gate_str[0]=='C'):
|
|
273
|
+
Umat.append(ext_cx(gate_str[1],gate_str[2],Nqb))
|
|
274
|
+
elif(gate_str[0]=='R'):
|
|
275
|
+
Umat.append(ext_Rz(gate_str[2],gate_str[1],Nqb))
|
|
276
|
+
else:
|
|
277
|
+
print('err in reading circuit')
|
|
278
|
+
|
|
279
|
+
#connect all the gates
|
|
280
|
+
res = Umat[-1]
|
|
281
|
+
for i in range(nlen-2,-1,-1):
|
|
282
|
+
res = res@Umat[i]
|
|
283
|
+
return res
|
|
284
|
+
|
|
285
|
+
#input an walsh coefficient (arra) of f_k array, output the circuit matrix for U=diag(e^(i f_k))
|
|
286
|
+
def cirqmat_walsh(arra,Nqb,epsilon):
|
|
287
|
+
if(len(arra)!=2**Nqb): print('err')
|
|
288
|
+
|
|
289
|
+
U = []
|
|
290
|
+
|
|
291
|
+
for j in range(1,2**Nqb):
|
|
292
|
+
if(abs(arra[j])<epsilon): continue
|
|
293
|
+
arr_j = binary(j,Nqb)
|
|
294
|
+
max_index = max_bit(arr_j) #the R gate will located at the max index
|
|
295
|
+
|
|
296
|
+
for ibit in range(max_index):
|
|
297
|
+
if(arr_j[ibit]==1):
|
|
298
|
+
U.append(ext_cx(ibit,max_index,Nqb))
|
|
299
|
+
|
|
300
|
+
U.append(ext_Rz(-2*arra[j],max_index,Nqb))
|
|
301
|
+
|
|
302
|
+
for ibit in range(max_index-1,-1,-1):
|
|
303
|
+
if(arr_j[ibit]==1):
|
|
304
|
+
U.append(ext_cx(ibit,max_index,Nqb))
|
|
305
|
+
|
|
306
|
+
#connect all the gates
|
|
307
|
+
res = U[-1]
|
|
308
|
+
for i in range(len(U)-2,-1,-1):
|
|
309
|
+
res = res@U[i]
|
|
310
|
+
return res
|
|
311
|
+
#============================
|
qflux/typing/__init__.py
ADDED
|
File without changes
|
qflux/typing/examples.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from numpy.typing import NDArray
|
|
5
|
+
|
|
6
|
+
# Basic numerical types
|
|
7
|
+
type Real = float | np.float64
|
|
8
|
+
type Complex = complex | np.complex128
|
|
9
|
+
|
|
10
|
+
# General vector and matrix types
|
|
11
|
+
type FloatVector = NDArray[np.float64] # Shape: (n,)
|
|
12
|
+
type ComplexVector = NDArray[np.complex128] # Shape: (n,)
|
|
13
|
+
type Float2D = NDArray[np.float64] # Shape: (n, m)
|
|
14
|
+
type Complex2D = NDArray[np.complex128] # Shape: (n, m)
|
|
15
|
+
|
|
16
|
+
type AtomicCoordinates = NDArray[np.float64] # Shape: (n_atoms, 3)
|
|
17
|
+
type AtomicNumbers = NDArray[np.int32] # Shape: (n_atoms,)
|
|
18
|
+
|
|
19
|
+
# Type for atomic symbols with validation
|
|
20
|
+
type AtomicSymbol = Annotated[str, "Valid chemical element symbol"]
|
|
21
|
+
|
|
22
|
+
# Energy types with units (keeping these as they're fundamental)
|
|
23
|
+
type Energy = Annotated[float, "Energy in Hartree"]
|
|
24
|
+
type EnergyEV = Annotated[float, "Energy in electron volts"]
|
qflux/utils/__init__.py
ADDED
|
File without changes
|
qflux/utils/io.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def load_dataset(ds_path: Path) -> None:
|
|
5
|
+
"""Loads a dataset from the specified path.
|
|
6
|
+
|
|
7
|
+
This function is intended to load a dataset from the given file path.
|
|
8
|
+
Currently, it is not implemented and always raises a NotImplementedError.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
ds_path: The path to the dataset file.
|
|
12
|
+
|
|
13
|
+
Raises:
|
|
14
|
+
NotImplementedError: Always raised as the function is not yet implemented.
|
|
15
|
+
"""
|
|
16
|
+
raise NotImplementedError("Not implemented")
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import logging.config
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import tomli
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def setup_logging() -> None:
|
|
10
|
+
"""Configures logging for the application.
|
|
11
|
+
|
|
12
|
+
Reads the logging configuration from the 'pyproject.toml' file
|
|
13
|
+
and applies it using `logging.config.dictConfig`.
|
|
14
|
+
|
|
15
|
+
The log level can be overridden by setting the
|
|
16
|
+
'QFLUX_LOG_LEVEL' environment variable. If the environment
|
|
17
|
+
variable is not set or set to an invalid level, it defaults to 'INFO'.
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
FileNotFoundError: If 'pyproject.toml' is not found.
|
|
21
|
+
tomli.TOMLDecodeError: If 'pyproject.toml' is not a valid TOML file.
|
|
22
|
+
KeyError: If the 'tool.logging' section is missing in 'pyproject.toml'.
|
|
23
|
+
"""
|
|
24
|
+
# Construct the path to pyproject.toml, assuming it's 3 levels up from this file.
|
|
25
|
+
pyproject_path = Path(__file__).parents[3] / "pyproject.toml"
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
with open(pyproject_path, "rb") as f:
|
|
29
|
+
config = tomli.load(f)
|
|
30
|
+
except FileNotFoundError as e:
|
|
31
|
+
raise FileNotFoundError(f"pyproject.toml not found at {pyproject_path}: {e}") from e
|
|
32
|
+
except tomli.TOMLDecodeError as e:
|
|
33
|
+
raise tomli.TOMLDecodeError(f"Failed to decode pyproject.toml: {e}") from e
|
|
34
|
+
|
|
35
|
+
# Determine the log level:
|
|
36
|
+
# 1. Check for QFLUX_LOG_LEVEL environment variable.
|
|
37
|
+
# 2. Default to 'INFO' if not set or invalid.
|
|
38
|
+
log_level_env = os.getenv("QFLUX_LOG_LEVEL", "INFO").upper()
|
|
39
|
+
|
|
40
|
+
valid_levels = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
|
|
41
|
+
if log_level_env not in valid_levels:
|
|
42
|
+
print(f"Invalid log level '{log_level_env}' from environment, defaulting to INFO")
|
|
43
|
+
log_level = "INFO"
|
|
44
|
+
else:
|
|
45
|
+
log_level = log_level_env
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
# Extract logging configuration from pyproject.toml
|
|
49
|
+
logging_config = config["tool"]["logging"]
|
|
50
|
+
except KeyError as e:
|
|
51
|
+
raise KeyError(f"'tool.logging' section not found in pyproject.toml: {e}") from e
|
|
52
|
+
|
|
53
|
+
# Override the log level specified in pyproject.toml with the determined log level.
|
|
54
|
+
logging_config["loggers"]["qflux"]["level"] = log_level
|
|
55
|
+
|
|
56
|
+
# Configure logging using the dictionary configuration.
|
|
57
|
+
logging.config.dictConfig(logging_config)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# Get the logger for the 'qflux' application.
|
|
61
|
+
logger = logging.getLogger("qflux")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# initialization
|
|
File without changes
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from itertools import combinations, product
|
|
3
|
+
from scipy.linalg import expm, kron
|
|
4
|
+
|
|
5
|
+
# Ansatz Class and Operators
|
|
6
|
+
|
|
7
|
+
class Ansatz_class:
|
|
8
|
+
def __init__(self, nqbit, u0, relrcut, pool, theta=[], ansatz=[]):
|
|
9
|
+
self.theta = np.array(theta)
|
|
10
|
+
self.A = ansatz
|
|
11
|
+
self.state = u0.copy()
|
|
12
|
+
self.ref = u0.copy()
|
|
13
|
+
self.relrcut = relrcut
|
|
14
|
+
self.nqbit = nqbit
|
|
15
|
+
self.pool = pool
|
|
16
|
+
|
|
17
|
+
class AnsatzOperatorBase:
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
class PauliOperator_class(AnsatzOperatorBase):
|
|
21
|
+
def __init__(self, mat, tag, nqbit):
|
|
22
|
+
self.mat = mat
|
|
23
|
+
self.tag = tag
|
|
24
|
+
self.nqbit = nqbit
|
|
25
|
+
|
|
26
|
+
def single_clause(ops, q_ind, weight, num_qubit):
|
|
27
|
+
si = np.eye(2)
|
|
28
|
+
res = weight * np.eye(1)
|
|
29
|
+
for i in range(1, num_qubit + 1):
|
|
30
|
+
if i in q_ind:
|
|
31
|
+
op2 = eval(f"{ops[q_ind.index(i)]}")
|
|
32
|
+
res = kron(res, op2)
|
|
33
|
+
else:
|
|
34
|
+
res = kron(res, si)
|
|
35
|
+
return res
|
|
36
|
+
|
|
37
|
+
def PauliOperator(ops, idx, w, nqbit):
|
|
38
|
+
sortedRep = sorted(zip(ops, idx), key=lambda x: x[1])
|
|
39
|
+
tag = ''.join([f"{o}{i}" for o, i in sortedRep])
|
|
40
|
+
mat = single_clause(ops, idx, w, nqbit)
|
|
41
|
+
return PauliOperator_class(mat, tag, nqbit)
|
|
42
|
+
|
|
43
|
+
# Defining pool of operators/gates
|
|
44
|
+
sx = np.array([[0, 1], [1, 0]])
|
|
45
|
+
sy = np.array([[0, -1j], [1j, 0]])
|
|
46
|
+
sz = np.array([[1, 0], [0, -1]])
|
|
47
|
+
# S = np.array([[1, 0], [0, 1j]])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def build_pool(nqbit):
|
|
51
|
+
pauliStr = ["sx", "sz", "sy"]
|
|
52
|
+
res = []
|
|
53
|
+
for order in range(1, nqbit+1):
|
|
54
|
+
for idx in combinations(range(1, nqbit + 1), order):
|
|
55
|
+
for op in product(pauliStr, repeat=order):
|
|
56
|
+
res.append(PauliOperator(op, list(idx), 1, nqbit))
|
|
57
|
+
return res
|
|
58
|
+
|
|
59
|
+
def Ansatz(u0, relrcut, theta=[], ansatz=[]):
|
|
60
|
+
nqbit = int(np.log2(len(u0)))
|
|
61
|
+
pool_qubit = nqbit
|
|
62
|
+
# u0 = np.outer(u0, u0).flatten()
|
|
63
|
+
pool = build_pool(pool_qubit)
|
|
64
|
+
return Ansatz_class(nqbit, u0, relrcut, pool, theta, ansatz)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from itertools import combinations, product
|
|
3
|
+
from scipy.linalg import expm, kron
|
|
4
|
+
|
|
5
|
+
# Ansatz Class and Operators
|
|
6
|
+
class Ansatz_class:
|
|
7
|
+
def __init__(self, nqbit, u0, relrcut, pool, theta=[], ansatz=[]):
|
|
8
|
+
self.theta = np.array(theta)
|
|
9
|
+
self.A = ansatz
|
|
10
|
+
self.state = u0.copy()
|
|
11
|
+
self.ref = u0.copy()
|
|
12
|
+
self.relrcut = relrcut
|
|
13
|
+
self.nqbit = nqbit
|
|
14
|
+
self.pool = pool
|
|
15
|
+
|
|
16
|
+
class AnsatzOperatorBase:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
class PauliOperator_class(AnsatzOperatorBase):
|
|
20
|
+
def __init__(self, mat, tag, nqbit):
|
|
21
|
+
self.mat = mat
|
|
22
|
+
self.tag = tag
|
|
23
|
+
self.nqbit = nqbit
|
|
24
|
+
|
|
25
|
+
def single_clause(ops, q_ind, weight, num_qubit):
|
|
26
|
+
si = np.eye(2)
|
|
27
|
+
res = weight * np.eye(1)
|
|
28
|
+
for i in range(1, num_qubit + 1):
|
|
29
|
+
if i in q_ind:
|
|
30
|
+
op2 = eval(f"{ops[q_ind.index(i)]}")
|
|
31
|
+
res = kron(res, op2)
|
|
32
|
+
else:
|
|
33
|
+
res = kron(res, si)
|
|
34
|
+
return res
|
|
35
|
+
|
|
36
|
+
def PauliOperator(ops, idx, w, nqbit):
|
|
37
|
+
sortedRep = sorted(zip(ops, idx), key=lambda x: x[1])
|
|
38
|
+
tag = ''.join([f"{o}{i}" for o, i in sortedRep])
|
|
39
|
+
mat = single_clause(ops, idx, w, nqbit)
|
|
40
|
+
return PauliOperator_class(mat, tag, nqbit)
|
|
41
|
+
|
|
42
|
+
# Defining pool of operators/gates
|
|
43
|
+
sx = np.array([[0, 1], [1, 0]])
|
|
44
|
+
sy = np.array([[0, -1j], [1j, 0]])
|
|
45
|
+
sz = np.array([[1, 0], [0, -1]])
|
|
46
|
+
|
|
47
|
+
def build_pool(nqbit):
|
|
48
|
+
pauliStr = ["sx", "sz", "sy"]
|
|
49
|
+
res = []
|
|
50
|
+
for order in range(1, nqbit+1):
|
|
51
|
+
for idx in combinations(range(1, nqbit + 1), order):
|
|
52
|
+
for op in product(pauliStr, repeat=order):
|
|
53
|
+
res.append(PauliOperator(op, list(idx), 1, nqbit))
|
|
54
|
+
return res
|
|
55
|
+
|
|
56
|
+
def Ansatz(u0, relrcut, theta=[], ansatz=[]):
|
|
57
|
+
nqbit = int(np.log2(len(u0)))
|
|
58
|
+
pool_qubit = 2 * nqbit
|
|
59
|
+
u0 = np.outer(u0, u0).flatten()
|
|
60
|
+
pool = build_pool(pool_qubit)
|
|
61
|
+
return Ansatz_class(nqbit, u0, relrcut, pool, theta, ansatz)
|