• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

feihoo87 / waveforms / 6893865468

16 Nov 2023 04:49PM UTC coverage: 43.147% (+0.7%) from 42.467%
6893865468

push

github

feihoo87
update

7 of 17 new or added lines in 4 files covered. (41.18%)

588 existing lines in 10 files now uncovered.

7436 of 17234 relevant lines covered (43.15%)

3.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.0
/waveforms/qlisp/simulator/simple.py
1
import itertools
9✔
2
import re
9✔
3
from functools import partial, reduce
9✔
4

5
import numpy as np
9✔
6

7
from waveforms.math.matricies import (CR, CX, CZ, SWAP, H, S, Sdag, SQiSWAP, T,
9✔
8
                                      Tdag, U, fSim, iSWAP, make_immutable,
9
                                      rfUnitary, sigmaI, sigmaX, sigmaY,
10
                                      sigmaZ)
11

12
_matrix_of_gates = {}
9✔
13
_clifford_groups = {}
9✔
14

15

16
def regesterGateMatrix(gate, mat, N=None, docs=''):
9✔
17
    if isinstance(mat, np.ndarray):
9✔
18
        mat = make_immutable(mat)
9✔
19
    if N is None:
9✔
20
        N = round(np.log2(mat.shape[0]))
9✔
21
    _matrix_of_gates[gate] = (mat, N, docs)
9✔
22

23

24
def gate_name(gate):
9✔
25
    if isinstance(gate, tuple):
9✔
26
        return gate_name(gate[0])
9✔
27
    elif isinstance(gate, str):
9✔
28
        return gate
9✔
29
    else:
UNCOV
30
        raise ValueError(f'Unexcept gate {gate}')
×
31

32

33
def clifford_gate(gate: str):
9✔
34
    match = re.match(r'^C(\d+)_(\d+)$', gate)
×
35
    if match:
×
UNCOV
36
        N, i = [int(num) for num in match.groups()]
×
UNCOV
37
        return N, i
×
38
    else:
UNCOV
39
        return None
×
40

41

42
def gate2mat(gate):
9✔
43
    if isinstance(gate, str) and gate in _matrix_of_gates:
9✔
44
        if callable(_matrix_of_gates[gate][0]):
9✔
45
            return _matrix_of_gates[gate][0](), _matrix_of_gates[gate][1]
×
46
        else:
47
            return _matrix_of_gates[gate][:2]
9✔
48
    elif isinstance(gate, tuple) and gate[0] in _matrix_of_gates:
9✔
49
        if callable(_matrix_of_gates[gate[0]][0]):
9✔
50
            return _matrix_of_gates[gate[0]][0](
9✔
51
                *gate[1:]), _matrix_of_gates[gate[0]][1]
52
        else:
UNCOV
53
            raise ValueError(
×
54
                f"Could not call {gate[0]}(*{gate[1:]}), `{gate[0]}` is not callable."
55
            )
UNCOV
56
    elif clifford_gate(gate):
×
UNCOV
57
        N, i = clifford_gate(gate)
×
UNCOV
58
        if N == 1:
×
NEW
59
            from waveforms.math.group.clifford.funtions import \
×
60
                one_qubit_clifford_matricies
NEW
61
            return one_qubit_clifford_matricies[i], N
×
UNCOV
62
        elif N == 2:
×
NEW
63
            from waveforms.math.group.clifford.funtions import \
×
64
                two_qubit_clifford_matricies
NEW
65
            return two_qubit_clifford_matricies[i], N
×
66
        else:
NEW
67
            from waveforms.math.group import CliffordGroup
×
68

NEW
69
            if N not in _clifford_groups:
×
NEW
70
                _clifford_groups[N] = CliffordGroup(N)
×
NEW
71
            perm = _clifford_groups[N][i]
×
NEW
72
            return _clifford_groups[N].permutation_to_matrix(perm), N
×
UNCOV
73
    elif gate_name(gate) == 'C':
×
UNCOV
74
        U, N = gate2mat(gate[1])
×
UNCOV
75
        ret = np.eye(2 * U.shape[0], dtype=complex)
×
UNCOV
76
        ret[U.shape[0]:, U.shape[0]:] = U
×
UNCOV
77
        return ret, N + 1
×
78
    else:
UNCOV
79
        raise ValueError(f'Unexcept gate {gate}')
×
80

81

82
def splite_at(l, bits):
9✔
83
    """将 l 的二进制位于 bits 所列的位置上断开插上0
84
    
85
    如 splite_at(int('1111',2), [0,2,4,6]) == int('10101010', 2)
86
    bits 必须从小到大排列
87
    """
88
    r = l
9✔
89
    for n in bits:
9✔
90
        mask = (1 << n) - 1
9✔
91
        low = r & mask
9✔
92
        high = r - low
9✔
93
        r = (high << 1) + low
9✔
94
    return r
9✔
95

96

97
def place_at(l, bits):
9✔
98
    """将 l 的二进制位置于 bits 所列的位置上
99
    
100
    如 place_at(int('10111',2), [0,2,4,5,6]) == int('01010101', 2)
101
    """
102
    r = 0
9✔
103
    for index, n in enumerate(bits):
9✔
104
        b = (l >> index) & 1
9✔
105
        r += b << n
9✔
106
    return r
9✔
107

108

109
def reduceSubspace(targets, N, inputMat, func, args):
9✔
110
    innerDim = 2**len(targets)
9✔
111
    outerDim = 2**(N - len(targets))
9✔
112

113
    targets = tuple(reversed([N - i - 1 for i in targets]))
9✔
114

115
    def index(targets, i, j):
9✔
116
        return splite_at(j, sorted(targets)) | place_at(i, targets)
9✔
117

118
    if len(inputMat.shape) == 1:
9✔
119
        for k in range(outerDim):
9✔
120
            innerIndex = [index(targets, i, k) for i in range(innerDim)]
9✔
121
            inputMat[innerIndex] = func(inputMat[innerIndex], *args)
9✔
122
    else:
123
        for k, l in itertools.product(range(outerDim), repeat=2):
9✔
124
            innerIndex = np.asarray(
9✔
125
                [[index(targets, i, k),
126
                  index(targets, j, l)]
127
                 for i, j in itertools.product(range(innerDim), repeat=2)]).T
128
            sub = inputMat[innerIndex[0], innerIndex[1]].reshape(
9✔
129
                (innerDim, innerDim))
130
            inputMat[innerIndex[0], innerIndex[1]] = func(sub, *args).flatten()
9✔
131
    return inputMat
9✔
132

133

134
def _apply_gate(gate, inputMat, unitary_process, qubits, N):
9✔
135
    U, n = gate2mat(gate)
9✔
136
    if len(qubits) == n and all(isinstance(qubit, int) for qubit in qubits):
9✔
137
        reduceSubspace(qubits, N, inputMat, unitary_process, (U, ))
9✔
UNCOV
138
    elif n == 1 and all(isinstance(qubit, int) for qubit in qubits):
×
UNCOV
139
        for qubit in qubits:
×
UNCOV
140
            reduceSubspace([qubit], N, inputMat, unitary_process, (U, ))
×
UNCOV
141
    elif len(qubits) == n and all(
×
142
            isinstance(qubit, [tuple, list]) for qubit in qubits):
143
        for qubit_tuple in zip(*qubits):
×
144
            reduceSubspace(qubit_tuple, N, inputMat, unitary_process, (U, ))
×
145
    else:
UNCOV
146
        raise ValueError(f'Unexcept gate {gate} and qubits {qubits}')
×
147

148

149
def _measure_process(rho):
9✔
UNCOV
150
    return np.array([[rho[0, 0], 0], [0, rho[1, 1]]])
×
151

152

153
def _reset_process(rho, p1):
9✔
154
    s0 = np.array([[rho[0, 0] + rho[1, 1], 0], [0, 0]])
×
155
    s1 = np.array([[0, 0], [0, rho[0, 0] + rho[1, 1]]])
×
UNCOV
156
    return (1 - p1) * s0 + p1 * s1
×
157

158

159
def _decohherence_process(rho, Gamma_t, gamma_t):
9✔
160
    rho00 = rho[0, 0] + rho[1, 1] * (1 - np.exp(-Gamma_t))
×
161
    rho11 = rho[1, 1] * np.exp(-Gamma_t)
×
162
    rho01 = rho[0, 1] * np.exp(-Gamma_t / 2 - gamma_t**2)
×
163
    rho10 = rho[1, 0] * np.exp(-Gamma_t / 2 - gamma_t**2)
×
164
    return np.array([[rho00, rho01], [rho10, rho11]])
×
165

166

167
def applySeq(seq, psi0=None):
9✔
168

169
    def _set_vector_to_rho(psi):
9✔
170
        psi = psi.reshape(-1, 1).conj() @ psi.reshape(1, -1)
×
UNCOV
171
        unitary_process = lambda psi, U: U @ psi @ U.T.conj()
×
172
        return psi, unitary_process, np.array([[1, 0], [0, 0]])
×
173

174
    if psi0 is None:
9✔
175
        psi = np.array([1, 0], dtype=complex)
9✔
176
        N = 1
9✔
177
    else:
178
        psi = psi0
×
179
        N = round(np.log2(psi.shape[0]))
×
180

181
    psi0 = np.array([1, 0])
9✔
182
    unitary_process = lambda psi, U: U @ psi
9✔
183

184
    if psi.ndim == 2:
9✔
185
        psi, unitary_process, psi0 = _set_vector_to_rho(psi)
×
186

187
    for gate, *qubits in seq:
9✔
188
        if len(qubits) == 1 and isinstance(qubits[0], tuple):
9✔
189
            qubits = qubits[0]
×
190
        M = max(qubits)
9✔
191
        if M >= N:
9✔
192
            psi = reduce(np.kron, itertools.repeat(psi0, times=M - N + 1), psi)
9✔
193
            N = M + 1
9✔
194

195
        if gate_name(gate) in ['Barrier']:
9✔
196
            continue
×
197
        if gate_name(gate) in ['Delay']:
9✔
198
            if len(gate) == 2:
×
UNCOV
199
                continue
×
200
            else:
UNCOV
201
                if len(gate) == 3:
×
UNCOV
202
                    _, t, T1 = gate
×
UNCOV
203
                    Gamma_t = t / T1
×
UNCOV
204
                    gamma_t = 0
×
205
                else:
UNCOV
206
                    _, t, T1, Tphi = gate
×
207
                    Gamma_t = t / T1
×
UNCOV
208
                    gamma_t = t / Tphi
×
209
        if gate_name(gate) in ['Reset']:
9✔
UNCOV
210
            if isinstance(gate, tuple) and len(gate) == 2:
×
UNCOV
211
                _, p1 = gate
×
212
            else:
UNCOV
213
                p1 = 0.0
×
214
        if gate_name(gate) in ['Measure', 'Reset', 'Delay'] and psi.ndim == 1:
9✔
UNCOV
215
            psi, unitary_process, psi0 = _set_vector_to_rho(psi)
×
216

217
        if gate_name(gate) == 'Measure':
9✔
UNCOV
218
            reduceSubspace(qubits, N, psi, _measure_process, ())
×
219
        elif gate_name(gate) == 'Reset':
9✔
UNCOV
220
            reduceSubspace(qubits, N, psi, _reset_process, (p1, ))
×
221
        elif gate_name(gate) == 'Delay':
9✔
UNCOV
222
            reduceSubspace(qubits, N, psi, _decohherence_process,
×
223
                           (Gamma_t, gamma_t))
224
        else:
225
            _apply_gate(gate, psi, unitary_process, qubits, N)
9✔
226

227
    return psi
9✔
228

229

230
def seq2mat(seq, U=None):
9✔
231
    I = np.eye(2, dtype=complex)
9✔
232
    if U is None:
9✔
233
        U = np.eye(2, dtype=complex)
9✔
234
        N = 1
9✔
235
    else:
UNCOV
236
        N = round(np.log2(U.shape[0]))
×
237

238
    unitary_process = lambda U0, U: U @ U0
9✔
239

240
    for gate, *qubits in seq:
9✔
241
        if len(qubits) == 1 and isinstance(qubits[0], tuple):
9✔
UNCOV
242
            qubits = qubits[0]
×
243
        M = max(qubits)
9✔
244
        if M >= N:
9✔
245
            U = reduce(np.kron, itertools.repeat(I, times=M - N + 1), U)
9✔
246
            N = M + 1
9✔
247
        if gate_name(gate) in ['Delay', 'Barrier']:
9✔
UNCOV
248
            continue
×
249
        if gate_name(gate) in ['Measure', 'Reset']:
9✔
UNCOV
250
            raise ValueError(
×
251
                'Measure and Reset must be applied to a state vector')
252
        else:
253
            _apply_gate(gate, U, unitary_process, qubits, N)
9✔
254
    return U
9✔
255

256

257
regesterGateMatrix('U', U, 1)
9✔
258
regesterGateMatrix('u1', lambda p: U(theta=0, phi=0, lambda_=p), 1)
9✔
259
regesterGateMatrix('u2', lambda phi, lam: U(np.pi / 2, phi, lam), 1)
9✔
260
regesterGateMatrix('u3', U, 1)
9✔
261
regesterGateMatrix('P', lambda p=np.pi / 2: U(theta=0, phi=0, lambda_=p), 1)
9✔
262
regesterGateMatrix('rfUnitary', rfUnitary, 1)
9✔
263
regesterGateMatrix('R', lambda phi: rfUnitary(np.pi / 2, phi), 1)
9✔
264
regesterGateMatrix('Rx', partial(rfUnitary, phi=0), 1)
9✔
265
regesterGateMatrix('Ry', partial(rfUnitary, phi=np.pi / 2), 1)
9✔
266
regesterGateMatrix('Rz', lambda p: U(theta=0, phi=0, lambda_=p), 1)
9✔
267
regesterGateMatrix('fSim', fSim, 2)
9✔
268
regesterGateMatrix('Cphase', lambda phi: fSim(theta=0, phi=phi), 2)
9✔
269

270
# one qubit
271
regesterGateMatrix('I', sigmaI())
9✔
272
regesterGateMatrix('X', -1j * sigmaX())
9✔
273
regesterGateMatrix('Y', -1j * sigmaY())
9✔
274
regesterGateMatrix('X/2', np.array([[1, -1j], [-1j, 1]]) / np.sqrt(2))
9✔
275
regesterGateMatrix('Y/2', np.array([[1, -1], [1, 1]]) / np.sqrt(2))
9✔
276
regesterGateMatrix('-X/2', np.array([[1, 1j], [1j, 1]]) / np.sqrt(2))
9✔
277
regesterGateMatrix('-Y/2', np.array([[1, 1], [-1, 1]]) / np.sqrt(2))
9✔
278
regesterGateMatrix('Z', sigmaZ())
9✔
279
regesterGateMatrix('S', S)
9✔
280
regesterGateMatrix('-S', Sdag)
9✔
281
regesterGateMatrix('H', H)
9✔
282

283
# non-clifford
284
regesterGateMatrix('T', T)
9✔
285
regesterGateMatrix('-T', Tdag)
9✔
286
regesterGateMatrix('W/2', rfUnitary(np.pi / 2, np.pi / 4))
9✔
287
regesterGateMatrix('-W/2', rfUnitary(-np.pi / 2, np.pi / 4))
9✔
288
regesterGateMatrix('V/2', rfUnitary(np.pi / 2, 3 * np.pi / 4))
9✔
289
regesterGateMatrix('-V/2', rfUnitary(-np.pi / 2, 3 * np.pi / 4))
9✔
290

291
# two qubits
292
regesterGateMatrix('CZ', CZ)
9✔
293
regesterGateMatrix('Cnot', CX)
9✔
294
regesterGateMatrix('CX', CX)
9✔
295
regesterGateMatrix('iSWAP', iSWAP)
9✔
296
regesterGateMatrix('SWAP', SWAP)
9✔
297
regesterGateMatrix('CR', CR)
9✔
298

299
# non-clifford
300
regesterGateMatrix('SQiSWAP', SQiSWAP)
9✔
301

302
if __name__ == '__main__':
9✔
303
    # Porter-Thomas distribution
304

UNCOV
305
    def randomSeq(depth, N):
×
306
        seq = []
×
307
        for i in range(depth):
×
UNCOV
308
            for j in range(N):
×
309
                seq.append((np.random.choice(['X/2', 'Y/2', 'W/2']), j))
×
UNCOV
310
            for j in range(i % 2, N, 2):
×
311
                seq.append(('SQiSWAP', j, (j + 1) % N))
×
312
        return seq
×
313

UNCOV
314
    p = []
×
315
    # run 1000 random circuit on 6 qubits
UNCOV
316
    for i in range(1000):
×
UNCOV
317
        print('    ', i, end='')
×
UNCOV
318
        seq = randomSeq(50, 6)
×
UNCOV
319
        psi = applySeq(seq)
×
UNCOV
320
        p.extend(list(np.abs(psi)**2))
×
UNCOV
321
        print('    ', i)
×
UNCOV
322
    p = np.asarray(p)
×
323

324
    # plot distribution of probabilities
UNCOV
325
    N = 2**6
×
UNCOV
326
    y, x = np.histogram(N * p, bins=50, density=True)
×
327

UNCOV
328
    import matplotlib.pyplot as plt
×
329

UNCOV
330
    plt.semilogy((x[:-1] + x[1:]) / 2, y)
×
UNCOV
331
    plt.show()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc