Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
tests/partitioning/fixtures/workloads.py
Go to the documentation of this file.
1 """Mandatory microcase and structured workload surfaces for partitioning API tests (mirrors benchmarks planner_surface.workloads)."""
2 
3 from __future__ import annotations
4 
5 import random
6 from typing import Iterable
7 
8 from squander.partitioning.noisy_planner import (
9  DEFAULT_PARTITION_DESCRIPTOR_MAX_QUBITS,
10  build_partition_descriptor_set,
11  build_canonical_planner_surface_from_operation_specs,
12 )
13 
14 
15 MICROCASE_QUBITS = (2, 3, 4)
16 STRUCTURED_QUBITS = (8, 10)
17 MANDATORY_NOISE_PATTERNS = ("sparse", "periodic", "dense")
18 STRUCTURED_FAMILY_NAMES = (
19  "layered_nearest_neighbor",
20  "seeded_random_layered",
21  "partition_stress_ladder",
22 )
23 DEFAULT_STRUCTURED_SEED = 20260318
24 PHASE31_PRIMARY_STRUCTURED_FAMILY_NAMES = (
25  "phase31_pair_repeat",
26  "phase31_alternating_ladder",
27 )
28 PHASE31_CONTROL_STRUCTURED_FAMILY_NAMES = ("layered_nearest_neighbor",)
29 PHASE31_PRIMARY_NOISE_PATTERNS = ("periodic", "dense")
30 PHASE31_CONTROL_NOISE_PATTERNS = ("sparse",)
31 PHASE31_PRIMARY_SEEDS = (
32  DEFAULT_STRUCTURED_SEED,
33  DEFAULT_STRUCTURED_SEED + 1,
34  DEFAULT_STRUCTURED_SEED + 2,
35 )
36 
37 
38 def _u3(target_qbit: int) -> dict:
39  return {
40  "kind": "gate",
41  "name": "U3",
42  "target_qbit": target_qbit,
43  "param_count": 3,
44  }
45 
46 
47 def _cnot(target_qbit: int, control_qbit: int) -> dict:
48  return {
49  "kind": "gate",
50  "name": "CNOT",
51  "target_qbit": target_qbit,
52  "control_qbit": control_qbit,
53  "param_count": 0,
54  }
55 
56 
57 def _noise(channel: str, target_qbit: int, after_gate_index: int, fixed_value: float) -> dict:
58  return {
59  "kind": "noise",
60  "name": channel,
61  "target_qbit": target_qbit,
62  "source_gate_index": after_gate_index,
63  "fixed_value": fixed_value,
64  "param_count": 0,
65  }
66 
67 
68 def _noise_value(channel: str) -> float:
69  return {
70  "local_depolarizing": 0.1,
71  "amplitude_damping": 0.05,
72  "phase_damping": 0.07,
73  }[channel]
74 
75 
76 def mandatory_microcase_definitions() -> tuple[dict, ...]:
77  return (
78  {
79  "case_name": "microcase_2q_entangler_local_depolarizing",
80  "qbit_num": 2,
81  "noise_pattern": "sparse",
82  "operation_specs": [
83  _u3(0),
84  _u3(1),
85  _cnot(1, 0),
86  _noise("local_depolarizing", 1, 2, _noise_value("local_depolarizing")),
87  _u3(0),
88  ],
89  },
90  {
91  "case_name": "microcase_3q_mixed_local_noise_sequence",
92  "qbit_num": 3,
93  "noise_pattern": "periodic",
94  "operation_specs": [
95  _u3(0),
96  _u3(1),
97  _cnot(1, 0),
98  _noise("amplitude_damping", 1, 2, _noise_value("amplitude_damping")),
99  _u3(2),
100  _cnot(2, 1),
101  _noise("phase_damping", 2, 4, _noise_value("phase_damping")),
102  _u3(0),
103  ],
104  },
105  {
106  "case_name": "microcase_4q_partition_boundary_triplet",
107  "qbit_num": 4,
108  "noise_pattern": "dense",
109  "operation_specs": [
110  _u3(0),
111  _u3(1),
112  _cnot(1, 0),
113  _noise("local_depolarizing", 0, 2, _noise_value("local_depolarizing")),
114  _u3(2),
115  _u3(3),
116  _cnot(3, 2),
117  _noise("phase_damping", 2, 5, _noise_value("phase_damping")),
118  _cnot(2, 1),
119  _noise("amplitude_damping", 1, 6, _noise_value("amplitude_damping")),
120  ],
121  },
122  )
123 
124 
125 def phase31_microcase_definitions() -> tuple[dict, ...]:
126  return (
127  {
128  "case_name": "phase31_microcase_1q_u3_local_noise_chain",
129  "qbit_num": 1,
130  "noise_pattern": "dense",
131  "support_qbits": (0,),
132  "operation_specs": [
133  _u3(0),
134  _noise("local_depolarizing", 0, 0, _noise_value("local_depolarizing")),
135  _u3(0),
136  _noise("phase_damping", 0, 1, _noise_value("phase_damping")),
137  _u3(0),
138  ],
139  },
140  {
141  "case_name": "phase31_microcase_2q_cnot_local_noise_pair",
142  "qbit_num": 2,
143  "noise_pattern": "periodic",
144  "support_qbits": (0, 1),
145  "operation_specs": [
146  _u3(0),
147  _u3(1),
148  _cnot(1, 0),
149  _noise("amplitude_damping", 1, 2, _noise_value("amplitude_damping")),
150  _noise("phase_damping", 0, 2, _noise_value("phase_damping")),
151  _u3(0),
152  ],
153  },
154  {
155  "case_name": "phase31_microcase_2q_multi_noise_entangler_chain",
156  "qbit_num": 2,
157  "noise_pattern": "periodic",
158  "support_qbits": (0, 1),
159  "operation_specs": [
160  _u3(0),
161  _u3(1),
162  _cnot(1, 0),
163  _noise("local_depolarizing", 1, 2, _noise_value("local_depolarizing")),
164  _u3(1),
165  _cnot(0, 1),
166  _noise("amplitude_damping", 0, 4, _noise_value("amplitude_damping")),
167  _u3(0),
168  _noise("phase_damping", 1, 5, _noise_value("phase_damping")),
169  ],
170  },
171  {
172  "case_name": "phase31_microcase_2q_dense_same_support_motif",
173  "qbit_num": 2,
174  "noise_pattern": "dense",
175  "support_qbits": (0, 1),
176  "operation_specs": [
177  _u3(0),
178  _u3(1),
179  _cnot(1, 0),
180  _noise("local_depolarizing", 1, 2, _noise_value("local_depolarizing")),
181  _cnot(0, 1),
182  _noise("amplitude_damping", 0, 3, _noise_value("amplitude_damping")),
183  _u3(0),
184  _u3(1),
185  _noise("phase_damping", 1, 5, _noise_value("phase_damping")),
186  ],
187  },
188  {
189  "case_name": "phase31_local_support_q4_spectator_embedding_smoke",
190  "qbit_num": 4,
191  "noise_pattern": "periodic",
192  "support_qbits": (0, 1, 2, 3),
193  "operation_specs": [
194  _u3(0),
195  _u3(1),
196  _cnot(1, 0),
197  _noise(
198  "amplitude_damping", 1, 2, _noise_value("amplitude_damping")
199  ),
200  _noise("phase_damping", 0, 2, _noise_value("phase_damping")),
201  _u3(0),
202  _u3(2),
203  _u3(3),
204  _cnot(3, 2),
205  _noise(
206  "amplitude_damping", 3, 6, _noise_value("amplitude_damping")
207  ),
208  _noise("phase_damping", 2, 6, _noise_value("phase_damping")),
209  _u3(2),
210  ],
211  },
212  )
213 
214 
215 def _lookup_microcase_definition(case_name: str) -> dict:
216  return next(
217  candidate
219  if candidate["case_name"] == case_name
220  )
221 
222 
223 def build_microcase_surface(case_name: str):
224  case = _lookup_microcase_definition(case_name)
226  qbit_num=case["qbit_num"],
227  source_type="microcase_builder",
228  workload_id=case["case_name"],
229  operation_specs=case["operation_specs"],
230  )
231 
232 
234  for case in mandatory_microcase_definitions():
235  yield case, build_microcase_surface(case["case_name"])
236 
237 
239  for case in phase31_microcase_definitions():
240  yield case, build_microcase_surface(case["case_name"])
241 
242 
244  case_name: str,
245  *,
246  max_partition_qubits: int = DEFAULT_PARTITION_DESCRIPTOR_MAX_QUBITS,
247 ):
249  build_microcase_surface(case_name),
250  max_partition_qubits=max_partition_qubits,
251  )
252 
253 
255  *,
256  max_partition_qubits: int = DEFAULT_PARTITION_DESCRIPTOR_MAX_QUBITS,
257 ):
258  for case in mandatory_microcase_definitions():
259  yield case, build_microcase_descriptor_set(
260  case["case_name"], max_partition_qubits=max_partition_qubits
261  )
262 
263 
265  case_name: str,
266  *,
267  max_partition_qubits: int = DEFAULT_PARTITION_DESCRIPTOR_MAX_QUBITS,
268 ):
270  case_name,
271  max_partition_qubits=max_partition_qubits,
272  )
273 
274 
276  *,
277  max_partition_qubits: int = DEFAULT_PARTITION_DESCRIPTOR_MAX_QUBITS,
278 ):
279  for case in phase31_microcase_definitions():
281  case["case_name"], max_partition_qubits=max_partition_qubits
282  )
283 
284 
285 def _layered_nearest_neighbor_layers(qbit_num: int, layer_count: int) -> list[list[dict]]:
286  layers: list[list[dict]] = []
287  for layer_index in range(layer_count):
288  layer_specs = [_u3(target) for target in range(qbit_num)]
289  start = layer_index % 2
290  for control in range(start, qbit_num - 1, 2):
291  layer_specs.append(_cnot(control + 1, control))
292  layers.append(layer_specs)
293  return layers
294 
295 
297  qbit_num: int, layer_count: int, seed: int
298 ) -> list[list[dict]]:
299  rng = random.Random(seed)
300  layers: list[list[dict]] = []
301  for layer_index in range(layer_count):
302  layer_specs = [_u3(target) for target in range(qbit_num)]
303  start = layer_index % 2
304  for left in range(start, qbit_num - 1, 2):
305  if rng.random() < 0.5:
306  layer_specs.append(_cnot(left + 1, left))
307  else:
308  layer_specs.append(_cnot(left, left + 1))
309  layers.append(layer_specs)
310  return layers
311 
312 
313 def _partition_stress_layers(qbit_num: int, layer_count: int) -> list[list[dict]]:
314  layers: list[list[dict]] = []
315  for layer_index in range(layer_count):
316  layer_specs = [_u3(target) for target in range(qbit_num)]
317  if layer_index % 2 == 0:
318  for control in range(qbit_num - 1):
319  layer_specs.append(_cnot(control + 1, control))
320  else:
321  for target in range(qbit_num - 2, -1, -1):
322  layer_specs.append(_cnot(target, target + 1))
323  layers.append(layer_specs)
324  return layers
325 
326 
327 def _phase31_pair_repeat_layers(qbit_num: int, layer_count: int) -> list[list[dict]]:
328  layers: list[list[dict]] = []
329  for layer_index in range(layer_count):
330  layer_specs = [_u3(target) for target in range(qbit_num)]
331  start = layer_index % 2
332  for left in range(start, qbit_num - 1, 2):
333  layer_specs.append(_cnot(left + 1, left))
334  layer_specs.append(_u3(left))
335  layer_specs.append(_u3(left + 1))
336  layer_specs.append(_cnot(left, left + 1))
337  layers.append(layer_specs)
338  return layers
339 
340 
341 def _phase31_alternating_ladder_layers(qbit_num: int, layer_count: int) -> list[list[dict]]:
342  layers: list[list[dict]] = []
343  for layer_index in range(layer_count):
344  layer_specs = [_u3(target) for target in range(qbit_num)]
345  if layer_index % 2 == 0:
346  for left in range(qbit_num - 1):
347  layer_specs.append(_cnot(left + 1, left))
348  layer_specs.extend(_u3(target) for target in range(qbit_num))
349  for left in range(qbit_num - 2, -1, -1):
350  layer_specs.append(_cnot(left, left + 1))
351  else:
352  for left in range(qbit_num - 2, -1, -1):
353  layer_specs.append(_cnot(left, left + 1))
354  layer_specs.extend(_u3(target) for target in range(qbit_num))
355  for left in range(qbit_num - 1):
356  layer_specs.append(_cnot(left + 1, left))
357  layers.append(layer_specs)
358  return layers
359 
360 
362  layers: Iterable[list[dict]], qbit_num: int, noise_pattern: str
363 ) -> list[dict]:
364  operations: list[dict] = []
365  gate_index = -1
366  entangler_counter = 0
367  channels = ("local_depolarizing", "amplitude_damping", "phase_damping")
368 
369  for layer_index, layer in enumerate(layers):
370  layer_entanglers: list[tuple[int, dict]] = []
371  for gate in layer:
372  operations.append(gate)
373  gate_index += 1
374  if gate["name"] == "CNOT":
375  layer_entanglers.append((gate_index, gate))
376 
377  if not layer_entanglers:
378  continue
379 
380  if noise_pattern == "sparse":
381  if layer_index % 2 == 0:
382  gate_idx, gate = layer_entanglers[0]
383  channel = channels[layer_index % len(channels)]
384  operations.append(
385  _noise(channel, gate["target_qbit"], gate_idx, _noise_value(channel))
386  )
387  elif noise_pattern == "periodic":
388  gate_idx, gate = layer_entanglers[-1]
389  channel = channels[layer_index % len(channels)]
390  operations.append(
391  _noise(channel, gate["target_qbit"], gate_idx, _noise_value(channel))
392  )
393  elif noise_pattern == "dense":
394  for gate_idx, gate in layer_entanglers:
395  channel = channels[entangler_counter % len(channels)]
396  operations.append(
397  _noise(channel, gate["target_qbit"], gate_idx, _noise_value(channel))
398  )
399  entangler_counter += 1
400  continue
401  else:
402  raise ValueError(
403  "Unsupported structured workload noise pattern '{}'".format(noise_pattern)
404  )
405 
406  entangler_counter += len(layer_entanglers)
407 
408  return operations
409 
410 
412  family_name: str,
413  qbit_num: int,
414  *,
415  seed: int,
416 ) -> list[list[dict]]:
417  layer_count = 3 if qbit_num <= 8 else 4
418  if family_name == "layered_nearest_neighbor":
419  return _layered_nearest_neighbor_layers(qbit_num, layer_count)
420  if family_name == "seeded_random_layered":
421  return _seeded_random_layered_layers(qbit_num, layer_count, seed)
422  if family_name == "partition_stress_ladder":
423  return _partition_stress_layers(qbit_num, layer_count)
424  if family_name == "phase31_pair_repeat":
425  return _phase31_pair_repeat_layers(qbit_num, layer_count)
426  if family_name == "phase31_alternating_ladder":
427  return _phase31_alternating_ladder_layers(qbit_num, layer_count)
428  raise ValueError("Unsupported structured workload family '{}'".format(family_name))
429 
430 
432  family_name: str,
433  *,
434  qbit_num: int,
435  noise_pattern: str,
436  seed: int = DEFAULT_STRUCTURED_SEED,
437 ):
438  layers = _structured_family_layers(family_name, qbit_num, seed=seed)
439  operation_specs = _apply_noise_pattern(layers, qbit_num, noise_pattern)
440  workload_id = "{}_q{}_{}_seed{}".format(family_name, qbit_num, noise_pattern, seed)
442  qbit_num=qbit_num,
443  source_type="structured_family_builder",
444  workload_id=workload_id,
445  operation_specs=operation_specs,
446  )
447 
448 
449 def iter_structured_surfaces(seed: int = DEFAULT_STRUCTURED_SEED):
450  for family_name in STRUCTURED_FAMILY_NAMES:
451  for qbit_num in STRUCTURED_QUBITS:
452  for noise_pattern in MANDATORY_NOISE_PATTERNS:
453  metadata = {
454  "family_name": family_name,
455  "qbit_num": qbit_num,
456  "noise_pattern": noise_pattern,
457  "seed": seed,
458  "workload_id": "{}_q{}_{}_seed{}".format(
459  family_name, qbit_num, noise_pattern, seed
460  ),
461  }
462  yield metadata, build_structured_surface(
463  family_name,
464  qbit_num=qbit_num,
465  noise_pattern=noise_pattern,
466  seed=seed,
467  )
468 
469 
471  family_name: str,
472  *,
473  qbit_num: int,
474  noise_pattern: str,
475  seed: int = DEFAULT_STRUCTURED_SEED,
476  max_partition_qubits: int = DEFAULT_PARTITION_DESCRIPTOR_MAX_QUBITS,
477 ):
480  family_name,
481  qbit_num=qbit_num,
482  noise_pattern=noise_pattern,
483  seed=seed,
484  ),
485  max_partition_qubits=max_partition_qubits,
486  )
487 
488 
490  *,
491  seed: int = DEFAULT_STRUCTURED_SEED,
492  max_partition_qubits: int = DEFAULT_PARTITION_DESCRIPTOR_MAX_QUBITS,
493 ):
494  for family_name in STRUCTURED_FAMILY_NAMES:
495  for qbit_num in STRUCTURED_QUBITS:
496  for noise_pattern in MANDATORY_NOISE_PATTERNS:
497  metadata = {
498  "family_name": family_name,
499  "qbit_num": qbit_num,
500  "noise_pattern": noise_pattern,
501  "seed": seed,
502  "workload_id": "{}_q{}_{}_seed{}".format(
503  family_name, qbit_num, noise_pattern, seed
504  ),
505  }
506  yield metadata, build_structured_descriptor_set(
507  family_name,
508  qbit_num=qbit_num,
509  noise_pattern=noise_pattern,
510  seed=seed,
511  max_partition_qubits=max_partition_qubits,
512  )
513 
514 
516  for family_name in PHASE31_PRIMARY_STRUCTURED_FAMILY_NAMES:
517  for qbit_num in STRUCTURED_QUBITS:
518  for noise_pattern in PHASE31_PRIMARY_NOISE_PATTERNS:
519  for seed in PHASE31_PRIMARY_SEEDS:
520  metadata = {
521  "family_name": family_name,
522  "qbit_num": qbit_num,
523  "noise_pattern": noise_pattern,
524  "seed": seed,
525  "workload_id": "{}_q{}_{}_seed{}".format(
526  family_name, qbit_num, noise_pattern, seed
527  ),
528  }
529  yield metadata, build_structured_surface(
530  family_name,
531  qbit_num=qbit_num,
532  noise_pattern=noise_pattern,
533  seed=seed,
534  )
535  for family_name in PHASE31_CONTROL_STRUCTURED_FAMILY_NAMES:
536  for qbit_num in STRUCTURED_QUBITS:
537  for noise_pattern in PHASE31_CONTROL_NOISE_PATTERNS:
538  metadata = {
539  "family_name": family_name,
540  "qbit_num": qbit_num,
541  "noise_pattern": noise_pattern,
542  "seed": DEFAULT_STRUCTURED_SEED,
543  "workload_id": "{}_q{}_{}_seed{}".format(
544  family_name, qbit_num, noise_pattern, DEFAULT_STRUCTURED_SEED
545  ),
546  }
547  yield metadata, build_structured_surface(
548  family_name,
549  qbit_num=qbit_num,
550  noise_pattern=noise_pattern,
551  seed=DEFAULT_STRUCTURED_SEED,
552  )
553 
554 
556  family_name: str,
557  *,
558  qbit_num: int,
559  noise_pattern: str,
560  seed: int = DEFAULT_STRUCTURED_SEED,
561  max_partition_qubits: int = DEFAULT_PARTITION_DESCRIPTOR_MAX_QUBITS,
562 ):
564  family_name,
565  qbit_num=qbit_num,
566  noise_pattern=noise_pattern,
567  seed=seed,
568  max_partition_qubits=max_partition_qubits,
569  )
570 
571 
573  *,
574  max_partition_qubits: int = DEFAULT_PARTITION_DESCRIPTOR_MAX_QUBITS,
575 ):
576  for metadata, _surface in iter_phase31_structured_surfaces():
578  metadata["family_name"],
579  qbit_num=metadata["qbit_num"],
580  noise_pattern=metadata["noise_pattern"],
581  seed=metadata["seed"],
582  max_partition_qubits=max_partition_qubits,
583  )