0
# Parameter Studies and Sweeps
1
2
The study module provides tools for parameterized quantum circuits and parameter sweeps, enabling systematic exploration of quantum algorithms and optimization of quantum circuit parameters.
3
4
## Parameter Resolution
5
6
### ParamResolver
7
8
Resolves symbolic parameters to concrete values.
9
10
```python { .api }
11
class ParamResolver:
12
"""Resolves symbolic parameters to values."""
13
14
def __init__(self, param_dict: Optional[Dict[Union[str, sympy.Symbol], Any]] = None) -> None:
15
"""Initialize parameter resolver.
16
17
Args:
18
param_dict: Mapping from parameter names/symbols to values
19
"""
20
21
def resolve(self, value: Any) -> Any:
22
"""Resolve a parameterized value.
23
24
Args:
25
value: Value that may contain parameters
26
27
Returns:
28
Value with parameters substituted
29
"""
30
31
@property
32
def param_dict(self) -> Dict[Union[str, sympy.Symbol], Any]:
33
"""Dictionary mapping parameters to values."""
34
35
def __getitem__(self, key: Union[str, sympy.Symbol]) -> Any:
36
"""Get parameter value by key."""
37
38
def __contains__(self, key: Union[str, sympy.Symbol]) -> bool:
39
"""Check if parameter is defined."""
40
41
def __bool__(self) -> bool:
42
"""True if resolver contains any parameters."""
43
44
def __len__(self) -> int:
45
"""Number of parameters in resolver."""
46
47
def keys(self) -> KeysView[Union[str, sympy.Symbol]]:
48
"""Parameter names/symbols."""
49
50
def values(self) -> ValuesView[Any]:
51
"""Parameter values."""
52
53
def items(self) -> ItemsView[Union[str, sympy.Symbol], Any]:
54
"""Parameter name-value pairs."""
55
```
56
57
### ExpressionMap
58
59
Maps expressions to values for parameter resolution.
60
61
```python { .api }
62
class ExpressionMap:
63
"""Maps expressions to values."""
64
65
def __init__(self, param_dict: Optional[Dict] = None) -> None:
66
"""Initialize expression map."""
67
68
def transform_dicts(self, dict_list: Iterable[Dict]) -> List[Dict]:
69
"""Transform list of parameter dictionaries."""
70
71
def zip_with(self, *expr_maps: 'ExpressionMap') -> 'ExpressionMap':
72
"""Zip with other expression maps."""
73
```
74
75
## Parameter Sweeps
76
77
### Sweep - Base Class
78
79
Abstract base class for parameter sweeps.
80
81
```python { .api }
82
class Sweep:
83
"""Abstract base class for parameter sweeps."""
84
85
@abc.abstractmethod
86
def __len__(self) -> int:
87
"""Number of parameter configurations in sweep."""
88
89
@abc.abstractmethod
90
def __getitem__(self, index: int) -> ParamResolver:
91
"""Get parameter resolver at given index."""
92
93
def __iter__(self) -> Iterator[ParamResolver]:
94
"""Iterate over parameter resolvers."""
95
96
@property
97
def keys(self) -> AbstractSet[Union[str, sympy.Symbol]]:
98
"""Set of parameter keys swept over."""
99
100
def param_tuples(self) -> Iterator[Tuple[Tuple[Union[str, sympy.Symbol], Any], ...]]:
101
"""Yield parameter tuples for each configuration."""
102
```
103
104
### UnitSweep - Single Parameter Set
105
106
Sweep containing a single parameter configuration.
107
108
```python { .api }
109
class UnitSweep(Sweep):
110
"""Sweep with a single parameter configuration."""
111
112
def __init__(self, param_resolver: Optional[ParamResolver] = None) -> None:
113
"""Initialize unit sweep."""
114
115
UNIT_SWEEP: UnitSweep
116
"""Default unit sweep with no parameters."""
117
```
118
119
### ListSweep - Explicit Parameter List
120
121
Sweep over an explicit list of parameter configurations.
122
123
```python { .api }
124
class ListSweep(Sweep):
125
"""Sweep over explicit list of parameter configurations."""
126
127
def __init__(self, param_resolvers: Sequence[ParamResolver]) -> None:
128
"""Initialize list sweep.
129
130
Args:
131
param_resolvers: List of parameter resolvers to sweep over
132
"""
133
134
def __len__(self) -> int:
135
"""Number of configurations."""
136
137
def __getitem__(self, index: int) -> ParamResolver:
138
"""Get parameter resolver at index."""
139
```
140
141
### Linspace - Linear Parameter Sweep
142
143
Linear spacing of parameter values between start and stop.
144
145
```python { .api }
146
class Linspace(Sweep):
147
"""Linear spacing parameter sweep."""
148
149
def __init__(self, key: Union[str, sympy.Symbol], start: float, stop: float, length: int) -> None:
150
"""Initialize linear space sweep.
151
152
Args:
153
key: Parameter name or symbol
154
start: Starting value
155
stop: Ending value
156
length: Number of points in sweep
157
"""
158
159
@property
160
def key(self) -> Union[str, sympy.Symbol]:
161
"""Parameter being swept."""
162
163
@property
164
def start(self) -> float:
165
"""Starting value."""
166
167
@property
168
def stop(self) -> float:
169
"""Ending value."""
170
171
@property
172
def length(self) -> int:
173
"""Number of points."""
174
```
175
176
### Points - Discrete Parameter Values
177
178
Sweep over discrete parameter values.
179
180
```python { .api }
181
class Points(Sweep):
182
"""Discrete points parameter sweep."""
183
184
def __init__(self, key: Union[str, sympy.Symbol], points: Sequence[Any]) -> None:
185
"""Initialize points sweep.
186
187
Args:
188
key: Parameter name or symbol
189
points: Sequence of parameter values
190
"""
191
192
@property
193
def key(self) -> Union[str, sympy.Symbol]:
194
"""Parameter being swept."""
195
196
@property
197
def points(self) -> Tuple[Any, ...]:
198
"""Parameter values."""
199
```
200
201
### Composite Sweeps
202
203
Sweeps that combine multiple parameter sweeps.
204
205
```python { .api }
206
class Product(Sweep):
207
"""Cartesian product of parameter sweeps."""
208
209
def __init__(self, *sweeps: Sweep) -> None:
210
"""Initialize product sweep.
211
212
Args:
213
*sweeps: Sweeps to take Cartesian product of
214
"""
215
216
@property
217
def factors(self) -> Tuple[Sweep, ...]:
218
"""Factor sweeps in the product."""
219
220
class Zip(Sweep):
221
"""Zip multiple parameter sweeps together."""
222
223
def __init__(self, *sweeps: Sweep) -> None:
224
"""Initialize zip sweep.
225
226
Args:
227
*sweeps: Sweeps to zip together (must have same length)
228
"""
229
230
class ZipLongest(Sweep):
231
"""Zip sweeps with different lengths using longest."""
232
233
def __init__(self, *sweeps: Sweep, fillvalue: ParamResolver = None) -> None:
234
"""Initialize zip longest sweep."""
235
236
class Concat(Sweep):
237
"""Concatenate multiple parameter sweeps."""
238
239
def __init__(self, *sweeps: Sweep) -> None:
240
"""Initialize concatenated sweep."""
241
```
242
243
## Sweep Construction Functions
244
245
Utility functions for creating parameter sweeps.
246
247
```python { .api }
248
def dict_to_product_sweep(param_dict: Dict[Union[str, sympy.Symbol], Sequence[Any]]) -> Product:
249
"""Create product sweep from dictionary.
250
251
Args:
252
param_dict: Dictionary mapping parameter names to sequences of values
253
254
Returns:
255
Product sweep over all parameter combinations
256
257
Example:
258
>>> sweep = dict_to_product_sweep({'theta': [0, π/2, π], 'phi': [0, π/4]})
259
>>> len(sweep) # 3 * 2 = 6 combinations
260
6
261
"""
262
263
def dict_to_zip_sweep(param_dict: Dict[Union[str, sympy.Symbol], Sequence[Any]]) -> Zip:
264
"""Create zip sweep from dictionary.
265
266
Args:
267
param_dict: Dictionary mapping parameter names to equal-length sequences
268
269
Returns:
270
Zip sweep pairing corresponding values
271
"""
272
```
273
274
## Sweep Conversion Functions
275
276
Functions for converting various objects to sweeps.
277
278
```python { .api }
279
def to_resolvers(sweepable: 'Sweepable') -> List[ParamResolver]:
280
"""Convert sweepable object to list of parameter resolvers.
281
282
Args:
283
sweepable: Object that can be converted to parameter resolvers
284
285
Returns:
286
List of parameter resolvers
287
"""
288
289
def to_sweep(sweep_or_resolver_or_params: 'Sweepable') -> Sweep:
290
"""Convert object to parameter sweep.
291
292
Args:
293
sweep_or_resolver_or_params: Object to convert to sweep
294
295
Returns:
296
Parameter sweep
297
"""
298
299
def to_sweeps(sweepable: 'Sweepable') -> List[Sweep]:
300
"""Convert to list of sweeps."""
301
```
302
303
## Result Classes
304
305
### Result - Circuit Execution Results
306
307
Container for circuit execution results.
308
309
```python { .api }
310
class Result:
311
"""Result of running a quantum circuit."""
312
313
def __init__(self, *,
314
params: ParamResolver,
315
measurements: Dict[str, np.ndarray],
316
records: Optional[Dict[str, np.ndarray]] = None) -> None:
317
"""Initialize result.
318
319
Args:
320
params: Parameters used for this execution
321
measurements: Measurement outcomes keyed by measurement key
322
records: Raw measurement records
323
"""
324
325
@property
326
def params(self) -> ParamResolver:
327
"""Parameters used in this execution."""
328
329
@property
330
def measurements(self) -> Dict[str, np.ndarray]:
331
"""Measurement results keyed by measurement key."""
332
333
@property
334
def records(self) -> Dict[str, np.ndarray]:
335
"""Raw measurement records."""
336
337
def histogram(self, *, key: str, fold_func: Optional[Callable] = None) -> Dict[Any, int]:
338
"""Get histogram of measurement outcomes.
339
340
Args:
341
key: Measurement key to histogram
342
fold_func: Function to fold multi-qubit outcomes
343
344
Returns:
345
Dictionary mapping outcomes to counts
346
"""
347
348
def multi_measurement_histogram(self, *, keys: Sequence[str],
349
fold_func: Optional[Callable] = None) -> Dict[Tuple[Any, ...], int]:
350
"""Get histogram over multiple measurement keys."""
351
352
def data(self, *, name: str = None) -> pd.DataFrame:
353
"""Get measurement data as pandas DataFrame."""
354
355
def __getitem__(self, key: str) -> np.ndarray:
356
"""Get measurement outcomes for key."""
357
358
def __contains__(self, key: str) -> bool:
359
"""Check if measurement key is present."""
360
361
def __len__(self) -> int:
362
"""Number of repetitions in result."""
363
364
def __iter__(self) -> Iterator[str]:
365
"""Iterate over measurement keys."""
366
367
def __bool__(self) -> bool:
368
"""True if result contains measurements."""
369
```
370
371
### ResultDict - Dictionary-like Result Access
372
373
Enhanced result access with dictionary-like interface.
374
375
```python { .api }
376
class ResultDict:
377
"""Dictionary-like access to circuit results."""
378
379
def __init__(self, params_to_results: Dict[ParamResolver, Result]) -> None:
380
"""Initialize result dictionary."""
381
382
def __getitem__(self, params: ParamResolver) -> Result:
383
"""Get result for parameter configuration."""
384
385
def __contains__(self, params: ParamResolver) -> bool:
386
"""Check if parameters are in results."""
387
388
def __iter__(self) -> Iterator[ParamResolver]:
389
"""Iterate over parameter configurations."""
390
391
def __len__(self) -> int:
392
"""Number of parameter configurations."""
393
394
def keys(self) -> KeysView[ParamResolver]:
395
"""Parameter configurations."""
396
397
def values(self) -> ValuesView[Result]:
398
"""Results for each configuration."""
399
400
def items(self) -> ItemsView[ParamResolver, Result]:
401
"""Parameter-result pairs."""
402
```
403
404
## Expression Flattening
405
406
Utilities for flattening nested expressions with parameters.
407
408
```python { .api }
409
def flatten(sweep_or_resolver_or_params: 'Sweepable') -> List[ParamResolver]:
410
"""Flatten sweepable to list of parameter resolvers."""
411
412
def flatten_with_params(program: 'cirq.CIRCUIT_LIKE',
413
params: 'Sweepable') -> List[Tuple['cirq.CIRCUIT_LIKE', ParamResolver]]:
414
"""Flatten program with parameters to (program, resolver) pairs."""
415
416
def flatten_with_sweep(program: 'cirq.CIRCUIT_LIKE',
417
params: 'Sweepable') -> List[Tuple['cirq.CIRCUIT_LIKE', ParamResolver]]:
418
"""Flatten program with sweep to (program, resolver) pairs."""
419
```
420
421
## Type Definitions
422
423
Type aliases and hints used throughout the study module.
424
425
```python { .api }
426
ParamDictType = Mapping[Union[str, sympy.Symbol], Any]
427
"""Type for parameter dictionaries."""
428
429
ParamMappingType = Mapping[Union[str, sympy.Symbol], Any]
430
"""Type for parameter mappings."""
431
432
ParamResolverOrSimilarType = Union[ParamResolver, ParamDictType, None, Iterable[Any]]
433
"""Type for parameter resolver-like objects."""
434
435
Sweepable = Union[Sweep, ParamResolver, ParamDictType, Iterable[Any]]
436
"""Type for objects that can be converted to sweeps."""
437
```
438
439
## Usage Examples
440
441
### Basic Parameter Resolution
442
443
```python
444
import cirq
445
import sympy
446
import numpy as np
447
448
# Create parameterized circuit
449
theta, phi = sympy.symbols('theta phi')
450
qubits = cirq.LineQubit.range(2)
451
452
circuit = cirq.Circuit([
453
cirq.rx(theta)(qubits[0]),
454
cirq.ry(phi)(qubits[1]),
455
cirq.CNOT(qubits[0], qubits[1]),
456
cirq.measure(*qubits, key='result')
457
])
458
459
print(f"Original circuit parameters: {cirq.parameter_names(circuit)}")
460
461
# Resolve parameters
462
resolver = cirq.ParamResolver({'theta': np.pi/4, 'phi': np.pi/3})
463
resolved_circuit = cirq.resolve_parameters(circuit, resolver)
464
465
print("Resolved circuit:")
466
print(resolved_circuit)
467
```
468
469
### Linear Parameter Sweeps
470
471
```python
472
import cirq
473
import sympy
474
import numpy as np
475
476
# Create parameterized rotation circuit
477
theta = sympy.Symbol('theta')
478
qubit = cirq.LineQubit(0)
479
480
circuit = cirq.Circuit([
481
cirq.rx(theta)(qubit),
482
cirq.measure(qubit, key='result')
483
])
484
485
# Create linear sweep over rotation angle
486
sweep = cirq.Linspace('theta', start=0, stop=2*np.pi, length=8)
487
488
print(f"Sweep length: {len(sweep)}")
489
print("Parameter values:")
490
for i, resolver in enumerate(sweep):
491
print(f" Point {i}: theta = {resolver['theta']:.3f}")
492
493
# Simulate over sweep
494
simulator = cirq.Simulator()
495
results = simulator.run_sweep(circuit, sweep, repetitions=100)
496
497
print("\nSweep results:")
498
for i, result in enumerate(results):
499
theta_val = sweep[i]['theta']
500
prob_zero = result.histogram(key='result').get(0, 0) / 100
501
print(f"θ = {theta_val:.3f}: P(|0⟩) = {prob_zero:.3f}")
502
```
503
504
### Product Sweeps - Multiple Parameters
505
506
```python
507
import cirq
508
import sympy
509
import numpy as np
510
511
# Two-parameter circuit
512
theta, phi = sympy.symbols('theta phi')
513
qubits = cirq.LineQubit.range(2)
514
515
circuit = cirq.Circuit([
516
cirq.rx(theta)(qubits[0]),
517
cirq.ry(phi)(qubits[1]),
518
cirq.CNOT(qubits[0], qubits[1]),
519
cirq.measure(*qubits, key='result')
520
])
521
522
# Create product sweep over both parameters
523
theta_sweep = cirq.Linspace('theta', 0, np.pi, 3)
524
phi_sweep = cirq.Linspace('phi', 0, np.pi/2, 2)
525
product_sweep = cirq.Product(theta_sweep, phi_sweep)
526
527
print(f"Product sweep length: {len(product_sweep)} = {len(theta_sweep)} × {len(phi_sweep)}")
528
529
# Alternative: create from dictionary
530
param_dict = {
531
'theta': [0, np.pi/2, np.pi],
532
'phi': [0, np.pi/4]
533
}
534
dict_sweep = cirq.dict_to_product_sweep(param_dict)
535
print(f"Dictionary sweep length: {len(dict_sweep)}")
536
537
# Run the sweep
538
results = cirq.sample_sweep(circuit, product_sweep, repetitions=50)
539
540
print("\nProduct sweep results:")
541
for i, result in enumerate(results):
542
params = product_sweep[i]
543
theta_val = params['theta']
544
phi_val = params['phi']
545
outcomes = result.histogram(key='result')
546
print(f"θ={theta_val:.2f}, φ={phi_val:.2f}: {outcomes}")
547
```
548
549
### Zip Sweeps - Paired Parameters
550
551
```python
552
import cirq
553
import sympy
554
import numpy as np
555
556
# Circuit with two correlated parameters
557
alpha, beta = sympy.symbols('alpha beta')
558
qubit = cirq.LineQubit(0)
559
560
circuit = cirq.Circuit([
561
cirq.rx(alpha)(qubit),
562
cirq.rz(beta)(qubit),
563
cirq.measure(qubit, key='result')
564
])
565
566
# Create paired parameter sweep
567
alpha_values = np.linspace(0, np.pi, 5)
568
beta_values = 2 * alpha_values # β = 2α correlation
569
570
alpha_sweep = cirq.Points('alpha', alpha_values)
571
beta_sweep = cirq.Points('beta', beta_values)
572
zip_sweep = cirq.Zip(alpha_sweep, beta_sweep)
573
574
print("Zip sweep - correlated parameters:")
575
for i, resolver in enumerate(zip_sweep):
576
alpha_val = resolver['alpha']
577
beta_val = resolver['beta']
578
print(f" Point {i}: α = {alpha_val:.3f}, β = {beta_val:.3f}")
579
580
# Alternative: from dictionary with equal-length lists
581
param_dict = {
582
'alpha': alpha_values,
583
'beta': beta_values
584
}
585
dict_zip_sweep = cirq.dict_to_zip_sweep(param_dict)
586
587
# Run zip sweep
588
results = cirq.sample_sweep(circuit, zip_sweep, repetitions=100)
589
print(f"\nRan zip sweep with {len(results)} parameter configurations")
590
```
591
592
### Working with Results
593
594
```python
595
import cirq
596
import numpy as np
597
598
# Create and run a simple circuit
599
qubits = cirq.LineQubit.range(2)
600
circuit = cirq.Circuit([
601
cirq.H(qubits[0]),
602
cirq.CNOT(qubits[0], qubits[1]),
603
cirq.measure(*qubits, key='bell_pair')
604
])
605
606
simulator = cirq.Simulator()
607
result = simulator.run(circuit, repetitions=1000)
608
609
# Analyze results
610
print("Result analysis:")
611
print(f"Number of repetitions: {len(result)}")
612
print(f"Measurement keys: {list(result.measurements.keys())}")
613
print(f"Bell pair histogram: {result.histogram(key='bell_pair')}")
614
615
# Access raw measurement data
616
bell_measurements = result['bell_pair']
617
print(f"Measurement array shape: {bell_measurements.shape}")
618
print(f"First 10 outcomes: {bell_measurements[:10]}")
619
620
# Convert to DataFrame for analysis
621
import pandas as pd
622
df = result.data()
623
print(f"\nDataFrame shape: {df.shape}")
624
print(df.head())
625
626
# Multi-key measurements
627
multi_circuit = cirq.Circuit([
628
cirq.H(qubits[0]),
629
cirq.measure(qubits[0], key='first'),
630
cirq.CNOT(qubits[0], qubits[1]),
631
cirq.measure(qubits[1], key='second')
632
])
633
634
multi_result = simulator.run(multi_circuit, repetitions=100)
635
multi_hist = multi_result.multi_measurement_histogram(keys=['first', 'second'])
636
print(f"\nMulti-key histogram: {multi_hist}")
637
```
638
639
### Advanced Sweep Combinations
640
641
```python
642
import cirq
643
import sympy
644
import numpy as np
645
646
# Complex parameter sweep combining multiple sweep types
647
theta, phi, gamma = sympy.symbols('theta phi gamma')
648
qubits = cirq.LineQubit.range(2)
649
650
circuit = cirq.Circuit([
651
cirq.rx(theta)(qubits[0]),
652
cirq.ry(phi)(qubits[0]),
653
cirq.rz(gamma)(qubits[1]),
654
cirq.CNOT(qubits[0], qubits[1]),
655
cirq.measure(*qubits, key='result')
656
])
657
658
# Combine different sweep types
659
theta_sweep = cirq.Linspace('theta', 0, np.pi, 3)
660
phi_points = cirq.Points('phi', [0, np.pi/4, np.pi/2])
661
gamma_sweep = cirq.Linspace('gamma', 0, 2*np.pi, 4)
662
663
# First combine theta and phi as product
664
theta_phi_product = cirq.Product(theta_sweep, phi_points)
665
666
# Then zip with gamma (need to match lengths)
667
# Repeat gamma sweep to match product length
668
repeated_gamma = cirq.Concat(*[gamma_sweep] * (len(theta_phi_product) // len(gamma_sweep) + 1))
669
truncated_gamma = cirq.ListSweep([repeated_gamma[i] for i in range(len(theta_phi_product))])
670
671
final_sweep = cirq.Zip(theta_phi_product, truncated_gamma)
672
673
print(f"Complex sweep length: {len(final_sweep)}")
674
print("First few parameter combinations:")
675
for i in range(min(5, len(final_sweep))):
676
params = final_sweep[i]
677
print(f" {i}: θ={params['theta']:.2f}, φ={params['phi']:.2f}, γ={params['gamma']:.2f}")
678
```
679
680
This comprehensive parameter study system enables systematic exploration and optimization of quantum algorithms through structured parameter sweeps and result analysis.