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