Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
noisy_runtime_channel_native.py
Go to the documentation of this file.
1 """Phase 3.1 channel-native fused motifs (Kraus composition, slice v1)."""
2 
3 from __future__ import annotations
4 
5 from typing import Literal
6 
7 import numpy as np
8 
9 from squander.density_matrix import DensityMatrix
10 from squander.partitioning.noisy_runtime_core import (
11  PHASE3_FUSION_CLASS_FUSED,
12  PHASE31_FUSION_KIND_CHANNEL_NATIVE_MOTIF,
13  NoisyRuntimeFusedRegionRecord,
14  _segment_parameter_vector,
15 )
16 from squander.partitioning.noisy_runtime_errors import runtime_validation_error
17 from squander.partitioning.noisy_runtime_fusion import (
18  _embed_cnot_gate,
19  _embed_single_qubit_gate,
20  _kernel_indices_for_fused_cnot,
21 )
22 from squander.partitioning.noisy_validation_errors import NoisyRuntimeValidationError
23 from squander.partitioning.noisy_types import (
24  PLANNER_OP_KIND_GATE,
25  PLANNER_OP_KIND_NOISE,
26  NoisyPartitionDescriptor,
27  NoisyPartitionDescriptorMember,
28  NoisyPartitionDescriptorSet,
29 )
30 
31 # P31-ADR-008 style (representation-level equality residuals)
32 _PHASE31_KRAUS_COMPLETENESS_TOL = 1e-10
33 _PHASE31_CHOI_POSITIVITY_FLOOR = -1e-12
34 
35 _PAULI_X = np.array([[0, 1], [1, 0]], dtype=np.complex128)
36 _PAULI_Y = np.array([[0, -1j], [1j, 0]], dtype=np.complex128)
37 _PAULI_Z = np.array([[1, 0], [0, -1]], dtype=np.complex128)
38 
39 
41  support_qubit_count: int,
42  *,
43  descriptor_set: NoisyPartitionDescriptorSet,
44  runtime_path: str,
45 ) -> int:
46  """Hilbert-space dimension d = 2**n for bounded local support n in {1, 2}."""
47  if support_qubit_count == 1:
48  return 2
49  if support_qubit_count == 2:
50  return 4
52  descriptor_set,
53  category="unsupported_runtime_execution",
54  first_unsupported_condition="channel_native_representation",
55  failure_stage="runtime_preflight",
56  runtime_path=runtime_path,
57  reason=(
58  "Channel-native Kraus operator dimension supports local support "
59  "qubit count 1 or 2 only, got {}".format(support_qubit_count)
60  ),
61  )
62 
63 
65  bundle: np.ndarray,
66  *,
67  descriptor_set: NoisyPartitionDescriptorSet,
68  runtime_path: str,
69  label: str = "kraus_bundle",
70 ) -> int:
71  """Require rank-3 (K, d, d) with square Kraus matrices and d in {2, 4}."""
72  if bundle.ndim != 3:
74  descriptor_set,
75  category="unsupported_runtime_execution",
76  first_unsupported_condition="channel_native_representation",
77  failure_stage="runtime_preflight",
78  runtime_path=runtime_path,
79  reason=(
80  "Channel-native {} must be rank-3 (kraus_count, d, d), got ndim={}".format(
81  label, bundle.ndim
82  )
83  ),
84  )
85  if bundle.shape[0] < 1:
87  descriptor_set,
88  category="unsupported_runtime_execution",
89  first_unsupported_condition="channel_native_representation",
90  failure_stage="runtime_preflight",
91  runtime_path=runtime_path,
92  reason="Channel-native {} must have at least one Kraus operator".format(
93  label
94  ),
95  )
96  d = int(bundle.shape[1])
97  if bundle.shape[2] != d:
99  descriptor_set,
100  category="unsupported_runtime_execution",
101  first_unsupported_condition="channel_native_representation",
102  failure_stage="runtime_preflight",
103  runtime_path=runtime_path,
104  reason=(
105  "Channel-native {} Kraus matrices must be square, got shape {}".format(
106  label, bundle.shape
107  )
108  ),
109  )
110  if d not in (2, 4):
112  descriptor_set,
113  category="unsupported_runtime_execution",
114  first_unsupported_condition="channel_native_representation",
115  failure_stage="runtime_preflight",
116  runtime_path=runtime_path,
117  reason=(
118  "Channel-native {} supports operator dimension d in {{2, 4}} only, got d={}".format(
119  label, d
120  )
121  ),
122  )
123  return d
124 
125 
127  support_qubit_count: int,
128  *,
129  descriptor_set: NoisyPartitionDescriptorSet,
130  runtime_path: str,
131 ) -> np.ndarray:
133  support_qubit_count,
134  descriptor_set=descriptor_set,
135  runtime_path=runtime_path,
136  )
137  bundle = np.array([np.eye(d, dtype=np.complex128)])
139  bundle,
140  descriptor_set=descriptor_set,
141  runtime_path=runtime_path,
142  label="identity kraus bundle",
143  )
144  return bundle
145 
146 
147 def _clamp_noise_rate_to_unit_interval(x: float) -> float:
148  """Match LocalDepolarizingOp / AmplitudeDampingOp / PhaseDampingOp parametric clamp in noise_operation.cpp."""
149  v = float(x)
150  if v < 0.0:
151  return 0.0
152  if v > 1.0:
153  return 1.0
154  return v
155 
156 
157 def _u3_unitary(theta_over_2: float, phi: float, lam: float) -> np.ndarray:
158  return np.asarray(
159  [
160  [np.cos(theta_over_2), -np.exp(1j * lam) * np.sin(theta_over_2)],
161  [
162  np.exp(1j * phi) * np.sin(theta_over_2),
163  np.exp(1j * (phi + lam)) * np.cos(theta_over_2),
164  ],
165  ],
166  dtype=np.complex128,
167  )
168 
169 
171  acc: np.ndarray,
172  nxt: np.ndarray,
173  *,
174  descriptor_set: NoisyPartitionDescriptorSet,
175  runtime_path: str,
176 ) -> np.ndarray:
177  """Φ_nxt ∘ Φ_acc has Kraus operators N_b A_a (apply acc first, then nxt)."""
179  acc,
180  descriptor_set=descriptor_set,
181  runtime_path=runtime_path,
182  label="compose acc",
183  )
185  nxt,
186  descriptor_set=descriptor_set,
187  runtime_path=runtime_path,
188  label="compose nxt",
189  )
190  if d_acc != d_nxt:
192  descriptor_set,
193  category="unsupported_runtime_execution",
194  first_unsupported_condition="channel_native_representation",
195  failure_stage="runtime_preflight",
196  runtime_path=runtime_path,
197  reason=(
198  "Channel-native Kraus bundle dimension mismatch in composition: "
199  "d_acc={} d_nxt={}".format(d_acc, d_nxt)
200  ),
201  )
202  na = acc.shape[0]
203  nb = nxt.shape[0]
204  d = d_acc
205  out = np.empty((na * nb, d, d), dtype=np.complex128)
206  idx = 0
207  for b in range(nb):
208  for a in range(na):
209  out[idx] = nxt[b] @ acc[a]
210  idx += 1
211  return out
212 
213 
214 def _kraus_bundle_u3(theta: float, phi: float, lam: float) -> np.ndarray:
215  u = _u3_unitary(float(theta), float(phi), float(lam))
216  return np.array([u], dtype=np.complex128)
217 
218 
219 def _kraus_bundle_local_depolarizing(p: float) -> np.ndarray:
220  """Match LocalDepolarizingOp in noise_operation.cpp (identity + Pauli weights)."""
221  p = float(p)
222  s0 = np.sqrt(max(0.0, 1.0 - 0.75 * p))
223  sp = np.sqrt(max(0.0, p)) / 2.0
224  return np.stack(
225  [
226  s0 * np.eye(2, dtype=np.complex128),
227  sp * _PAULI_X,
228  sp * _PAULI_Y,
229  sp * _PAULI_Z,
230  ]
231  )
232 
233 
234 def _kraus_bundle_amplitude_damping(gamma: float) -> np.ndarray:
235  g = float(gamma)
236  sqrt_1mg = np.sqrt(max(0.0, 1.0 - g))
237  sqrt_g = np.sqrt(max(0.0, g))
238  k0 = np.array([[1, 0], [0, sqrt_1mg]], dtype=np.complex128)
239  k1 = np.array([[0, sqrt_g], [0, 0]], dtype=np.complex128)
240  return np.stack([k0, k1])
241 
242 
243 def _kraus_bundle_phase_damping(lam: float) -> np.ndarray:
244  """Kraus form equivalent to PhaseDampingOp::apply_phase_damping (diag unchanged)."""
245  lmb = float(lam)
246  a = np.sqrt(max(0.0, 1.0 - lmb))
247  b = np.sqrt(max(0.0, lmb))
248  k0 = np.array([[1, 0], [0, a]], dtype=np.complex128)
249  k1 = np.array([[0, 0], [0, b]], dtype=np.complex128)
250  return np.stack([k0, k1])
251 
252 
254  bundle: np.ndarray,
255  *,
256  descriptor_set: NoisyPartitionDescriptorSet,
257  runtime_path: str,
258 ) -> None:
260  bundle,
261  descriptor_set=descriptor_set,
262  runtime_path=runtime_path,
263  label="invariant check",
264  )
265  acc = np.zeros((d, d), dtype=np.complex128)
266  for k in range(bundle.shape[0]):
267  kj = bundle[k]
268  acc += kj.conj().T @ kj
269  res = float(np.linalg.norm(acc - np.eye(d, dtype=np.complex128), ord="fro"))
270  if res > _PHASE31_KRAUS_COMPLETENESS_TOL:
272  descriptor_set,
273  category="unsupported_runtime_execution",
274  first_unsupported_condition="channel_native_invariant_failure",
275  failure_stage="runtime_preflight",
276  runtime_path=runtime_path,
277  reason=(
278  "Channel-native Kraus completeness residual {:.3e} exceeds tol {:.3e}".format(
279  res, _PHASE31_KRAUS_COMPLETENESS_TOL
280  )
281  ),
282  )
283  choi = np.zeros((d * d, d * d), dtype=np.complex128)
284  for k in range(bundle.shape[0]):
285  v = np.reshape(bundle[k], (d * d,), order="C")
286  choi += np.outer(v, v.conj())
287  choi = (choi + choi.conj().T) / 2.0
288  min_eig = float(np.min(np.real(np.linalg.eigvalsh(choi))))
289  if min_eig < _PHASE31_CHOI_POSITIVITY_FLOOR:
291  descriptor_set,
292  category="unsupported_runtime_execution",
293  first_unsupported_condition="channel_native_invariant_failure",
294  failure_stage="runtime_preflight",
295  runtime_path=runtime_path,
296  reason=(
297  "Channel-native Choi minimum eigenvalue {:.3e} below floor {:.3e}".format(
298  min_eig, _PHASE31_CHOI_POSITIVITY_FLOOR
299  )
300  ),
301  )
302 
303 
305  op: np.ndarray,
306  *,
307  qbit_num: int,
308  g0: int,
309  g1: int,
310 ) -> np.ndarray:
311  """Embed a 4×4 operator on global qubits ``g0`` (local index 0) and ``g1`` (local index 1).
312 
313  Basis convention matches ``_embed_single_qubit_gate``: global state index bit ``k`` is qubit ``k``.
314  Subsystem row/column index is ``b_g0 + 2 * b_g1``.
315  """
316  dim = 1 << qbit_num
317  mask_all = dim - 1
318  rest_mask = mask_all & ~((1 << g0) | (1 << g1))
319  embedded = np.zeros((dim, dim), dtype=np.complex128)
320  for r in range(dim):
321  for c in range(dim):
322  if (r & rest_mask) != (c & rest_mask):
323  continue
324  sub_r = ((r >> g0) & 1) + 2 * ((r >> g1) & 1)
325  sub_c = ((c >> g0) & 1) + 2 * ((c >> g1) & 1)
326  embedded[r, c] = op[sub_r, sub_c]
327  return embedded
328 
329 
331  bundle: np.ndarray,
332  rho: DensityMatrix,
333  *,
334  qbit_num: int,
335  local_support: tuple[int, ...],
336  global_target_qbits: tuple[int, ...],
337  descriptor_set: NoisyPartitionDescriptorSet,
338  runtime_path: str,
339 ) -> DensityMatrix:
341  bundle,
342  descriptor_set=descriptor_set,
343  runtime_path=runtime_path,
344  label="apply",
345  )
346  if len(local_support) != len(global_target_qbits):
348  descriptor_set,
349  category="unsupported_runtime_execution",
350  first_unsupported_condition="channel_native_representation",
351  failure_stage="runtime_preflight",
352  runtime_path=runtime_path,
353  reason=(
354  "Channel-native apply local_support length {} does not match "
355  "global_target_qbits length {}".format(
356  len(local_support), len(global_target_qbits)
357  )
358  ),
359  )
360  if len(local_support) > 2:
362  descriptor_set,
363  category="unsupported_runtime_execution",
364  first_unsupported_condition="channel_native_representation",
365  failure_stage="runtime_preflight",
366  runtime_path=runtime_path,
367  reason=(
368  "Channel-native apply supports at most two local qubits, got {}".format(
369  len(local_support)
370  )
371  ),
372  )
373  if (1 << len(local_support)) != d:
375  descriptor_set,
376  category="unsupported_runtime_execution",
377  first_unsupported_condition="channel_native_representation",
378  failure_stage="runtime_preflight",
379  runtime_path=runtime_path,
380  reason=(
381  "Channel-native apply bundle dimension d={} inconsistent with "
382  "local support width {}".format(d, len(local_support))
383  ),
384  )
385  for g in global_target_qbits:
386  if g < 0 or g >= qbit_num:
388  descriptor_set,
389  category="unsupported_runtime_execution",
390  first_unsupported_condition="channel_native_representation",
391  failure_stage="runtime_preflight",
392  runtime_path=runtime_path,
393  reason=(
394  "Channel-native apply global target qubit {} out of range for qbit_num={}".format(
395  g, qbit_num
396  )
397  ),
398  )
399  if d == 4 and global_target_qbits[0] == global_target_qbits[1]:
401  descriptor_set,
402  category="unsupported_runtime_execution",
403  first_unsupported_condition="channel_native_representation",
404  failure_stage="runtime_preflight",
405  runtime_path=runtime_path,
406  reason="Channel-native apply requires two distinct global qubits for a 2-qubit bundle",
407  )
408 
409  rho_np = np.asarray(rho.to_numpy(), dtype=np.complex128)
410  dim = 1 << qbit_num
411  if rho_np.shape != (dim, dim):
413  descriptor_set,
414  category="unsupported_runtime_execution",
415  first_unsupported_condition="channel_native_representation",
416  failure_stage="runtime_preflight",
417  runtime_path=runtime_path,
418  reason=(
419  "Channel-native apply density matrix shape {} does not match qbit_num={}".format(
420  rho_np.shape, qbit_num
421  )
422  ),
423  )
424 
425  if (
426  qbit_num == 1
427  and len(local_support) == 1
428  and d == 2
429  and global_target_qbits == (0,)
430  ):
431  out = np.zeros((2, 2), dtype=np.complex128)
432  for k in range(bundle.shape[0]):
433  kj = bundle[k]
434  out += kj @ rho_np @ kj.conj().T
435  return DensityMatrix.from_numpy(out)
436 
437  out = np.zeros((dim, dim), dtype=np.complex128)
438  for k in range(bundle.shape[0]):
439  kj = bundle[k]
440  if d == 2:
441  k_full = _embed_single_qubit_gate(
442  kj,
443  total_kernel_qbits=qbit_num,
444  kernel_target_qbit=global_target_qbits[0],
445  )
446  else:
448  kj,
449  qbit_num=qbit_num,
450  g0=global_target_qbits[0],
451  g1=global_target_qbits[1],
452  )
453  out += k_full @ rho_np @ k_full.conj().T
454  return DensityMatrix.from_numpy(out)
455 
456 
457 _CHANNEL_NATIVE_SLICE_ALLOWED_OPS = frozenset(
458  {"U3", "CNOT", "local_depolarizing", "amplitude_damping", "phase_damping"}
459 )
460 
461 
463  descriptor_set: NoisyPartitionDescriptorSet,
464  partition: NoisyPartitionDescriptor,
465 ) -> tuple[Literal["eligible"], tuple[int, ...]] | tuple[Literal["route"], str]:
466  """Return eligible local support, or a frozen hybrid route reason (no exceptions)."""
467  members = partition.members
468  if not members:
469  return ("route", "channel_native_support_surface")
470  noise_seen = False
471  local_support: set[int] = set()
472  for member in members:
473  op = descriptor_set.canonical_operation_for(member)
474  if op.name not in _CHANNEL_NATIVE_SLICE_ALLOWED_OPS:
475  return ("route", "channel_native_support_surface")
476  if op.kind == PLANNER_OP_KIND_NOISE:
477  noise_seen = True
478  elif op.kind != PLANNER_OP_KIND_GATE:
479  return ("route", "channel_native_support_surface")
480  local_support.update(member.local_qubit_support)
481  if not noise_seen:
482  return ("route", "pure_unitary_partition")
483  if len(local_support) > 2:
484  return ("route", "channel_native_qubit_span")
485  for member in members:
486  for lb in member.local_qubit_support:
487  if lb not in local_support:
488  return ("route", "channel_native_support_surface")
489  return ("eligible", tuple(sorted(local_support)))
490 
491 
493  descriptor_set: NoisyPartitionDescriptorSet,
494  partition: NoisyPartitionDescriptor,
495  *,
496  runtime_path: str,
497 ) -> tuple[bool, tuple[int, ...] | None, str]:
498  """Preflight classification for hybrid routing (strict execution unchanged).
499 
500  Returns ``(eligible, local_support_or_none, route_reason)``. When ``eligible``
501  is True, ``route_reason`` is ``eligible_channel_native_motif`` and
502  ``local_support`` is the motif support. When ``eligible`` is False, the
503  partition should be executed via the shipped Phase 3 path with
504  ``partition_route_reason`` set to the returned frozen reason string.
505  """
506  del runtime_path # reserved for future diagnostics parity with strict path
507  scan = _scan_channel_native_whole_partition_motif(descriptor_set, partition)
508  if scan[0] == "eligible":
509  return (True, scan[1], "eligible_channel_native_motif")
510  return (False, None, scan[1])
511 
512 
514  descriptor_set: NoisyPartitionDescriptorSet,
515  partition: NoisyPartitionDescriptor,
516  *,
517  runtime_path: str,
518 ) -> tuple[int, ...]:
519  scan = _scan_channel_native_whole_partition_motif(descriptor_set, partition)
520  if scan[0] == "eligible":
521  return scan[1]
522  route_reason = scan[1]
523  if route_reason == "pure_unitary_partition":
525  descriptor_set,
526  category="unsupported_runtime_operation",
527  first_unsupported_condition="channel_native_noise_presence",
528  failure_stage="runtime_preflight",
529  runtime_path=runtime_path,
530  reason="Channel-native counted motif requires at least one noise operation",
531  )
532  if route_reason == "channel_native_qubit_span":
534  descriptor_set,
535  category="unsupported_runtime_operation",
536  first_unsupported_condition="channel_native_qubit_span",
537  failure_stage="runtime_preflight",
538  runtime_path=runtime_path,
539  reason="Channel-native slice supports at most two local qubits in the motif",
540  )
541  members = partition.members
542  if not members:
544  descriptor_set,
545  category="unsupported_runtime_operation",
546  first_unsupported_condition="channel_native_support_surface",
547  failure_stage="runtime_preflight",
548  runtime_path=runtime_path,
549  reason="Channel-native slice requires non-empty partition members",
550  )
551  allowed = _CHANNEL_NATIVE_SLICE_ALLOWED_OPS
552  for member in members:
553  op = descriptor_set.canonical_operation_for(member)
554  if op.name not in allowed:
556  descriptor_set,
557  category="unsupported_runtime_operation",
558  first_unsupported_condition="channel_native_support_surface",
559  failure_stage="runtime_preflight",
560  runtime_path=runtime_path,
561  reason="Channel-native slice does not support operation '{}'".format(
562  op.name
563  ),
564  )
565  if op.kind != PLANNER_OP_KIND_GATE and op.kind != PLANNER_OP_KIND_NOISE:
567  descriptor_set,
568  category="unsupported_runtime_operation",
569  first_unsupported_condition="channel_native_support_surface",
570  failure_stage="runtime_preflight",
571  runtime_path=runtime_path,
572  reason="Channel-native slice expected gate or noise kind",
573  )
575  descriptor_set,
576  category="unsupported_runtime_operation",
577  first_unsupported_condition="channel_native_support_surface",
578  failure_stage="runtime_preflight",
579  runtime_path=runtime_path,
580  reason="Member local qubit not contained in motif support",
581  )
582 
583 
584 def _local_qbit_to_kernel_index(local_support: tuple[int, ...]) -> dict[int, int]:
585  return {lq: idx for idx, lq in enumerate(local_support)}
586 
587 
589  member: NoisyPartitionDescriptorMember,
590  *,
591  local_support: tuple[int, ...],
592  descriptor_set: NoisyPartitionDescriptorSet,
593  runtime_path: str,
594 ) -> np.ndarray:
595  if len(local_support) != 2:
597  descriptor_set,
598  category="unsupported_runtime_execution",
599  first_unsupported_condition="channel_native_representation",
600  failure_stage="runtime_preflight",
601  runtime_path=runtime_path,
602  reason=(
603  "Channel-native CNOT lowering requires local support width 2, got {}".format(
604  len(local_support)
605  )
606  ),
607  )
608  if member.local_control_qbit is None or member.local_target_qbit is None:
610  descriptor_set,
611  category="descriptor_to_runtime_mismatch",
612  first_unsupported_condition="local_control_qbit",
613  failure_stage="runtime_preflight",
614  runtime_path=runtime_path,
615  reason="Channel-native CNOT requires local control and target qubits",
616  )
617  for wire in (member.local_control_qbit, member.local_target_qbit):
618  if wire not in local_support:
620  descriptor_set,
621  category="descriptor_to_runtime_mismatch",
622  first_unsupported_condition="local_target_qbit",
623  failure_stage="runtime_preflight",
624  runtime_path=runtime_path,
625  reason="Channel-native CNOT wire {} not in motif local_support".format(
626  wire
627  ),
628  )
629  lmap = _local_qbit_to_kernel_index(local_support)
630  k_ctl, k_tgt = _kernel_indices_for_fused_cnot(
631  local_qbit_to_kernel_index=lmap,
632  local_target_qbit=member.local_target_qbit,
633  local_control_qbit=member.local_control_qbit,
634  )
635  u = _embed_cnot_gate(
636  total_kernel_qbits=2,
637  kernel_control_qbit=k_ctl,
638  kernel_target_qbit=k_tgt,
639  )
640  return np.array([u], dtype=np.complex128)
641 
642 
644  bundle_1q: np.ndarray,
645  *,
646  target_local_qbit: int | None,
647  local_support: tuple[int, ...],
648  descriptor_set: NoisyPartitionDescriptorSet,
649  runtime_path: str,
650  label: str = "single-qubit kraus bundle",
651 ) -> np.ndarray:
652  n = len(local_support)
653  if n == 1:
655  bundle_1q,
656  descriptor_set=descriptor_set,
657  runtime_path=runtime_path,
658  label=label,
659  )
660  if d != 2:
662  descriptor_set,
663  category="unsupported_runtime_execution",
664  first_unsupported_condition="channel_native_representation",
665  failure_stage="runtime_preflight",
666  runtime_path=runtime_path,
667  reason="Channel-native {} on 1-qubit support must have d=2".format(
668  label
669  ),
670  )
671  return bundle_1q
672  if n == 2:
673  if target_local_qbit is None:
675  descriptor_set,
676  category="descriptor_to_runtime_mismatch",
677  first_unsupported_condition="local_target_qbit",
678  failure_stage="runtime_preflight",
679  runtime_path=runtime_path,
680  reason=(
681  "Channel-native {} lift to 2-qubit support requires "
682  "local_target_qbit".format(label)
683  ),
684  )
685  if target_local_qbit not in local_support:
687  descriptor_set,
688  category="descriptor_to_runtime_mismatch",
689  first_unsupported_condition="local_target_qbit",
690  failure_stage="runtime_preflight",
691  runtime_path=runtime_path,
692  reason="Channel-native {} target local qubit {} not in support".format(
693  label, target_local_qbit
694  ),
695  )
697  bundle_1q,
698  descriptor_set=descriptor_set,
699  runtime_path=runtime_path,
700  label=label,
701  )
702  if d != 2:
704  descriptor_set,
705  category="unsupported_runtime_execution",
706  first_unsupported_condition="channel_native_representation",
707  failure_stage="runtime_preflight",
708  runtime_path=runtime_path,
709  reason="Channel-native {} before lift must have d=2".format(label),
710  )
711  kernel_idx = local_support.index(target_local_qbit)
712  kcnt = bundle_1q.shape[0]
713  out = np.empty((kcnt, 4, 4), dtype=np.complex128)
714  for i in range(kcnt):
715  out[i] = _embed_single_qubit_gate(
716  bundle_1q[i],
717  total_kernel_qbits=2,
718  kernel_target_qbit=kernel_idx,
719  )
720  return out
722  descriptor_set,
723  category="unsupported_runtime_execution",
724  first_unsupported_condition="channel_native_representation",
725  failure_stage="runtime_preflight",
726  runtime_path=runtime_path,
727  reason=(
728  "Channel-native single-qubit bundle lift supports width 1 or 2 only, got {}".format(
729  n
730  )
731  ),
732  )
733 
734 
736  descriptor_set: NoisyPartitionDescriptorSet,
737  member: NoisyPartitionDescriptorMember,
738  segment_parameters: np.ndarray,
739  *,
740  local_support: tuple[int, ...],
741  runtime_path: str,
742 ) -> np.ndarray:
743  op = descriptor_set.canonical_operation_for(member)
744  if op.kind == PLANNER_OP_KIND_GATE:
745  if op.name == "U3":
746  if member.local_target_qbit is None:
748  descriptor_set,
749  category="descriptor_to_runtime_mismatch",
750  first_unsupported_condition="local_target_qbit",
751  failure_stage="runtime_preflight",
752  runtime_path=runtime_path,
753  reason="Channel-native requires local_target_qbit for U3",
754  )
755  local_start = member.local_param_start
756  local_stop = local_start + op.param_count
757  theta, phi, lam = segment_parameters[local_start:local_stop]
758  base = _kraus_bundle_u3(float(theta), float(phi), float(lam))
760  base,
761  target_local_qbit=member.local_target_qbit,
762  local_support=local_support,
763  descriptor_set=descriptor_set,
764  runtime_path=runtime_path,
765  label="U3 kraus bundle",
766  )
767  if op.name == "CNOT":
769  member,
770  local_support=local_support,
771  descriptor_set=descriptor_set,
772  runtime_path=runtime_path,
773  )
774  if op.kind == PLANNER_OP_KIND_NOISE:
775  local_start = member.local_param_start
776  local_stop = local_start + op.param_count
777  if op.name == "local_depolarizing":
778  if op.param_count == 0:
779  p = _clamp_noise_rate_to_unit_interval(float(op.fixed_value))
780  else:
781  if local_stop > segment_parameters.size:
783  descriptor_set,
784  category="descriptor_to_runtime_mismatch",
785  first_unsupported_condition="parameter_routing",
786  failure_stage="runtime_preflight",
787  runtime_path=runtime_path,
788  reason="Channel-native noise parameters out of range",
789  )
791  float(segment_parameters[local_start])
792  )
795  base,
796  target_local_qbit=member.local_target_qbit,
797  local_support=local_support,
798  descriptor_set=descriptor_set,
799  runtime_path=runtime_path,
800  label="local_depolarizing kraus bundle",
801  )
802  if op.name == "amplitude_damping":
803  if op.param_count == 0:
804  g = _clamp_noise_rate_to_unit_interval(float(op.fixed_value))
805  else:
806  if local_stop > segment_parameters.size:
808  descriptor_set,
809  category="descriptor_to_runtime_mismatch",
810  first_unsupported_condition="parameter_routing",
811  failure_stage="runtime_preflight",
812  runtime_path=runtime_path,
813  reason="Channel-native noise parameters out of range",
814  )
816  float(segment_parameters[local_start])
817  )
820  base,
821  target_local_qbit=member.local_target_qbit,
822  local_support=local_support,
823  descriptor_set=descriptor_set,
824  runtime_path=runtime_path,
825  label="amplitude_damping kraus bundle",
826  )
827  if op.name == "phase_damping":
828  if op.param_count == 0:
829  lam = _clamp_noise_rate_to_unit_interval(float(op.fixed_value))
830  else:
831  if local_stop > segment_parameters.size:
833  descriptor_set,
834  category="descriptor_to_runtime_mismatch",
835  first_unsupported_condition="parameter_routing",
836  failure_stage="runtime_preflight",
837  runtime_path=runtime_path,
838  reason="Channel-native noise parameters out of range",
839  )
841  float(segment_parameters[local_start])
842  )
843  base = _kraus_bundle_phase_damping(lam)
845  base,
846  target_local_qbit=member.local_target_qbit,
847  local_support=local_support,
848  descriptor_set=descriptor_set,
849  runtime_path=runtime_path,
850  label="phase_damping kraus bundle",
851  )
853  descriptor_set,
854  category="unsupported_runtime_operation",
855  first_unsupported_condition="channel_native_support_surface",
856  failure_stage="runtime_preflight",
857  runtime_path=runtime_path,
858  reason="Channel-native cannot lower operation '{}'".format(op.name),
859  )
860 
861 
863  partition: NoisyPartitionDescriptor,
864  local_support: tuple[int, ...],
865 ) -> tuple[int, ...]:
866  return tuple(partition.local_to_global_qbits[lq] for lq in local_support)
867 
868 
870  descriptor_set: NoisyPartitionDescriptorSet,
871  partition: NoisyPartitionDescriptor,
872  members: tuple[NoisyPartitionDescriptorMember, ...],
873  *,
874  kraus_count: int,
875 ) -> NoisyRuntimeFusedRegionRecord:
876  active_local_qbits = tuple(
877  sorted({lq for m in members for lq in m.local_qubit_support})
878  )
879  global_target_qbits = tuple(
880  partition.local_to_global_qbits[lq] for lq in active_local_qbits
881  )
882  return NoisyRuntimeFusedRegionRecord(
883  partition_index=partition.partition_index,
884  candidate_kind=PHASE31_FUSION_KIND_CHANNEL_NATIVE_MOTIF,
885  classification=PHASE3_FUSION_CLASS_FUSED,
886  reason="channel_native_motif_kraus_count_{}".format(kraus_count),
887  partition_member_indices=tuple(m.partition_member_index for m in members),
888  canonical_operation_indices=tuple(m.canonical_operation_index for m in members),
889  operation_names=tuple(
890  descriptor_set.canonical_operation_for(m).name for m in members
891  ),
892  global_target_qbits=global_target_qbits,
893  local_target_qbits=active_local_qbits,
894  member_count=len(members),
895  gate_count=sum(
896  1
897  for m in members
898  if descriptor_set.canonical_operation_for(m).kind == PLANNER_OP_KIND_GATE
899  ),
900  )
901 
902 
904  descriptor_set: NoisyPartitionDescriptorSet,
905  partition: NoisyPartitionDescriptor,
906  local_parameter_vector: np.ndarray,
907  rho: DensityMatrix,
908  *,
909  runtime_path: str,
910 ) -> tuple[tuple[NoisyRuntimeFusedRegionRecord, ...], DensityMatrix]:
911  members = partition.members
912  local_support = _validate_whole_partition_motif(
913  descriptor_set, partition, runtime_path=runtime_path
914  )
915  segment_parameters = _segment_parameter_vector(
916  descriptor_set,
917  members,
918  local_parameter_vector,
919  runtime_path=runtime_path,
920  )
922  len(local_support),
923  descriptor_set=descriptor_set,
924  runtime_path=runtime_path,
925  )
926  for member in members:
928  descriptor_set,
929  member,
930  segment_parameters,
931  local_support=local_support,
932  runtime_path=runtime_path,
933  )
934  kraus_bundle = _compose_kraus_bundles(
935  kraus_bundle,
936  step,
937  descriptor_set=descriptor_set,
938  runtime_path=runtime_path,
939  )
941  kraus_bundle,
942  descriptor_set=descriptor_set,
943  runtime_path=runtime_path,
944  )
945  global_target_qbits = _global_targets_for_local_support(partition, local_support)
946  try:
947  rho_out = _apply_kraus_bundle(
948  kraus_bundle,
949  rho,
950  qbit_num=descriptor_set.qbit_num,
951  local_support=local_support,
952  global_target_qbits=global_target_qbits,
953  descriptor_set=descriptor_set,
954  runtime_path=runtime_path,
955  )
956  except NoisyRuntimeValidationError:
957  raise
958  except NotImplementedError as exc:
960  descriptor_set,
961  category="unsupported_runtime_execution",
962  first_unsupported_condition="channel_native_runtime_execution",
963  failure_stage="runtime_execution",
964  runtime_path=runtime_path,
965  reason=str(exc),
966  ) from exc
967  except Exception as exc:
969  descriptor_set,
970  category="unsupported_runtime_execution",
971  first_unsupported_condition="channel_native_runtime_execution",
972  failure_stage="runtime_execution",
973  runtime_path=runtime_path,
974  reason="Channel-native apply failed: {}".format(exc),
975  ) from exc
977  descriptor_set,
978  partition,
979  members,
980  kraus_count=int(kraus_bundle.shape[0]),
981  )
982  return (rec,), rho_out