Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
required_local_noise_validation.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 """Validation: required local-noise positive slice.
3 
4 Runs one supported positive case per required Phase 2 local-noise model and
5 records reviewable bridge metadata proving that the requested local model is
6 what actually reaches the VQE-facing density path.
7 
8 This script is intentionally narrower than the broader exactness and
9 noise-support workflow validation package:
10 - it proves each required local model executes on the supported generated-HEA
11  density path,
12 - it records model identity, placement, and fixed value metadata,
13 - it marks every emitted case as part of the mandatory required-local-noise baseline,
14 - and it rejects whole-register depolarizing as a stand-in for the required
15  local-noise baseline.
16 
17 Run with:
18  python benchmarks/density_matrix/noise_support/required_local_noise_validation.py
19 """
20 
21 from __future__ import annotations
22 
23 import argparse
24 import json
25 import sys
26 from pathlib import Path
27 
28 import numpy as np
29 import qiskit
30 import qiskit_aer
31 from qiskit_aer import AerSimulator
32 
33 REPO_ROOT = Path(__file__).resolve().parents[3]
34 if str(REPO_ROOT) not in sys.path:
35  sys.path.insert(0, str(REPO_ROOT))
36 
37 from benchmarks.density_matrix.workflow_evidence.exact_density_vqe_validation import (
38  DEFAULT_ANSATZ,
39  DEFAULT_INNER_BLOCKS,
40  DEFAULT_LAYERS,
41  PRIMARY_BACKEND,
42  REFERENCE_BACKEND,
43  build_case_metadata,
44  build_software_metadata,
45  build_reference_bridge_metadata,
46  build_hamiltonian_metadata,
47  build_initial_parameters,
48  build_vqe,
49  density_energy,
50  insert_reference_noise,
51 )
52 from benchmarks.density_matrix.noise_support.support_tiers import (
53  SUPPORT_TIER_VOCABULARY,
54  build_required_case_classification,
55  build_support_tier_summary,
56 )
57 
58 SUITE_NAME = "required_local_noise_validation"
59 ARTIFACT_FILENAME = "required_local_noise_bundle.json"
60 DEFAULT_OUTPUT_DIR = REPO_ROOT / "benchmarks" / "density_matrix" / "artifacts" / "noise_support"
61 REQUIRED_LOCAL_NOISE_MODELS = (
62  "local_depolarizing",
63  "amplitude_damping",
64  "phase_damping",
65 )
66 REQUIRED_LOCAL_NOISE_CASES = (
67  {
68  "case_name": "required_local_noise_4q_local_depolarizing_positive",
69  "qbit_num": 4,
70  "requested_noise_channel": "local_depolarizing",
71  "expected_target": 0,
72  "expected_after_gate_index": 0,
73  "expected_value": 0.1,
74  "density_noise": [
75  {
76  "channel": "local_depolarizing",
77  "target": 0,
78  "after_gate_index": 0,
79  "error_rate": 0.1,
80  }
81  ],
82  },
83  {
84  "case_name": "required_local_noise_4q_amplitude_damping_positive",
85  "qbit_num": 4,
86  "requested_noise_channel": "amplitude_damping",
87  "expected_target": 1,
88  "expected_after_gate_index": 2,
89  "expected_value": 0.05,
90  "density_noise": [
91  {
92  "channel": "amplitude_damping",
93  "target": 1,
94  "after_gate_index": 2,
95  "gamma": 0.05,
96  }
97  ],
98  },
99  {
100  "case_name": "required_local_noise_4q_phase_damping_positive",
101  "qbit_num": 4,
102  "requested_noise_channel": "phase_damping",
103  "expected_target": 0,
104  "expected_after_gate_index": 4,
105  "expected_value": 0.07,
106  "density_noise": [
107  {
108  "channel": "phase_damping",
109  "target": 0,
110  "after_gate_index": 4,
111  "lambda": 0.07,
112  }
113  ],
114  },
115 )
116 
117 
119  return {
120  "source_type": "generated_hea",
121  "required_local_noise_models": list(REQUIRED_LOCAL_NOISE_MODELS),
122  "support_tier_vocabulary": list(SUPPORT_TIER_VOCABULARY),
123  "case_names": [case["case_name"] for case in REQUIRED_LOCAL_NOISE_CASES],
124  "workflow_qubits": sorted({case["qbit_num"] for case in REQUIRED_LOCAL_NOISE_CASES}),
125  "required_bridge_fields": [
126  "bridge_source_type",
127  "bridge_operation_count",
128  "bridge_noise_count",
129  "bridge_noise_sequence",
130  "bridge_noise_targets",
131  "bridge_noise_after_gate_indices",
132  "bridge_noise_fixed_values",
133  ],
134  }
135 
136 
138  required_fields = (
139  "case_name",
140  "status",
141  "backend",
142  "qbit_num",
143  "topology",
144  "ansatz",
145  "layers",
146  "inner_blocks",
147  "density_noise",
148  "requested_noise_channel",
149  "bridge_source_type",
150  "bridge_noise_sequence",
151  "bridge_noise_targets",
152  "bridge_noise_after_gate_indices",
153  "bridge_noise_fixed_values",
154  "positive_slice_pass",
155  "support_tier",
156  "case_purpose",
157  "counts_toward_mandatory_baseline",
158  )
159  missing_fields = [field for field in required_fields if field not in case]
160  if missing_fields:
161  raise ValueError(
162  "Required local-noise case artifact is missing required fields: {}".format(
163  ", ".join(missing_fields)
164  )
165  )
166 
167  if case["status"] not in {"pass", "fail"}:
168  raise ValueError(
169  "Required local-noise case artifact has unsupported status '{}'".format(
170  case["status"]
171  )
172  )
173 
174  if case["backend"] != PRIMARY_BACKEND:
175  raise ValueError(
176  "Required local-noise case artifact has unexpected backend '{}'".format(
177  case["backend"]
178  )
179  )
180 
181 
183  density_noise = [dict(item) for item in case["density_noise"]]
184  vqe, hamiltonian, topology = build_vqe(
185  case["qbit_num"],
186  density_noise=density_noise,
187  )
188  params = build_initial_parameters(vqe.get_Parameter_Num())
189  vqe.set_Optimized_Parameters(params)
190  bridge_metadata = build_reference_bridge_metadata(vqe)
191 
192  squander_energy = float(vqe.Optimization_Problem(params))
193 
194  noisy_qiskit_circuit = insert_reference_noise(vqe.get_Qiskit_Circuit(), density_noise)
195  simulator = AerSimulator(method="density_matrix")
196  result = simulator.run(noisy_qiskit_circuit, shots=1).result()
197  aer_rho = np.asarray(result.data()["density_matrix"])
198  aer_energy_real, aer_energy_imag = density_energy(hamiltonian, aer_rho)
199 
200  requested_channel = case["requested_noise_channel"]
201  noise_sequence = bridge_metadata["bridge_noise_sequence"]
202  noise_targets = bridge_metadata["bridge_noise_targets"]
203  noise_after_gate_indices = bridge_metadata["bridge_noise_after_gate_indices"]
204  noise_fixed_values = bridge_metadata["bridge_noise_fixed_values"]
205 
206  bridge_source_pass = bridge_metadata["bridge_source_type"] == "generated_hea"
207  bridge_noise_count_pass = bridge_metadata["bridge_noise_count"] == 1
208  bridge_noise_model_pass = noise_sequence == [requested_channel]
209  bridge_noise_target_pass = noise_targets == [case["expected_target"]]
210  bridge_noise_position_pass = noise_after_gate_indices == [
211  case["expected_after_gate_index"]
212  ]
213  bridge_noise_value_pass = bool(
214  len(noise_fixed_values) == 1
215  and np.isclose(
216  noise_fixed_values[0],
217  case["expected_value"],
218  atol=1e-12,
219  )
220  )
221  no_whole_register_substitute_pass = all(
222  noise_name != "depolarizing" for noise_name in noise_sequence
223  )
224  energy_is_finite = bool(np.isfinite(squander_energy))
225  positive_slice_pass = all(
226  (
227  bridge_source_pass,
228  bridge_noise_count_pass,
229  bridge_noise_model_pass,
230  bridge_noise_target_pass,
231  bridge_noise_position_pass,
232  bridge_noise_value_pass,
233  no_whole_register_substitute_pass,
234  energy_is_finite,
235  )
236  )
237 
238  artifact = build_case_metadata(
239  backend=PRIMARY_BACKEND,
240  qbit_num=case["qbit_num"],
241  topology=topology,
242  density_noise=density_noise,
243  ansatz=DEFAULT_ANSATZ,
244  layers=DEFAULT_LAYERS,
245  inner_blocks=DEFAULT_INNER_BLOCKS,
246  reference_backend=REFERENCE_BACKEND,
247  hamiltonian=build_hamiltonian_metadata(),
248  )
249  artifact.update(
250  {
251  "case_name": case["case_name"],
252  "status": "pass" if positive_slice_pass else "fail",
253  "requested_noise_channel": requested_channel,
254  "parameter_vector": params.tolist(),
255  "squander_energy": squander_energy,
256  "aer_energy_real": aer_energy_real,
257  "aer_energy_imag": aer_energy_imag,
258  "absolute_energy_error": abs(squander_energy - aer_energy_real),
259  "bridge_source_pass": bridge_source_pass,
260  "bridge_noise_count_pass": bridge_noise_count_pass,
261  "bridge_noise_model_pass": bridge_noise_model_pass,
262  "bridge_noise_target_pass": bridge_noise_target_pass,
263  "bridge_noise_position_pass": bridge_noise_position_pass,
264  "bridge_noise_value_pass": bridge_noise_value_pass,
265  "no_whole_register_substitute_pass": no_whole_register_substitute_pass,
266  "energy_is_finite": energy_is_finite,
267  "positive_slice_pass": positive_slice_pass,
268  **bridge_metadata,
270  }
271  )
272  validate_case_artifact(artifact)
273  return artifact
274 
275 
276 def run_validation(*, verbose: bool = False):
277  results = []
278  for case in REQUIRED_LOCAL_NOISE_CASES:
279  result = run_required_local_noise_case(case)
280  results.append(result)
281  if verbose:
282  print(
283  "{} [{}] requested={} sequence={}".format(
284  result["case_name"],
285  result["status"],
286  result["requested_noise_channel"],
287  result["bridge_noise_sequence"],
288  )
289  )
290  return results
291 
292 
294  results = list(results)
295  passed_cases = sum(case["status"] == "pass" for case in results)
296  total_cases = len(results)
297  pass_rate = (passed_cases / total_cases) if total_cases else 0.0
298  support_tier_summary = build_support_tier_summary(results)
299 
300  bundle = {
301  "suite_name": SUITE_NAME,
302  "status": "pass"
303  if support_tier_summary["mandatory_baseline_completed"]
304  and passed_cases == total_cases
305  and total_cases
306  else "fail",
307  "backend": PRIMARY_BACKEND,
308  "reference_backend": REFERENCE_BACKEND,
309  "requirements": build_requirement_metadata(),
310  "software": build_software_metadata(),
311  "summary": {
312  "total_cases": total_cases,
313  "passed_cases": passed_cases,
314  "failed_cases": total_cases - passed_cases,
315  "pass_rate": pass_rate,
316  **support_tier_summary,
317  "whole_register_substitute_failures": sum(
318  not case["no_whole_register_substitute_pass"] for case in results
319  ),
320  "reviewable_bridge_cases": sum(
321  case["bridge_noise_model_pass"]
322  and case["bridge_noise_target_pass"]
323  and case["bridge_noise_position_pass"]
324  and case["bridge_noise_value_pass"]
325  for case in results
326  ),
327  },
328  "cases": results,
329  }
330  return bundle
331 
332 
333 def write_artifact_bundle(path: Path, bundle):
334  path.parent.mkdir(parents=True, exist_ok=True)
335  path.write_text(json.dumps(bundle, indent=2, sort_keys=True) + "\n")
336 
337 
339  parser = argparse.ArgumentParser(
340  description="Run required local-noise validation."
341  )
342  parser.add_argument(
343  "--output-dir",
344  type=Path,
345  default=DEFAULT_OUTPUT_DIR,
346  help="Directory for the emitted JSON bundle.",
347  )
348  parser.add_argument(
349  "--quiet",
350  action="store_true",
351  help="Suppress per-case progress output.",
352  )
353  return parser.parse_args()
354 
355 
356 def main():
357  args = parse_args()
358  results = run_validation(verbose=not args.quiet)
359  bundle = build_artifact_bundle(results)
360  output_path = args.output_dir / ARTIFACT_FILENAME
361  write_artifact_bundle(output_path, bundle)
362  print(
363  "Wrote {} with status {} ({}/{})".format(
364  output_path,
365  bundle["status"],
366  bundle["summary"]["passed_cases"],
367  bundle["summary"]["total_cases"],
368  )
369  )
370 
371 
372 if __name__ == "__main__":
373  main()
def build_support_tier_summary(cases)
def build_required_case_classification()
def build_software_metadata()