Skip to content

Commit 4da731e

Browse files
authored
Fix Tests (#24)
* Fix generation of random tensors In some edge cases, `tnco.tests.utils.generate_random_tensors` is unable to get all the requested number of tensors at the first attempt. In the rare event this happens, the routine now discards the invalid set of tensors and try to create a new one * `tnco.tests` --> `tnco.testing` * Fix `pytest` fixtures
1 parent a79bc3a commit 4da731e

File tree

11 files changed

+354
-288
lines changed

11 files changed

+354
-288
lines changed

.github/workflows/run_tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ jobs:
7575
== '3.13.x'
7676
run: |
7777
pip install pytest-sugar
78-
PYTEST_FRACTION_N_TESTS=2 pytest --verbose --timeout=180 -n auto tests/*.py
78+
PYTEST_FRACTION_N_TESTS=0.2 pytest --verbose --timeout=180 -n auto tests/*.py
7979
8080
- name: Run CLI for ${{ matrix.os }}, with ${{ matrix.compiler }} and python==${{
8181
matrix.python-version }} (${{ matrix.target }})

examples/Optimization.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@
406406
],
407407
"source": [
408408
"# An arbitrary tensor tensor can be specified by using 'tnco.app.TensorNetwork'\n",
409-
"from tnco.tests.utils import generate_random_tensors\n",
409+
"from tnco.testing.utils import generate_random_tensors\n",
410410
"\n",
411411
"# Get a list of random indices for each tensor. The resulting tensor network\n",
412412
"# will have two disconnected components, 2 * 10 tensors (10 tensors for each\n",

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ testing = [
6767
"numpy",
6868
"ply",
6969
"pytest",
70+
"pytest-repeat",
7071
"pytest-timeout",
7172
"pytest-xdist",
7273
"qiskit",

tests/conftest.py

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,9 @@
1313
# limitations under the License.
1414

1515
import signal
16-
from os import environ
17-
from random import randrange
1816

1917
import pytest
2018

21-
# Global seed
22-
global_seed = None
23-
fraction_n_tests = 0.1
24-
25-
26-
def pytest_configure(config):
27-
# Randomize hash seed
28-
environ["PYTEST_SEED"] = environ.get("PYTEST_SEED", str(randrange(2**32)))
29-
30-
31-
def pytest_sessionstart(session):
32-
# Assign global seed
33-
global global_seed
34-
global_seed = environ["PYTEST_SEED"]
35-
print(f'seed: {global_seed}')
36-
37-
# Set maximum number of tests
38-
global fraction_n_tests
39-
fraction_n_tests = float(environ.get('PYTEST_FRACTION_N_TESTS', 10)) / 100
40-
print(f'fraction_n_tests: {fraction_n_tests * 100:1.0f}%')
41-
4219

4320
@pytest.fixture
4421
def timeout(request):

tests/test_app.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import json
1818
import pickle
1919
from decimal import Decimal
20+
from os import environ
2021
from random import Random
2122
from tempfile import NamedTemporaryFile
2223

@@ -26,24 +27,36 @@
2627
import pytest
2728
from cirq.contrib.qasm_import import circuit_from_qasm
2829
from quimb.tensor import Tensor, TensorNetwork
29-
30-
from conftest import fraction_n_tests, global_seed # Get global seed
3130
from tnco.app import Optimizer
3231
from tnco.app import Tensor as TS
3332
from tnco.app import TensorNetwork as TN
3433
from tnco.app import load_tn
35-
from tnco.tests.utils import generate_random_tensors
34+
from tnco.testing.utils import generate_random_tensors
35+
36+
# Initialize RNG
37+
rng = Random(
38+
environ.get('PYTEST_SEED') +
39+
environ.get('PYTEST_XDIST_WORKER') if 'PYTEST_SEED' in environ else None)
40+
41+
# Fix max number of repetitions
42+
max_repeat = max(1, float(environ.get('PYTEST_MAX_REPEAT', 'inf')))
43+
44+
# Fix ratio of number of tests
45+
fraction_n_tests = max(
46+
min(float(environ.get('PYTEST_FRACTION_N_TESTS', '1')), 1), 0)
47+
3648

37-
rng = Random(global_seed)
49+
def repeat(n: int):
50+
return pytest.mark.repeat(max(min(n * fraction_n_tests, max_repeat), 1))
3851

3952

40-
def sample_seeds(k, /):
41-
k = int(max(1, k * fraction_n_tests))
42-
return rng.sample(range(2**32), k=k)
53+
@pytest.fixture
54+
def random_seed():
55+
return rng.randrange(2**32)
4356

4457

45-
@pytest.mark.parametrize('seed', sample_seeds(200))
46-
def test_LoadTN_CirqCircuit(seed):
58+
@repeat(20)
59+
def test_LoadTN_CirqCircuit(random_seed):
4760

4861
def check_tn(U, tn):
4962
tn = TensorNetwork(map(Tensor, tn.arrays,
@@ -52,7 +65,7 @@ def check_tn(U, tn):
5265
np.testing.assert_allclose(U.ravel(), tn.data.ravel(), atol=1e-5)
5366

5467
# Get RNG
55-
rng = Random(seed)
68+
rng = Random(random_seed)
5669

5770
# Get random seed
5871
circuit_seed = rng.randrange(2**30)
@@ -99,8 +112,8 @@ def check_tn(U, tn):
99112
load_tn_(circuit.to_qasm()))
100113

101114

102-
@pytest.mark.parametrize('seed', sample_seeds(400))
103-
def test_OptimizeTN(seed, **kwargs):
115+
@repeat(40)
116+
def test_OptimizeTN(random_seed, **kwargs):
104117
# How to convert inds
105118
def convert_index(x):
106119
if isinstance(x, (str, int, frozenset)):
@@ -119,10 +132,10 @@ def to_tuple(x):
119132
return x
120133

121134
# Get rng
122-
rng = Random(seed)
135+
rng = Random(random_seed)
123136

124137
# Get numpy rng
125-
rng_np = np.random.default_rng(seed)
138+
rng_np = np.random.default_rng(random_seed)
126139

127140
# Initialize variables
128141
n_tensors = kwargs.get('n_tensors', rng.randint(5, 10))
@@ -164,7 +177,7 @@ def to_tuple(x):
164177
n_cc=n_cc,
165178
n_output_inds=n_output_inds,
166179
randomize_names=randomize_names,
167-
seed=seed)
180+
seed=random_seed)
168181

169182
# Get random dimensions
170183
dims = dict(

tests/test_circuit.py

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import itertools as its
1616
from math import sqrt
17+
from os import environ
1718
from random import Random
1819

1920
import cirq
@@ -23,20 +24,32 @@
2324
import qiskit
2425
from qiskit.circuit.random import random_circuit
2526
from quimb.tensor import Tensor, TensorNetwork
26-
27-
from conftest import fraction_n_tests, global_seed # Get global seed
2827
from tnco.utils.circuit import commute, load, same
2928

30-
rng = Random(global_seed)
29+
# Initialize RNG
30+
rng = Random(
31+
environ.get('PYTEST_SEED') +
32+
environ.get('PYTEST_XDIST_WORKER') if 'PYTEST_SEED' in environ else None)
33+
34+
# Fix max number of repetitions
35+
max_repeat = max(1, float(environ.get('PYTEST_MAX_REPEAT', 'inf')))
36+
37+
# Fix ratio of number of tests
38+
fraction_n_tests = max(
39+
min(float(environ.get('PYTEST_FRACTION_N_TESTS', '1')), 1), 0)
40+
41+
42+
def repeat(n: int):
43+
return pytest.mark.repeat(max(min(n * fraction_n_tests, max_repeat), 1))
3144

3245

33-
def sample_seeds(k, /):
34-
k = int(max(1, k * fraction_n_tests))
35-
return rng.sample(range(2**32), k=k)
46+
@pytest.fixture
47+
def random_seed():
48+
return rng.randrange(2**32)
3649

3750

38-
@pytest.mark.parametrize('seed', sample_seeds(200))
39-
def test_Commute(seed, **kwargs):
51+
@repeat(20)
52+
def test_Commute(random_seed, **kwargs):
4053
# Commutation using cirq
4154
def commute_(gate_A, gate_B, *, use_matrix_commutation=True, atol=1e-5):
4255
if use_matrix_commutation:
@@ -48,7 +61,7 @@ def commute_(gate_A, gate_B, *, use_matrix_commutation=True, atol=1e-5):
4861
return not frozenset(gate_A[1]) & frozenset(gate_B[1])
4962

5063
# Initialize RNG
51-
rng = Random(seed)
64+
rng = Random(random_seed)
5265

5366
# Set parameters
5467
n_qubits = kwargs.get('n_qubits', 5)
@@ -58,10 +71,9 @@ def commute_(gate_A, gate_B, *, use_matrix_commutation=True, atol=1e-5):
5871
circuit = tuple(
5972
map(
6073
lambda gate: (cirq.unitary(gate), gate.qubits),
61-
cirq.testing.random_circuit(n_qubits,
62-
n_moments,
63-
op_density=1,
64-
random_state=seed).all_operations()))
74+
cirq.testing.random_circuit(
75+
n_qubits, n_moments, op_density=1,
76+
random_state=random_seed).all_operations()))
6577

6678
# Check commutation
6779
assert all(
@@ -76,8 +88,8 @@ def commute_(gate_A, gate_B, *, use_matrix_commutation=True, atol=1e-5):
7688
zip(rng.choices(circuit, k=200), rng.choices(circuit, k=200))))
7789

7890

79-
@pytest.mark.parametrize('seed', sample_seeds(200))
80-
def test_Same(seed, **kwargs):
91+
@repeat(20)
92+
def test_Same(random_seed, **kwargs):
8193
# Same using quimb
8294
def same_(gate_A, gate_B, atol=1e-5):
8395
gate_A = Tensor(
@@ -92,7 +104,7 @@ def same_(gate_A, gate_B, atol=1e-5):
92104
gate_A.data, gate_B.transpose_like(gate_A).data, atol=1e-5)
93105

94106
# Initialize RNG
95-
rng = Random(seed)
107+
rng = Random(random_seed)
96108

97109
# Set parameters
98110
n_qubits = kwargs.get('n_qubits', 5)
@@ -102,10 +114,9 @@ def same_(gate_A, gate_B, atol=1e-5):
102114
circuit = tuple(
103115
map(
104116
lambda gate: (cirq.unitary(gate), gate.qubits),
105-
cirq.testing.random_circuit(n_qubits,
106-
n_moments,
107-
op_density=1,
108-
random_state=seed).all_operations()))
117+
cirq.testing.random_circuit(
118+
n_qubits, n_moments, op_density=1,
119+
random_state=random_seed).all_operations()))
109120

110121
# Check same
111122
assert all(
@@ -116,11 +127,11 @@ def same_(gate_A, gate_B, atol=1e-5):
116127
map(lambda g: same_(g, g) and same(g, g), rng.choices(circuit, k=200)))
117128

118129

119-
@pytest.mark.parametrize('seed', sample_seeds(200))
120-
def test_LoadArbitraryInitialFinalState(seed, **kwargs):
130+
@repeat(20)
131+
def test_LoadArbitraryInitialFinalState(random_seed, **kwargs):
121132
# Get rng
122-
rng = Random(seed)
123-
np_rng = np.random.default_rng(seed)
133+
rng = Random(random_seed)
134+
np_rng = np.random.default_rng(random_seed)
124135

125136
# Get parameters
126137
n_qubits = kwargs.get('n_qubits', rng.randint(5, 10))
@@ -151,7 +162,7 @@ def random_qubit():
151162
circuit = cirq.testing.random_circuit(n_qubits,
152163
n_moments,
153164
op_density=op_density,
154-
random_state=seed)
165+
random_state=random_seed)
155166
else:
156167
circuit = cirq.testing.random_circuit(n_qubits,
157168
n_moments,
@@ -163,7 +174,7 @@ def random_qubit():
163174
cirq.CCZPowGate(): 3,
164175
cirq.ISWAP: 2
165176
},
166-
random_state=seed)
177+
random_state=random_seed)
167178
circuit = map((cirq.H**0.5).on, circuit.all_qubits()) + circuit + map(
168179
(cirq.H**0.5).on, circuit.all_qubits())
169180

@@ -188,7 +199,7 @@ def random_qubit():
188199
decompose_hyper_inds=decompose_hyper_inds,
189200
fuse=fuse,
190201
use_matrix_commutation=use_matrix_commutation,
191-
seed=seed)
202+
seed=random_seed)
192203

193204
def get_state(x):
194205
valid_token = {
@@ -243,10 +254,10 @@ def get_state(x):
243254
np.testing.assert_allclose(ex, ts, atol=1e-5)
244255

245256

246-
@pytest.mark.parametrize('seed', sample_seeds(200))
247-
def test_LoadUnitary(seed, **kwargs):
257+
@repeat(20)
258+
def test_LoadUnitary(random_seed, **kwargs):
248259
# Get rng
249-
rng = Random(seed)
260+
rng = Random(random_seed)
250261

251262
# Get parameters
252263
n_qubits = kwargs.get('n_qubits', rng.randint(5, 10))
@@ -265,7 +276,7 @@ def test_LoadUnitary(seed, **kwargs):
265276
circuit = cirq.testing.random_circuit(n_qubits,
266277
n_moments,
267278
op_density=op_density,
268-
random_state=seed)
279+
random_state=random_seed)
269280
else:
270281
circuit = cirq.testing.random_circuit(n_qubits,
271282
n_moments,
@@ -277,7 +288,7 @@ def test_LoadUnitary(seed, **kwargs):
277288
cirq.CCZPowGate(): 3,
278289
cirq.ISWAP: 2
279290
},
280-
random_state=seed)
291+
random_state=random_seed)
281292
circuit = map((cirq.H**0.5).on, circuit.all_qubits()) + circuit + map(
282293
(cirq.H**0.5).on, circuit.all_qubits())
283294

@@ -291,7 +302,7 @@ def test_LoadUnitary(seed, **kwargs):
291302
decompose_hyper_inds=decompose_hyper_inds,
292303
fuse=fuse,
293304
use_matrix_commutation=use_matrix_commutation,
294-
seed=seed)
305+
seed=random_seed)
295306

296307
# Get exact unitary
297308
U = cirq.unitary(circuit)
@@ -337,8 +348,8 @@ def test_LoadUnitary(seed, **kwargs):
337348
assert not arrays and not ts_inds and not output_inds
338349

339350

340-
@pytest.mark.parametrize('seed', sample_seeds(200))
341-
def test_LoadQisKit(seed):
351+
@repeat(20)
352+
def test_LoadQisKit(random_seed):
342353
# Set number of qubits and depth of the circuit
343354
n_qubits = 6
344355
circuit_depth = 12
@@ -350,8 +361,8 @@ def test_LoadQisKit(seed):
350361
# Get a random circuit
351362
circuit = qiskit.QuantumCircuit(n_qubits)
352363
mit.consume(map(lambda i: circuit.append(sqrt_H, [i]), range(n_qubits)))
353-
circuit = circuit.compose(random_circuit(n_qubits, circuit_depth,
354-
seed=seed))
364+
circuit = circuit.compose(
365+
random_circuit(n_qubits, circuit_depth, seed=random_seed))
355366
mit.consume(map(lambda i: circuit.append(sqrt_H, [i]), range(n_qubits)))
356367

357368
# Get unitary

0 commit comments

Comments
 (0)