Skip to content

Commit 5697bdf

Browse files
build: merge master
2 parents b26cc4b + 9538523 commit 5697bdf

File tree

6 files changed

+103
-47
lines changed

6 files changed

+103
-47
lines changed

poetry.lock

+1-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+2-3
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,10 @@ scikit-learn = "^1.2.1"
6767
pytest-cov = "^4.0.0"
6868
pylint = "^3.1.0"
6969
matplotlib = "^3.7.0"
70-
qibojit = { git = "https://github.com/qiboteam/qibojit.git" }
71-
qibotn = { git = "https://github.com/qiboteam/qibotn.git" }
7270
tensorflow = { version = "^2.16.1", markers = "sys_platform == 'linux'" }
7371
torch = "^2.1.1"
72+
qibojit = { git = "https://github.com/qiboteam/qibojit.git" }
73+
qibotn = { git = "https://github.com/qiboteam/qibotn.git" }
7474
stim = "^1.12.0"
7575

7676
[tool.poe.tasks]
@@ -91,7 +91,6 @@ cuquantum-python-cu11 = "^23.3.0"
9191
qibojit = { git = "https://github.com/qiboteam/qibojit.git" }
9292
qibotn = { git = "https://github.com/qiboteam/qibotn.git" }
9393

94-
9594
[tool.poetry.group.cuda12]
9695
optional = true
9796

src/qibo/backends/_clifford_operations.py

+94-29
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from functools import reduce
1+
from functools import cache, reduce
22

33
import numpy as np
44
from scipy import sparse
@@ -329,7 +329,9 @@ def ECR(symplectic_matrix, control_q, target_q, nqubits):
329329
return X(symplectic_matrix, control_q, nqubits)
330330

331331

332-
def _exponent(x1, z1, x2, z2):
332+
def _exponent(
333+
x1: np.ndarray, z1: np.ndarray, x2: np.ndarray, z2: np.ndarray
334+
) -> np.ndarray:
333335
"""Helper function that computes the exponent to which i is raised for the product of the x and z paulis encoded in the symplectic matrix. This is used in _rowsum. The computation is performed parallely over the separated paulis x1[i], z1[i], x2[i] and z2[i].
334336
335337
Args:
@@ -341,13 +343,7 @@ def _exponent(x1, z1, x2, z2):
341343
Returns:
342344
(np.array): The calculated exponents.
343345
"""
344-
x1_ = x1.astype(np.int8)
345-
x2_ = x2.astype(np.int8)
346-
z1_ = z1.astype(np.int8)
347-
z2_ = z2.astype(np.int8)
348-
return (
349-
2 * (x1_ * x2_ * (z2_ - z1_) + z1_ * z2_ * (x1_ - x2_)) - x1_ * z2_ + x2_ * z1_
350-
)
346+
return 2 * (x1 * x2 * (z2 - z1) + z1 * z2 * (x1 - x2)) - x1 * z2 + x2 * z1
351347

352348

353349
def _rowsum(symplectic_matrix, h, i, nqubits, determined=False):
@@ -362,16 +358,16 @@ def _rowsum(symplectic_matrix, h, i, nqubits, determined=False):
362358
Returns:
363359
(np.array): The updated symplectic matrix.
364360
"""
365-
xi, xh = symplectic_matrix[i, :nqubits], symplectic_matrix[h, :nqubits]
366-
zi, zh = symplectic_matrix[i, nqubits:-1], symplectic_matrix[h, nqubits:-1]
361+
xi, zi = symplectic_matrix[i, :nqubits], symplectic_matrix[i, nqubits:-1]
362+
xh, zh = symplectic_matrix[h, :nqubits], symplectic_matrix[h, nqubits:-1]
367363
exponents = _exponent(xi, zi, xh, zh)
368364
ind = (
369365
2 * symplectic_matrix[h, -1]
370366
+ 2 * symplectic_matrix[i, -1]
371367
+ np.sum(exponents, axis=-1)
372368
) % 4 == 0
373-
r = np.ones(h.shape[0], dtype=bool)
374-
r[ind] = False
369+
r = np.ones(h.shape[0], dtype=np.uint8)
370+
r[ind] = 0
375371

376372
xi_xh = xi ^ xh
377373
zi_zh = zi ^ zh
@@ -390,57 +386,108 @@ def _rowsum(symplectic_matrix, h, i, nqubits, determined=False):
390386

391387

392388
def _determined_outcome(state, q, nqubits):
393-
state[-1, :] = False
389+
state[-1, :] = 0
394390
idx = (state[:nqubits, q].nonzero()[0] + nqubits).astype(np.uint)
391+
state = _pack_for_measurements(state, nqubits)
395392
state = _rowsum(
396393
state,
397394
2 * nqubits * np.ones(idx.shape, dtype=np.uint),
398-
idx.astype(np.uint),
399-
nqubits,
395+
idx,
396+
_packed_size(nqubits),
400397
True,
401398
)
402-
return state, np.uint(state[-1, -1])
399+
state = _unpack_for_measurements(state, nqubits)
400+
return state, state[-1, -1]
403401

404402

405403
def _random_outcome(state, p, q, nqubits):
406404
p = p[0] + nqubits
407405
tmp = state[p, q].copy()
408-
state[p, q] = False
406+
state[p, q] = 0
409407
h = state[:-1, q].nonzero()[0]
410408
state[p, q] = tmp
411409
if h.shape[0] > 0:
410+
state = _pack_for_measurements(state, nqubits)
412411
state = _rowsum(
413412
state,
414413
h.astype(np.uint),
415414
p * np.ones(h.shape[0], dtype=np.uint),
416-
nqubits,
415+
_packed_size(nqubits),
417416
False,
418417
)
418+
state = _unpack_for_measurements(state, nqubits)
419419
state[p - nqubits, :] = state[p, :]
420420
outcome = np.random.randint(2, size=1).item()
421-
state[p, :] = False
421+
state[p, :] = 0
422422
state[p, -1] = outcome
423-
state[p, nqubits + q] = True
423+
state[p, nqubits + q] = 1
424424
return state, outcome
425425

426426

427-
def _get_p(state, q, nqubits):
428-
return state[nqubits:-1, q].nonzero()[0]
427+
@cache
428+
def _dim(nqubits):
429+
"""Returns the dimension of the symplectic matrix for a given number of qubits."""
430+
return 2 * nqubits + 1
431+
432+
433+
@cache
434+
def _packed_size(n):
435+
"""Returns the size of an array of `n` booleans after packing."""
436+
return np.ceil(n / 8).astype(int)
437+
438+
439+
def _packbits(array, axis):
440+
return np.packbits(array, axis=axis)
441+
442+
443+
def _unpackbits(array, axis):
444+
return np.unpackbits(array, axis=axis)
445+
446+
447+
def _pack_for_measurements(state, nqubits):
448+
"""Prepares the state for measurements by packing the rows of the X and Z sections of the symplectic matrix."""
449+
r, x, z = _get_rxz(state, nqubits)
450+
x = _packbits(x, axis=1)
451+
z = _packbits(z, axis=1)
452+
return np.hstack((x, z, r[:, None]))
453+
454+
455+
@cache
456+
def _pad_size(n):
457+
"""Returns the size of the pad added to an array of original dimension `n` after unpacking."""
458+
return 8 - (n % 8)
459+
460+
461+
def _unpack_for_measurements(state, nqubits):
462+
"""Unpacks the symplectc matrix that was packed for measurements."""
463+
xz = _unpackbits(state[:, :-1], axis=1)
464+
padding_size = _pad_size(nqubits)
465+
x, z = xz[:, :nqubits], xz[:, nqubits + padding_size : -padding_size]
466+
return np.hstack((x, z, state[:, -1][:, None]))
467+
468+
469+
def _init_state_for_measurements(state, nqubits, collapse):
470+
if collapse:
471+
return _unpackbits(state, axis=0)[: _dim(nqubits)]
472+
else:
473+
return state.copy()
429474

430475

431476
# valid for a standard basis measurement only
432477
def M(state, qubits, nqubits, collapse=False):
433478
sample = []
434-
state_copy = state if collapse else state.copy()
479+
state = _init_state_for_measurements(state, nqubits, collapse)
435480
for q in qubits:
436-
p = _get_p(state_copy, q, nqubits)
481+
p = state[nqubits:-1, q].nonzero()[0]
437482
# random outcome, affects the state
438483
if len(p) > 0:
439-
state_copy, outcome = _random_outcome(state_copy, p, q, nqubits)
484+
state, outcome = _random_outcome(state, p, q, nqubits)
440485
# determined outcome, state unchanged
441486
else:
442-
state_copy, outcome = _determined_outcome(state_copy, q, nqubits)
487+
state, outcome = _determined_outcome(state, q, nqubits)
443488
sample.append(outcome)
489+
if collapse:
490+
state = _packbits(state, axis=0)
444491
return sample
445492

446493

@@ -455,10 +502,28 @@ def cast(x, dtype=None, copy=False):
455502

456503

457504
def _clifford_pre_execution_reshape(state):
458-
return state
505+
"""Reshape and packing applied to the symplectic matrix before execution to prepare the state in the form needed by each engine.
506+
507+
Args:
508+
state (np.array): Input state.
509+
510+
Returns:
511+
(np.array) The packed and reshaped state.
512+
"""
513+
return _packbits(state, axis=0)
459514

460515

461-
def _clifford_post_execution_reshape(state, nqubits):
516+
def _clifford_post_execution_reshape(state, nqubits: int):
517+
"""Reshape and unpacking applied to the state after execution to retrieve the standard symplectic matrix form.
518+
519+
Args:
520+
state (np.array): Input state.
521+
nqubits (int): Number of qubits.
522+
523+
Returns:
524+
(np.array) The unpacked and reshaped state.
525+
"""
526+
state = _unpackbits(state, axis=0)[: _dim(nqubits)]
462527
return state
463528

464529

src/qibo/backends/clifford.py

+3-9
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def _clifford_pre_execution_reshape(self, state):
119119
state
120120
)
121121

122-
def _clifford_post_execution_reshape(self, state, nqubits):
122+
def _clifford_post_execution_reshape(self, state, nqubits: int):
123123
"""Reshape the symplectic matrix to the shape needed by the engine after circuit execution.
124124
125125
Args:
@@ -294,17 +294,11 @@ def sample_shots(
294294
if isinstance(qubits, list):
295295
qubits = tuple(qubits)
296296

297-
state = self._clifford_pre_execution_reshape(state)
298-
299297
if collapse:
300-
samples = [
301-
self.engine.M(state, qubits, nqubits) for _ in range(nshots - 1)
302-
] # parallelize?
298+
samples = [self.engine.M(state, qubits, nqubits) for _ in range(nshots - 1)]
303299
samples.append(self.engine.M(state, qubits, nqubits, collapse))
304300
else:
305-
samples = [
306-
self.engine.M(state, qubits, nqubits) for _ in range(nshots)
307-
] # parallelize?
301+
samples = [self.engine.M(state, qubits, nqubits) for _ in range(nshots)]
308302

309303
return self.engine.cast(samples, dtype=int)
310304

src/qibo/gates/abstract.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ def apply_density_matrix(self, backend, state, nqubits):
396396
return backend.apply_gate_density_matrix(self, state, nqubits)
397397

398398
def apply_clifford(self, backend, state, nqubits):
399-
return backend.apply_gate_clifford(self, state[:-1], nqubits)
399+
return backend.apply_gate_clifford(self, state, nqubits)
400400

401401

402402
class SpecialGate(Gate):

tests/test_backends_clifford.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,10 @@ def test_collapsing_measurements(backend, seed):
212212
c2.add(gates.M(*range(3)))
213213

214214
clifford_bkd.set_seed(seed)
215-
clifford_res = clifford_bkd.execute_circuit(c1, nshots=100)
215+
clifford_res = clifford_bkd.execute_circuit(c1, nshots=1000)
216216

217217
numpy_bkd.set_seed(seed)
218-
numpy_res = numpy_bkd.execute_circuit(c2, nshots=100)
218+
numpy_res = numpy_bkd.execute_circuit(c2, nshots=1000)
219219

220220
backend.assert_allclose(
221221
clifford_res.probabilities(), backend.cast(numpy_res.probabilities()), atol=1e-1

0 commit comments

Comments
 (0)