9
9
import fast_pauli .pypauli .operations as pp
10
10
11
11
# TODO add a separate benchmark for get_sparse_repr vs compose_sparse_pauli
12
+ # TODO control numpy threading in a fixture for fair comparison
12
13
13
14
14
15
@pytest .fixture
15
16
def all_strings_for_qubits () -> list [str ]:
16
17
"""Provide sample strings for testing."""
17
- return lambda q : list (map ("" .join , it .product ("IXYZ" , repeat = q ))) # type: ignore
18
+
19
+ def generate_paulis (qubits : int , limit : int = 1_000 ) -> list [str ]:
20
+ strings : list [str ] = []
21
+ for s in it .product ("IXYZ" , repeat = qubits ):
22
+ if limit and len (strings ) > limit :
23
+ break
24
+ strings .append ("" .join (s ))
25
+ return strings
26
+
27
+ return generate_paulis # type: ignore
28
+
29
+
30
+ @pytest .fixture (scope = "function" )
31
+ def generate_random_complex (rng_seed : int = 321 ) -> np .ndarray :
32
+ """Generate random complex numpy array with desired shape."""
33
+ rng = np .random .default_rng (rng_seed )
34
+ return lambda * shape : rng .random (shape ) + 1j * rng .random (shape )
35
+
36
+
37
+ QUBITS_TO_BENCHMARK = [1 , 2 , 4 , 10 ]
18
38
19
39
20
40
# following two helper functions are going to be removed once we align interfaces:
@@ -31,22 +51,29 @@ def benchmark_dense_conversion_py(paulis: list) -> None:
31
51
32
52
33
53
@pytest .mark .parametrize (
34
- "lang ,qubits,pauli_class ,bench_func" ,
54
+ "pauli_class ,qubits,lang ,bench_func" ,
35
55
it .chain (
36
- [("cpp" , q , fp .PauliString , benchmark_dense_conversion_cpp ) for q in [1 , 2 , 4 ]],
37
- [("py" , q , pp .PauliString , benchmark_dense_conversion_py ) for q in [1 , 2 , 4 ]],
56
+ [
57
+ (fp .PauliString , q , "cpp" , benchmark_dense_conversion_cpp )
58
+ for q in QUBITS_TO_BENCHMARK
59
+ ],
60
+ [
61
+ (pp .PauliString , q , "py" , benchmark_dense_conversion_py )
62
+ for q in QUBITS_TO_BENCHMARK
63
+ ],
38
64
),
39
65
)
40
66
def test_dense_conversion_n_qubits ( # type: ignore[no-untyped-def]
41
- benchmark , all_strings_for_qubits , lang , qubits , pauli_class , bench_func
67
+ benchmark , all_strings_for_qubits , pauli_class , qubits , lang , bench_func
42
68
) -> None :
43
69
"""Benchmark dense conversion.
44
70
45
71
Parametrized test case to run the benchmark across
46
72
all Pauli strings of given length for given PauliString class.
47
73
"""
74
+ n_strings_limit = 10 if qubits > 4 else None
48
75
prepared_paulis = list (
49
- map (lambda s : pauli_class (s ), all_strings_for_qubits (qubits ))
76
+ map (lambda s : pauli_class (s ), all_strings_for_qubits (qubits , n_strings_limit ))
50
77
)
51
78
benchmark (bench_func , paulis = prepared_paulis )
52
79
@@ -64,27 +91,35 @@ def benchmark_apply_py(paulis: list, states: list) -> None:
64
91
65
92
66
93
@pytest .mark .parametrize (
67
- "lang ,qubits,pauli_class ,bench_func" ,
94
+ "pauli_class ,qubits,lang ,bench_func" ,
68
95
it .chain (
69
- [("cpp" , q , fp . PauliString , benchmark_apply_cpp ) for q in [ 1 , 2 , 4 ] ],
70
- [("py" , q , pp . PauliString , benchmark_apply_py ) for q in [ 1 , 2 , 4 ] ],
96
+ [(fp . PauliString , q , "cpp" , benchmark_apply_cpp ) for q in QUBITS_TO_BENCHMARK ],
97
+ [(pp . PauliString , q , "py" , benchmark_apply_py ) for q in QUBITS_TO_BENCHMARK ],
71
98
),
72
99
)
73
100
def test_apply_n_qubits ( # type: ignore[no-untyped-def]
74
- benchmark , all_strings_for_qubits , lang , qubits , pauli_class , bench_func
101
+ benchmark ,
102
+ all_strings_for_qubits ,
103
+ generate_random_complex ,
104
+ pauli_class ,
105
+ qubits ,
106
+ lang ,
107
+ bench_func ,
75
108
) -> None :
76
109
"""Benchmark PauliString multiplication with provided state vector.
77
110
78
111
Parametrized test case to run the benchmark across
79
112
all Pauli strings of given length for given PauliString class.
80
113
"""
81
- rng = np . random . default_rng ( 321 )
82
- dim = 1 << qubits
114
+ n_dims = 1 << qubits
115
+ n_strings_limit = 10 if qubits > 4 else None
83
116
84
117
prepared_paulis = list (
85
- map (lambda s : pauli_class (s ), all_strings_for_qubits (qubits ))
118
+ map (lambda s : pauli_class (s ), all_strings_for_qubits (qubits , n_strings_limit ))
86
119
)
87
- prepared_states = [rng .random (dim ) for _ in range (len (prepared_paulis ))]
120
+ prepared_states = [
121
+ generate_random_complex (n_dims ) for _ in range (len (prepared_paulis ))
122
+ ]
88
123
89
124
benchmark (bench_func , paulis = prepared_paulis , states = prepared_states )
90
125
@@ -102,38 +137,47 @@ def benchmark_apply_batch_py(paulis: list, states: list) -> None:
102
137
103
138
104
139
@pytest .mark .parametrize (
105
- "lang ,qubits,states,pauli_class ,bench_func" ,
140
+ "pauli_class ,qubits,states,lang ,bench_func" ,
106
141
it .chain (
107
142
[
108
- ("cpp" , q , n , fp . PauliString , benchmark_apply_batch_cpp )
109
- for q in [ 1 , 2 , 4 ]
143
+ (fp . PauliString , q , n , "cpp" , benchmark_apply_batch_cpp )
144
+ for q in QUBITS_TO_BENCHMARK
110
145
for n in [16 , 128 ]
111
146
],
112
147
[
113
- ("py" , q , n , pp . PauliString , benchmark_apply_batch_py )
114
- for q in [ 1 , 2 , 4 ]
148
+ (pp . PauliString , q , n , "py" , benchmark_apply_batch_py )
149
+ for q in QUBITS_TO_BENCHMARK
115
150
for n in [16 , 128 ]
116
151
],
117
152
),
118
153
)
119
- def test_apply_batch_n_qubits ( # type: ignore[no-untyped-def]
120
- benchmark , all_strings_for_qubits , lang , qubits , states , pauli_class , bench_func
154
+ def test_apply_batch_n_qubits_n_states ( # type: ignore[no-untyped-def]
155
+ benchmark ,
156
+ all_strings_for_qubits ,
157
+ generate_random_complex ,
158
+ pauli_class ,
159
+ qubits ,
160
+ states ,
161
+ lang ,
162
+ bench_func ,
121
163
) -> None :
122
164
"""Benchmark PauliString multiplication with provided set of state vectors.
123
165
124
166
Parametrized test case to run the benchmark across
125
167
all Pauli strings of given length for given PauliString class.
126
168
"""
127
- rng = np . random . default_rng ( 321 )
128
- dim = 1 << qubits
169
+ n_dims = 1 << qubits
170
+ n_strings_limit = 10 if qubits > 4 else None
129
171
130
172
prepared_paulis = list (
131
- map (lambda s : pauli_class (s ), all_strings_for_qubits (qubits ))
173
+ map (lambda s : pauli_class (s ), all_strings_for_qubits (qubits , n_strings_limit ))
132
174
)
133
- prepared_states = [rng .random ((dim , states )) for _ in range (len (prepared_paulis ))]
175
+ prepared_states = [
176
+ generate_random_complex (n_dims , states ) for _ in range (len (prepared_paulis ))
177
+ ]
134
178
135
179
benchmark (bench_func , paulis = prepared_paulis , states = prepared_states )
136
180
137
181
138
182
if __name__ == "__main__" :
139
- pytest .main ()
183
+ pytest .main ([ __file__ ] )
0 commit comments