Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
support_tiers.py
Go to the documentation of this file.
1 """Shared noise-support vocabulary and bundle summary helpers."""
2 
3 from __future__ import annotations
4 
5 import re
6 
7 SUPPORT_TIER_REQUIRED = "required"
8 SUPPORT_TIER_OPTIONAL = "optional"
9 SUPPORT_TIER_DEFERRED = "deferred"
10 SUPPORT_TIER_UNSUPPORTED = "unsupported"
11 
12 CASE_PURPOSE_MANDATORY_BASELINE = "mandatory_baseline"
13 CASE_PURPOSE_OPTIONAL_REGRESSION = "optional_regression"
14 CASE_PURPOSE_OPTIONAL_STRESS = "optional_stress"
15 CASE_PURPOSE_OPTIONAL_COMPARISON = "optional_comparison"
16 CASE_PURPOSE_DEFERRED_SCOPE_GUARD = "deferred_scope_guard"
17 CASE_PURPOSE_UNSUPPORTED_SCOPE_GUARD = "unsupported_scope_guard"
18 
19 OPTIONAL_REASON_WHOLE_REGISTER_DEPOLARIZING = "whole_register_depolarizing_baseline"
20 OPTIONAL_REASON_WORKFLOW_JUSTIFIED_EXTENSION = "workflow_justified_extension"
21 
22 BOUNDARY_CLASS_MODEL_FAMILY = "model_family"
23 BOUNDARY_CLASS_SCHEDULE_ELEMENT = "schedule_element"
24 BOUNDARY_CLASS_CONFIGURATION = "configuration"
25 
26 DEFERRED_REASON_CORRELATED_MULTI_QUBIT = "correlated_multi_qubit_noise"
27 DEFERRED_REASON_READOUT = "readout_noise"
28 DEFERRED_REASON_CALIBRATION_AWARE = "calibration_aware_noise"
29 DEFERRED_REASON_NON_MARKOVIAN = "non_markovian_noise"
30 
31 UNSUPPORTED_REASON_DENSITY_NOISE_CHANNEL = "unsupported_density_noise_channel"
32 UNSUPPORTED_REASON_AFTER_GATE_INDEX_NEGATIVE = "after_gate_index_negative"
33 UNSUPPORTED_REASON_AFTER_GATE_INDEX_EXCEEDS_GATE_COUNT = (
34  "after_gate_index_exceeds_gate_count"
35 )
36 UNSUPPORTED_REASON_TARGET_QBIT_OUT_OF_RANGE = "target_qbit_out_of_range"
37 UNSUPPORTED_REASON_NOISE_VALUE_OUT_OF_RANGE = "noise_value_out_of_range"
38 UNSUPPORTED_REASON_NOISE_VALUE_NON_FINITE = "noise_value_non_finite"
39 
40 DEFERRED_FAMILY_REASONS = (
41  DEFERRED_REASON_CORRELATED_MULTI_QUBIT,
42  DEFERRED_REASON_READOUT,
43  DEFERRED_REASON_CALIBRATION_AWARE,
44  DEFERRED_REASON_NON_MARKOVIAN,
45 )
46 
47 UNSUPPORTED_CONFIGURATION_REASONS = (
48  UNSUPPORTED_REASON_DENSITY_NOISE_CHANNEL,
49  UNSUPPORTED_REASON_AFTER_GATE_INDEX_NEGATIVE,
50  UNSUPPORTED_REASON_AFTER_GATE_INDEX_EXCEEDS_GATE_COUNT,
51  UNSUPPORTED_REASON_TARGET_QBIT_OUT_OF_RANGE,
52  UNSUPPORTED_REASON_NOISE_VALUE_OUT_OF_RANGE,
53  UNSUPPORTED_REASON_NOISE_VALUE_NON_FINITE,
54 )
55 
56 SUPPORT_TIER_VOCABULARY = (
57  SUPPORT_TIER_REQUIRED,
58  SUPPORT_TIER_OPTIONAL,
59  SUPPORT_TIER_DEFERRED,
60  SUPPORT_TIER_UNSUPPORTED,
61 )
62 
63 
65  return {
66  "support_tier": SUPPORT_TIER_REQUIRED,
67  "case_purpose": CASE_PURPOSE_MANDATORY_BASELINE,
68  "counts_toward_mandatory_baseline": True,
69  }
70 
71 
72 def build_optional_case_classification(*, case_purpose, optional_reason):
73  return {
74  "support_tier": SUPPORT_TIER_OPTIONAL,
75  "case_purpose": case_purpose,
76  "optional_reason": optional_reason,
77  "counts_toward_mandatory_baseline": False,
78  }
79 
80 
82  *,
83  deferred_reason,
84  case_purpose=CASE_PURPOSE_DEFERRED_SCOPE_GUARD,
85 ):
86  return {
87  "support_tier": SUPPORT_TIER_DEFERRED,
88  "case_purpose": case_purpose,
89  "deferred_reason": deferred_reason,
90  "counts_toward_mandatory_baseline": False,
91  }
92 
93 
95  *,
96  unsupported_reason,
97  case_purpose=CASE_PURPOSE_UNSUPPORTED_SCOPE_GUARD,
98 ):
99  return {
100  "support_tier": SUPPORT_TIER_UNSUPPORTED,
101  "case_purpose": case_purpose,
102  "unsupported_scope_reason": unsupported_reason,
103  "counts_toward_mandatory_baseline": False,
104  }
105 
106 
107 def _extract_requested_channel(reason: str):
108  match = re.search(r"Unsupported density-noise channel '([^']+)'", reason)
109  if match:
110  return match.group(1)
111  return None
112 
113 
114 def _map_requested_channel_to_boundary_reason(requested_channel: str | None):
115  if not requested_channel:
116  return None
117 
118  lowered = requested_channel.lower()
119  if "readout" in lowered:
120  return DEFERRED_REASON_READOUT
121  if "correlated" in lowered:
122  return DEFERRED_REASON_CORRELATED_MULTI_QUBIT
123  if "calibration" in lowered:
124  return DEFERRED_REASON_CALIBRATION_AWARE
125  if "non_markovian" in lowered or "non-markovian" in lowered:
126  return DEFERRED_REASON_NON_MARKOVIAN
127  return UNSUPPORTED_REASON_DENSITY_NOISE_CHANNEL
128 
129 
131  requested_channel = _extract_requested_channel(reason)
132  if "Unsupported density-noise channel" in reason:
133  first_condition = _map_requested_channel_to_boundary_reason(requested_channel)
134  if first_condition in DEFERRED_FAMILY_REASONS:
135  classification = build_deferred_case_classification(
136  deferred_reason=first_condition
137  )
138  else:
139  classification = build_unsupported_case_classification(
140  unsupported_reason=UNSUPPORTED_REASON_DENSITY_NOISE_CHANNEL
141  )
142  return {
143  "unsupported_category": "noise_type",
144  "first_unsupported_condition": first_condition,
145  "noise_boundary_class": BOUNDARY_CLASS_MODEL_FAMILY,
146  "failure_stage": "python_normalization",
147  "requested_noise_channel": requested_channel,
148  **classification,
149  }
150 
151  if "after_gate_index must be non-negative" in reason:
152  return {
153  "unsupported_category": "noise_insertion",
154  "first_unsupported_condition": UNSUPPORTED_REASON_AFTER_GATE_INDEX_NEGATIVE,
155  "noise_boundary_class": BOUNDARY_CLASS_SCHEDULE_ELEMENT,
156  "failure_stage": "cxx_noise_spec_validation",
157  "requested_noise_channel": None,
159  unsupported_reason=UNSUPPORTED_REASON_AFTER_GATE_INDEX_NEGATIVE
160  ),
161  }
162 
163  if "after_gate_index exceeds generated gate count" in reason:
164  return {
165  "unsupported_category": "noise_insertion",
166  "first_unsupported_condition": (
167  UNSUPPORTED_REASON_AFTER_GATE_INDEX_EXCEEDS_GATE_COUNT
168  ),
169  "noise_boundary_class": BOUNDARY_CLASS_SCHEDULE_ELEMENT,
170  "failure_stage": "density_anchor_preflight",
171  "requested_noise_channel": None,
173  unsupported_reason=UNSUPPORTED_REASON_AFTER_GATE_INDEX_EXCEEDS_GATE_COUNT
174  ),
175  }
176 
177  if "target_qbit out of range" in reason:
178  return {
179  "unsupported_category": "noise_target",
180  "first_unsupported_condition": UNSUPPORTED_REASON_TARGET_QBIT_OUT_OF_RANGE,
181  "noise_boundary_class": BOUNDARY_CLASS_CONFIGURATION,
182  "failure_stage": "cxx_noise_spec_validation",
183  "requested_noise_channel": None,
185  unsupported_reason=UNSUPPORTED_REASON_TARGET_QBIT_OUT_OF_RANGE
186  ),
187  }
188 
189  if "noise values must be in [0, 1]" in reason:
190  return {
191  "unsupported_category": "noise_value",
192  "first_unsupported_condition": UNSUPPORTED_REASON_NOISE_VALUE_OUT_OF_RANGE,
193  "noise_boundary_class": BOUNDARY_CLASS_CONFIGURATION,
194  "failure_stage": "cxx_noise_spec_validation",
195  "requested_noise_channel": None,
197  unsupported_reason=UNSUPPORTED_REASON_NOISE_VALUE_OUT_OF_RANGE
198  ),
199  }
200 
201  if "non-finite value" in reason:
202  return {
203  "unsupported_category": "noise_value",
204  "first_unsupported_condition": UNSUPPORTED_REASON_NOISE_VALUE_NON_FINITE,
205  "noise_boundary_class": BOUNDARY_CLASS_CONFIGURATION,
206  "failure_stage": "python_normalization",
207  "requested_noise_channel": None,
209  unsupported_reason=UNSUPPORTED_REASON_NOISE_VALUE_NON_FINITE
210  ),
211  }
212 
213  return {
214  "unsupported_category": "workflow_execution",
215  "first_unsupported_condition": "workflow_execution",
216  "noise_boundary_class": "workflow_execution",
217  "failure_stage": "workflow_execution",
218  "requested_noise_channel": requested_channel,
220  unsupported_reason="workflow_execution"
221  ),
222  }
223 
224 
226  cases = list(cases)
227  required_cases = [
228  case for case in cases if case.get("support_tier") == SUPPORT_TIER_REQUIRED
229  ]
230  optional_cases = [
231  case for case in cases if case.get("support_tier") == SUPPORT_TIER_OPTIONAL
232  ]
233  deferred_cases = [
234  case for case in cases if case.get("support_tier") == SUPPORT_TIER_DEFERRED
235  ]
236  unsupported_cases = [
237  case for case in cases if case.get("support_tier") == SUPPORT_TIER_UNSUPPORTED
238  ]
239  mandatory_cases = [
240  case for case in cases if case.get("counts_toward_mandatory_baseline")
241  ]
242 
243  required_passed_cases = sum(case.get("status") == "pass" for case in required_cases)
244  optional_passed_cases = sum(case.get("status") == "pass" for case in optional_cases)
245  mandatory_passed_cases = sum(case.get("status") == "pass" for case in mandatory_cases)
246 
247  def _rate(passed, total):
248  return 0.0 if total == 0 else passed / total
249 
250  return {
251  "required_cases": len(required_cases),
252  "required_passed_cases": required_passed_cases,
253  "required_pass_rate": _rate(required_passed_cases, len(required_cases)),
254  "optional_cases": len(optional_cases),
255  "optional_passed_cases": optional_passed_cases,
256  "optional_pass_rate": _rate(optional_passed_cases, len(optional_cases)),
257  "deferred_cases": len(deferred_cases),
258  "unsupported_cases": len(unsupported_cases),
259  "mandatory_baseline_case_count": len(mandatory_cases),
260  "mandatory_baseline_passed_cases": mandatory_passed_cases,
261  "mandatory_baseline_completed": bool(mandatory_cases)
262  and mandatory_passed_cases == len(mandatory_cases),
263  "optional_cases_count_toward_mandatory_baseline": sum(
264  case.get("counts_toward_mandatory_baseline", False)
265  for case in optional_cases
266  ),
267  "support_tiers_present": sorted(
268  {
269  case.get("support_tier")
270  for case in cases
271  if case.get("support_tier") is not None
272  }
273  ),
274  }
def build_deferred_case_classification(deferred_reason, case_purpose=CASE_PURPOSE_DEFERRED_SCOPE_GUARD)
def _extract_requested_channel
def build_support_tier_summary(cases)
def build_optional_case_classification(case_purpose, optional_reason)
def classify_noise_boundary_reason
def build_unsupported_case_classification(unsupported_reason, case_purpose=CASE_PURPOSE_UNSUPPORTED_SCOPE_GUARD)
def build_required_case_classification()
def _map_requested_channel_to_boundary_reason