Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
unsupported_noise_validation.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 """Validation: unsupported and deferred noise boundary.
3 
4 Captures representative negative cases as structured artifacts. The
5 focus is the frozen Phase 2 noise support matrix:
6 - deferred families fail before execution,
7 - invalid ordered-noise schedule metadata fails deterministically,
8 - the first unsupported condition is recorded in a machine-reviewable way,
9 - and every emitted case stays outside the mandatory baseline.
10 
11 Run with:
12  python benchmarks/density_matrix/noise_support/unsupported_noise_validation.py
13 """
14 
15 from __future__ import annotations
16 
17 import argparse
18 import json
19 import sys
20 from pathlib import Path
21 
22 REPO_ROOT = Path(__file__).resolve().parents[3]
23 if str(REPO_ROOT) not in sys.path:
24  sys.path.insert(0, str(REPO_ROOT))
25 
26 from examples.VQE.shot_noise_measurement import generate_zz_xx_hamiltonian
27 from benchmarks.density_matrix.workflow_evidence.exact_density_vqe_validation import (
28  DEFAULT_ANSATZ,
29  DEFAULT_INNER_BLOCKS,
30  DEFAULT_LAYERS,
31  PRIMARY_BACKEND,
32  build_case_metadata,
33  build_open_chain_topology,
34  build_software_metadata,
35  build_optimizer_config,
36  build_hamiltonian_metadata,
37  build_vqe,
38 )
39 from benchmarks.density_matrix.noise_support.support_tiers import (
40  BOUNDARY_CLASS_CONFIGURATION,
41  BOUNDARY_CLASS_MODEL_FAMILY,
42  BOUNDARY_CLASS_SCHEDULE_ELEMENT,
43  DEFERRED_REASON_CALIBRATION_AWARE,
44  DEFERRED_REASON_CORRELATED_MULTI_QUBIT,
45  DEFERRED_REASON_NON_MARKOVIAN,
46  DEFERRED_REASON_READOUT,
47  SUPPORT_TIER_DEFERRED,
48  SUPPORT_TIER_UNSUPPORTED,
49  SUPPORT_TIER_VOCABULARY,
50  DEFERRED_FAMILY_REASONS,
51  classify_noise_boundary_reason,
52  build_support_tier_summary,
53 )
54 from squander import Variational_Quantum_Eigensolver
55 
56 SUITE_NAME = "unsupported_noise_boundary"
57 ARTIFACT_FILENAME = "unsupported_noise_bundle.json"
58 DEFAULT_OUTPUT_DIR = (
59  REPO_ROOT / "benchmarks" / "density_matrix" / "artifacts" / "noise_support"
60 )
61 ARTIFACT_CORE_FIELDS = (
62  "suite_name",
63  "status",
64  "backend",
65  "requirements",
66  "summary",
67  "software",
68  "cases",
69 )
70 
71 UNSUPPORTED_NOISE_BOUNDARY_CASES = (
72  {
73  "case_name": "deferred_readout_noise",
74  "qbit_num": 1,
75  "density_noise": [
76  {
77  "channel": "readout_noise",
78  "target": 0,
79  "after_gate_index": 0,
80  "value": 0.1,
81  }
82  ],
83  "purpose": "Verify readout-noise requests fail before execution on the VQE-facing density path.",
84  "expected_error_fragment": "Unsupported density-noise channel 'readout_noise'",
85  "expected_support_tier": SUPPORT_TIER_DEFERRED,
86  "expected_unsupported_category": "noise_type",
87  "expected_first_unsupported_condition": DEFERRED_REASON_READOUT,
88  "expected_noise_boundary_class": BOUNDARY_CLASS_MODEL_FAMILY,
89  "expected_failure_stage": "python_normalization",
90  "runner_kind": "constructor",
91  },
92  {
93  "case_name": "deferred_correlated_multi_qubit_noise",
94  "qbit_num": 2,
95  "density_noise": [
96  {
97  "channel": "correlated_multi_qubit_noise",
98  "target": 0,
99  "after_gate_index": 0,
100  "value": 0.1,
101  }
102  ],
103  "purpose": "Verify correlated multi-qubit noise requests remain deferred and fail before execution.",
104  "expected_error_fragment": "Unsupported density-noise channel 'correlated_multi_qubit_noise'",
105  "expected_support_tier": SUPPORT_TIER_DEFERRED,
106  "expected_unsupported_category": "noise_type",
107  "expected_first_unsupported_condition": (
108  DEFERRED_REASON_CORRELATED_MULTI_QUBIT
109  ),
110  "expected_noise_boundary_class": BOUNDARY_CLASS_MODEL_FAMILY,
111  "expected_failure_stage": "python_normalization",
112  "runner_kind": "constructor",
113  },
114  {
115  "case_name": "deferred_calibration_aware_noise",
116  "qbit_num": 1,
117  "density_noise": [
118  {
119  "channel": "calibration_aware_noise",
120  "target": 0,
121  "after_gate_index": 0,
122  "value": 0.1,
123  }
124  ],
125  "purpose": "Verify calibration-aware noise requests remain deferred and fail before execution.",
126  "expected_error_fragment": "Unsupported density-noise channel 'calibration_aware_noise'",
127  "expected_support_tier": SUPPORT_TIER_DEFERRED,
128  "expected_unsupported_category": "noise_type",
129  "expected_first_unsupported_condition": DEFERRED_REASON_CALIBRATION_AWARE,
130  "expected_noise_boundary_class": BOUNDARY_CLASS_MODEL_FAMILY,
131  "expected_failure_stage": "python_normalization",
132  "runner_kind": "constructor",
133  },
134  {
135  "case_name": "deferred_non_markovian_noise",
136  "qbit_num": 1,
137  "density_noise": [
138  {
139  "channel": "non_markovian_noise",
140  "target": 0,
141  "after_gate_index": 0,
142  "value": 0.1,
143  }
144  ],
145  "purpose": "Verify non-Markovian noise requests remain deferred and fail before execution.",
146  "expected_error_fragment": "Unsupported density-noise channel 'non_markovian_noise'",
147  "expected_support_tier": SUPPORT_TIER_DEFERRED,
148  "expected_unsupported_category": "noise_type",
149  "expected_first_unsupported_condition": DEFERRED_REASON_NON_MARKOVIAN,
150  "expected_noise_boundary_class": BOUNDARY_CLASS_MODEL_FAMILY,
151  "expected_failure_stage": "python_normalization",
152  "runner_kind": "constructor",
153  },
154  {
155  "case_name": "invalid_negative_after_gate_index",
156  "qbit_num": 4,
157  "density_noise": [
158  {
159  "channel": "local_depolarizing",
160  "target": 0,
161  "after_gate_index": -1,
162  "error_rate": 0.1,
163  }
164  ],
165  "purpose": "Verify negative after_gate_index fails during density-noise configuration.",
166  "expected_error_fragment": "after_gate_index must be non-negative",
167  "expected_support_tier": SUPPORT_TIER_UNSUPPORTED,
168  "expected_unsupported_category": "noise_insertion",
169  "expected_first_unsupported_condition": "after_gate_index_negative",
170  "expected_noise_boundary_class": BOUNDARY_CLASS_SCHEDULE_ELEMENT,
171  "expected_failure_stage": "cxx_noise_spec_validation",
172  "runner_kind": "constructor",
173  },
174  {
175  "case_name": "invalid_after_gate_index_exceeds_gate_count",
176  "qbit_num": 4,
177  "density_noise": [
178  {
179  "channel": "local_depolarizing",
180  "target": 0,
181  "after_gate_index": 999,
182  "error_rate": 0.1,
183  }
184  ],
185  "purpose": "Verify insertion points beyond the generated gate count fail before execution.",
186  "expected_error_fragment": "after_gate_index exceeds generated gate count",
187  "expected_support_tier": SUPPORT_TIER_UNSUPPORTED,
188  "expected_unsupported_category": "noise_insertion",
189  "expected_first_unsupported_condition": (
190  "after_gate_index_exceeds_gate_count"
191  ),
192  "expected_noise_boundary_class": BOUNDARY_CLASS_SCHEDULE_ELEMENT,
193  "expected_failure_stage": "density_anchor_preflight",
194  "runner_kind": "bridge_preflight",
195  },
196  {
197  "case_name": "invalid_noise_target_out_of_range",
198  "qbit_num": 2,
199  "density_noise": [
200  {
201  "channel": "phase_damping",
202  "target": 10,
203  "after_gate_index": 0,
204  "lambda": 0.1,
205  }
206  ],
207  "purpose": "Verify invalid noise targets fail during density-noise configuration.",
208  "expected_error_fragment": "target_qbit out of range",
209  "expected_support_tier": SUPPORT_TIER_UNSUPPORTED,
210  "expected_unsupported_category": "noise_target",
211  "expected_first_unsupported_condition": "target_qbit_out_of_range",
212  "expected_noise_boundary_class": BOUNDARY_CLASS_CONFIGURATION,
213  "expected_failure_stage": "cxx_noise_spec_validation",
214  "runner_kind": "constructor",
215  },
216  {
217  "case_name": "invalid_noise_value_out_of_range",
218  "qbit_num": 2,
219  "density_noise": [
220  {
221  "channel": "amplitude_damping",
222  "target": 0,
223  "after_gate_index": 0,
224  "gamma": 1.5,
225  }
226  ],
227  "purpose": "Verify out-of-range noise probabilities fail during density-noise configuration.",
228  "expected_error_fragment": "noise values must be in [0, 1]",
229  "expected_support_tier": SUPPORT_TIER_UNSUPPORTED,
230  "expected_unsupported_category": "noise_value",
231  "expected_first_unsupported_condition": "noise_value_out_of_range",
232  "expected_noise_boundary_class": BOUNDARY_CLASS_CONFIGURATION,
233  "expected_failure_stage": "cxx_noise_spec_validation",
234  "runner_kind": "constructor",
235  },
236 )
237 
238 
240  return {
241  "expected_status": "unsupported",
242  "support_tier_vocabulary": list(SUPPORT_TIER_VOCABULARY),
243  "required_support_tiers": [SUPPORT_TIER_DEFERRED, SUPPORT_TIER_UNSUPPORTED],
244  "deferred_family_reasons": list(DEFERRED_FAMILY_REASONS),
245  "required_categories": sorted(
246  {
247  case["expected_unsupported_category"]
248  for case in UNSUPPORTED_NOISE_BOUNDARY_CASES
249  }
250  ),
251  "required_boundary_classes": sorted(
252  {
253  case["expected_noise_boundary_class"]
254  for case in UNSUPPORTED_NOISE_BOUNDARY_CASES
255  }
256  ),
257  "required_failure_stages": sorted(
258  {case["expected_failure_stage"] for case in UNSUPPORTED_NOISE_BOUNDARY_CASES}
259  ),
260  "case_names": [case["case_name"] for case in UNSUPPORTED_NOISE_BOUNDARY_CASES],
261  "canonical_negative_fields": [
262  "support_tier",
263  "case_purpose",
264  "counts_toward_mandatory_baseline",
265  "unsupported_category",
266  "first_unsupported_condition",
267  "noise_boundary_class",
268  "failure_stage",
269  "unsupported_reason",
270  ],
271  }
272 
273 
274 def _build_hamiltonian(qbit_num: int, topology):
276  n_qubits=qbit_num,
277  h=0.5,
278  topology=topology,
279  Jz=1.0,
280  Jx=1.0,
281  Jy=1.0,
282  )[0]
283 
284 
286  topology = build_open_chain_topology(case["qbit_num"])
287 
288  def runner():
289  Variational_Quantum_Eigensolver(
290  _build_hamiltonian(case["qbit_num"], topology),
291  case["qbit_num"],
293  backend=PRIMARY_BACKEND,
294  density_noise=[dict(item) for item in case["density_noise"]],
295  )
296 
297  return topology, runner
298 
299 
301  def runner():
302  vqe, _, _ = build_vqe(
303  case["qbit_num"],
304  density_noise=[dict(item) for item in case["density_noise"]],
305  )
306  vqe.describe_density_bridge()
307 
308  return build_open_chain_topology(case["qbit_num"]), runner
309 
310 
312  if case["runner_kind"] == "constructor":
313  return _constructor_failure_runner(case)
314  if case["runner_kind"] == "bridge_preflight":
315  return _bridge_preflight_runner(case)
316  raise ValueError(
317  "Unsupported noise-boundary runner kind '{}'".format(case["runner_kind"])
318  )
319 
320 
321 def _base_case_metadata(case, topology):
322  metadata = build_case_metadata(
323  backend=PRIMARY_BACKEND,
324  qbit_num=case["qbit_num"],
325  topology=topology,
326  density_noise=[dict(item) for item in case["density_noise"]],
327  ansatz=DEFAULT_ANSATZ,
328  layers=DEFAULT_LAYERS,
329  inner_blocks=DEFAULT_INNER_BLOCKS,
330  hamiltonian=build_hamiltonian_metadata(),
331  )
332  metadata.update(
333  {
334  "case_name": case["case_name"],
335  "case_kind": "unsupported_noise_boundary_validation",
336  "purpose": case["purpose"],
337  "requested_noise_channel": case["density_noise"][0]["channel"],
338  "expected_support_tier": case["expected_support_tier"],
339  "expected_unsupported_category": case["expected_unsupported_category"],
340  "expected_first_unsupported_condition": case[
341  "expected_first_unsupported_condition"
342  ],
343  "expected_noise_boundary_class": case["expected_noise_boundary_class"],
344  "expected_failure_stage": case["expected_failure_stage"],
345  "silent_substitution_detected": False,
346  "silent_fallback_detected": False,
347  "pre_execution_failure_pass": False,
348  "error_match_pass": False,
349  "unsupported_boundary_pass": False,
350  }
351  )
352  return metadata
353 
354 
356  required_fields = (
357  "case_name",
358  "status",
359  "backend",
360  "qbit_num",
361  "topology",
362  "ansatz",
363  "layers",
364  "inner_blocks",
365  "density_noise",
366  "requested_noise_channel",
367  "support_tier",
368  "case_purpose",
369  "counts_toward_mandatory_baseline",
370  "unsupported_category",
371  "first_unsupported_condition",
372  "noise_boundary_class",
373  "failure_stage",
374  "unsupported_reason",
375  "error_match_pass",
376  "pre_execution_failure_pass",
377  "unsupported_boundary_pass",
378  "silent_substitution_detected",
379  "silent_fallback_detected",
380  )
381  missing_fields = [field for field in required_fields if field not in case]
382  if missing_fields:
383  raise ValueError(
384  "Unsupported-noise boundary case payload is missing required fields: {}".format(
385  ", ".join(missing_fields)
386  )
387  )
388 
389  if case["support_tier"] == SUPPORT_TIER_DEFERRED and "deferred_reason" not in case:
390  raise ValueError(
391  "Deferred noise-boundary case '{}' is missing deferred_reason".format(
392  case["case_name"]
393  )
394  )
395 
396  if (
397  case["support_tier"] == SUPPORT_TIER_UNSUPPORTED
398  and "unsupported_scope_reason" not in case
399  ):
400  raise ValueError(
401  "Unsupported noise-boundary case '{}' is missing unsupported_scope_reason".format(
402  case["case_name"]
403  )
404  )
405 
406 
407 def capture_unsupported_case(case, *, verbose=False):
408  topology, runner = _build_case_runner(case)
409  metadata = _base_case_metadata(case, topology)
410  try:
411  runner()
412  result = dict(metadata)
413  result.update(
414  {
415  "status": "fail",
416  "unsupported_reason": (
417  "Case executed successfully but was expected to fail before "
418  "density execution."
419  ),
420  "case_purpose": "unexpected_success",
421  "counts_toward_mandatory_baseline": False,
422  "unsupported_category": case["expected_unsupported_category"],
423  "first_unsupported_condition": case[
424  "expected_first_unsupported_condition"
425  ],
426  "noise_boundary_class": case["expected_noise_boundary_class"],
427  "failure_stage": case["expected_failure_stage"],
428  "silent_fallback_detected": True,
429  }
430  )
431  if case["expected_support_tier"] == SUPPORT_TIER_DEFERRED:
432  result.update(
433  {
434  "support_tier": SUPPORT_TIER_DEFERRED,
435  "deferred_reason": case["expected_first_unsupported_condition"],
436  }
437  )
438  else:
439  result.update(
440  {
441  "support_tier": SUPPORT_TIER_UNSUPPORTED,
442  "unsupported_scope_reason": case[
443  "expected_first_unsupported_condition"
444  ],
445  }
446  )
447  except Exception as exc:
448  reason = str(exc)
449  classification = classify_noise_boundary_reason(reason)
450  result = dict(metadata)
451  result.update(classification)
452  if result["requested_noise_channel"] is None:
453  result["requested_noise_channel"] = metadata["requested_noise_channel"]
454  result.update(
455  {
456  "status": "unsupported",
457  "unsupported_reason": reason,
458  "error_match_pass": case["expected_error_fragment"] in reason,
459  "pre_execution_failure_pass": True,
460  "unsupported_boundary_pass": bool(
461  classification["support_tier"] == case["expected_support_tier"]
462  and classification["unsupported_category"]
463  == case["expected_unsupported_category"]
464  and classification["first_unsupported_condition"]
465  == case["expected_first_unsupported_condition"]
466  and classification["noise_boundary_class"]
467  == case["expected_noise_boundary_class"]
468  and classification["failure_stage"] == case["expected_failure_stage"]
469  and not classification["counts_toward_mandatory_baseline"]
470  ),
471  }
472  )
473 
474  validate_case_payload(result)
475  if verbose:
476  print(
477  " {case_name:<54} tier={tier:<11} category={category:<15} status={status}".format(
478  case_name=result["case_name"],
479  tier=result["support_tier"],
480  category=result["unsupported_category"],
481  status=result["status"].upper(),
482  )
483  )
484  return result
485 
486 
487 def run_validation(*, verbose=False):
488  return [
489  capture_unsupported_case(case, verbose=verbose)
490  for case in UNSUPPORTED_NOISE_BOUNDARY_CASES
491  ]
492 
493 
495  results = list(results)
496  support_tier_summary = build_support_tier_summary(results)
497  total_cases = len(results)
498  unsupported_cases = sum(case["status"] == "unsupported" for case in results)
499  error_match_count = sum(case["error_match_pass"] for case in results)
500  pre_execution_failure_count = sum(case["pre_execution_failure_pass"] for case in results)
501  boundary_passed_cases = sum(case["unsupported_boundary_pass"] for case in results)
502  bundle = {
503  "suite_name": SUITE_NAME,
504  "status": "pass"
505  if total_cases
506  and unsupported_cases == total_cases
507  and error_match_count == total_cases
508  and pre_execution_failure_count == total_cases
509  and boundary_passed_cases == total_cases
510  else "fail",
511  "backend": PRIMARY_BACKEND,
512  "requirements": build_requirement_metadata(),
513  "software": build_software_metadata(),
514  "summary": {
515  "total_cases": total_cases,
516  "unsupported_status_cases": unsupported_cases,
517  "unsupported_cases": unsupported_cases,
518  "failed_cases": total_cases - unsupported_cases,
519  "error_match_count": error_match_count,
520  "pre_execution_failure_count": pre_execution_failure_count,
521  "boundary_passed_cases": boundary_passed_cases,
522  **support_tier_summary,
523  "categories_present": sorted(
524  {case["unsupported_category"] for case in results}
525  ),
526  "boundary_classes_present": sorted(
527  {case["noise_boundary_class"] for case in results}
528  ),
529  "failure_stages_present": sorted(
530  {case["failure_stage"] for case in results}
531  ),
532  "first_unsupported_conditions": sorted(
533  {case["first_unsupported_condition"] for case in results}
534  ),
535  "silent_substitution_failures": sum(
536  case["silent_substitution_detected"] for case in results
537  ),
538  "silent_fallback_failures": sum(
539  case["silent_fallback_detected"] for case in results
540  ),
541  },
542  "cases": results,
543  }
545  return bundle
546 
547 
549  missing_fields = [field for field in ARTIFACT_CORE_FIELDS if field not in bundle]
550  if missing_fields:
551  raise ValueError(
552  "Unsupported-noise boundary bundle is missing required fields: {}".format(
553  ", ".join(missing_fields)
554  )
555  )
556 
557 
558 def write_artifact_bundle(output_path: Path, bundle):
560  output_path.parent.mkdir(parents=True, exist_ok=True)
561  output_path.write_text(
562  json.dumps(bundle, indent=2, sort_keys=True) + "\n",
563  encoding="utf-8",
564  )
565 
566 
568  parser = argparse.ArgumentParser(description=__doc__)
569  parser.add_argument(
570  "--output-dir",
571  type=Path,
572  default=DEFAULT_OUTPUT_DIR,
573  help="Directory for the unsupported-noise boundary JSON artifact bundle.",
574  )
575  parser.add_argument(
576  "--quiet",
577  action="store_true",
578  help="Suppress per-case progress output.",
579  )
580  return parser.parse_args()
581 
582 
583 def main():
584  args = parse_args()
585  results = run_validation(verbose=not args.quiet)
586  bundle = build_artifact_bundle(results)
587  output_path = args.output_dir / ARTIFACT_FILENAME
588  write_artifact_bundle(output_path, bundle)
589  print(
590  "Wrote {} with status {} (boundary passed: {}/{})".format(
591  output_path,
592  bundle["status"],
593  bundle["summary"]["boundary_passed_cases"],
594  bundle["summary"]["total_cases"],
595  )
596  )
597  if bundle["status"] != "pass":
598  raise SystemExit(1)
599 
600 
601 if __name__ == "__main__":
602  main()
def build_optimizer_config()
def build_open_chain_topology
def build_support_tier_summary(cases)
def capture_unsupported_case(case, verbose=False)
def classify_noise_boundary_reason
def build_software_metadata()