1 from __future__
import annotations
3 from typing
import Mapping
8 from squander.partitioning.noisy_runtime_core
import (
9 PHASE3_FUSION_CLASS_DEFERRED,
10 PHASE3_FUSION_CLASS_FUSED,
11 PHASE3_FUSION_CLASS_SUPPORTED_UNFUSED,
12 PHASE3_FUSION_KIND_NOISE_BOUNDARY,
13 PHASE3_FUSION_KIND_UNITARY_ISLAND,
14 NoisyRuntimeFusedRegionRecord,
15 _execute_member_sequence,
17 from squander.partitioning.noisy_runtime_errors
import (
18 NoisyRuntimeValidationError,
19 runtime_validation_error,
21 from squander.partitioning.noisy_types
import (
22 NoisyPartitionDescriptor,
23 NoisyPartitionDescriptorMember,
24 NoisyPartitionDescriptorSet,
29 descriptor_set: NoisyPartitionDescriptorSet,
30 members: tuple[NoisyPartitionDescriptorMember, ...],
31 ) -> tuple[tuple[bool, tuple[NoisyPartitionDescriptorMember, ...]], ...]:
34 segments: list[tuple[bool, tuple[NoisyPartitionDescriptorMember, ...]]] = []
35 current_is_unitary = descriptor_set.canonical_operation_for(members[0]).is_unitary
36 current_members: list[NoisyPartitionDescriptorMember] = [members[0]]
37 for member
in members[1:]:
39 descriptor_set.canonical_operation_for(member).is_unitary
42 current_members.append(member)
44 segments.append((current_is_unitary, tuple(current_members)))
45 current_is_unitary = descriptor_set.canonical_operation_for(member).is_unitary
46 current_members = [member]
47 segments.append((current_is_unitary, tuple(current_members)))
48 return tuple(segments)
52 members: tuple[NoisyPartitionDescriptorMember, ...],
59 for local_qbit
in member.local_qubit_support
65 def _u3_unitary(theta_over_2: float, phi: float, lam: float) -> np.ndarray:
68 [np.cos(theta_over_2), -np.exp(1j * lam) * np.sin(theta_over_2)],
70 np.exp(1j * phi) * np.sin(theta_over_2),
71 np.exp(1j * (phi + lam)) * np.cos(theta_over_2),
79 local_parameter_vector: np.ndarray, local_start: int, local_stop: int
81 theta, phi, lam = local_parameter_vector[local_start:local_stop]
82 return _u3_unitary(float(theta), float(phi), float(lam))
87 local_qbit_to_kernel_index: Mapping[int, int],
88 local_target_qbit: int,
89 local_control_qbit: int,
91 """Map descriptor CNOT wires to _embed_cnot_gate(kernel_control, kernel_target).""" 93 local_qbit_to_kernel_index[local_control_qbit],
94 local_qbit_to_kernel_index[local_target_qbit],
99 gate_matrix: np.ndarray, *, total_kernel_qbits: int, kernel_target_qbit: int
101 dim = 1 << total_kernel_qbits
102 embedded = np.zeros((dim, dim), dtype=np.complex128)
103 for basis
in range(dim):
104 input_bit = (basis >> kernel_target_qbit) & 1
105 base_state = basis & ~(1 << kernel_target_qbit)
106 for output_bit
in (0, 1):
107 output_state = base_state | (output_bit << kernel_target_qbit)
108 embedded[output_state, basis] = gate_matrix[output_bit, input_bit]
113 *, total_kernel_qbits: int, kernel_control_qbit: int, kernel_target_qbit: int
115 dim = 1 << total_kernel_qbits
116 embedded = np.zeros((dim, dim), dtype=np.complex128)
117 for basis
in range(dim):
119 if (basis >> kernel_control_qbit) & 1:
120 output_state ^= 1 << kernel_target_qbit
121 embedded[output_state, basis] = 1.0
126 descriptor_set: NoisyPartitionDescriptorSet,
127 member: NoisyPartitionDescriptorMember,
128 local_parameter_vector: np.ndarray,
130 local_qbit_to_kernel_index: Mapping[int, int],
131 total_kernel_qbits: int,
134 op = descriptor_set.canonical_operation_for(member)
136 if member.local_target_qbit
is None:
139 category=
"descriptor_to_runtime_mismatch",
140 first_unsupported_condition=
"local_target_qbit",
141 failure_stage=
"runtime_preflight",
142 runtime_path=runtime_path,
143 reason=
"Fused partitioned runtime requires local_target_qbit for U3 members",
145 local_start = member.local_param_start
146 local_stop = local_start + op.param_count
147 if local_stop > local_parameter_vector.size:
150 category=
"descriptor_to_runtime_mismatch",
151 first_unsupported_condition=
"parameter_routing",
152 failure_stage=
"runtime_preflight",
153 runtime_path=runtime_path,
155 "Fused partitioned runtime has out-of-range parameters for workload " 156 "'{}' at canonical operation {}".format(
157 descriptor_set.workload_id, member.canonical_operation_index
163 local_parameter_vector, local_start, local_stop
165 total_kernel_qbits=total_kernel_qbits,
166 kernel_target_qbit=local_qbit_to_kernel_index[member.local_target_qbit],
168 if op.name ==
"CNOT":
169 if member.local_target_qbit
is None or member.local_control_qbit
is None:
172 category=
"descriptor_to_runtime_mismatch",
173 first_unsupported_condition=
"local_control_qbit",
174 failure_stage=
"runtime_preflight",
175 runtime_path=runtime_path,
176 reason=
"Fused partitioned runtime requires both local qubits for CNOT members",
179 local_qbit_to_kernel_index=local_qbit_to_kernel_index,
180 local_target_qbit=member.local_target_qbit,
181 local_control_qbit=member.local_control_qbit,
184 total_kernel_qbits=total_kernel_qbits,
185 kernel_control_qbit=k_ctl,
186 kernel_target_qbit=k_tgt,
190 category=
"unsupported_runtime_operation",
191 first_unsupported_condition=
"fusion_gate_name",
192 failure_stage=
"runtime_preflight",
193 runtime_path=runtime_path,
194 reason=
"Fused partitioned runtime cannot build a fused kernel for '{}'".format(
201 descriptor_set: NoisyPartitionDescriptorSet,
202 partition: NoisyPartitionDescriptor,
203 members: tuple[NoisyPartitionDescriptorMember, ...],
204 local_parameter_vector: np.ndarray,
207 ) -> tuple[np.ndarray, tuple[int, ...], tuple[int, ...]]:
209 if not active_local_qbits:
212 category=
"descriptor_to_runtime_mismatch",
213 first_unsupported_condition=
"local_qubit_support",
214 failure_stage=
"runtime_preflight",
215 runtime_path=runtime_path,
216 reason=
"Fused partitioned runtime requires non-empty local_qubit_support",
218 if len(active_local_qbits) > 2:
221 category=
"unsupported_runtime_operation",
222 first_unsupported_condition=
"fusion_qubit_span",
223 failure_stage=
"runtime_preflight",
224 runtime_path=runtime_path,
226 "Fused partitioned runtime supports only unitary islands on up to 2 " 227 "qubits, got span {} for workload '{}'".format(
228 len(active_local_qbits), descriptor_set.workload_id
232 local_qbit_to_kernel_index = {
233 local_qbit: kernel_index
234 for kernel_index, local_qbit
in enumerate(active_local_qbits)
236 kernel_dim = 1 << len(active_local_qbits)
237 fused_kernel = np.eye(kernel_dim, dtype=np.complex128)
238 for member
in members:
239 op = descriptor_set.canonical_operation_for(member)
240 if not op.is_unitary:
243 category=
"unsupported_runtime_operation",
244 first_unsupported_condition=
"fusion_member_kind",
245 failure_stage=
"runtime_preflight",
246 runtime_path=runtime_path,
247 reason=
"Fused partitioned runtime only supports unitary members inside one fused island",
252 local_parameter_vector,
253 local_qbit_to_kernel_index=local_qbit_to_kernel_index,
254 total_kernel_qbits=len(active_local_qbits),
255 runtime_path=runtime_path,
257 fused_kernel = gate_matrix @ fused_kernel
258 global_target_qbits = tuple(
259 partition.local_to_global_qbits[local_qbit]
for local_qbit
in active_local_qbits
261 return fused_kernel, active_local_qbits, global_target_qbits
265 descriptor_set: NoisyPartitionDescriptorSet,
266 partition: NoisyPartitionDescriptor,
267 members: tuple[NoisyPartitionDescriptorMember, ...],
271 ) -> NoisyRuntimeFusedRegionRecord:
273 global_target_qbits = tuple(
274 partition.local_to_global_qbits[local_qbit]
for local_qbit
in active_local_qbits
276 return NoisyRuntimeFusedRegionRecord(
277 partition_index=partition.partition_index,
278 candidate_kind=PHASE3_FUSION_KIND_UNITARY_ISLAND,
279 classification=classification,
281 partition_member_indices=tuple(
282 member.partition_member_index
for member
in members
284 canonical_operation_indices=tuple(
285 member.canonical_operation_index
for member
in members
287 operation_names=tuple(
288 descriptor_set.canonical_operation_for(m).name
for m
in members
290 global_target_qbits=global_target_qbits,
291 local_target_qbits=active_local_qbits,
292 member_count=len(members),
294 descriptor_set.canonical_operation_for(m).is_unitary
for m
in members
300 descriptor_set: NoisyPartitionDescriptorSet,
301 partition: NoisyPartitionDescriptor,
302 left_members: tuple[NoisyPartitionDescriptorMember, ...],
303 boundary_members: tuple[NoisyPartitionDescriptorMember, ...],
304 right_members: tuple[NoisyPartitionDescriptorMember, ...],
305 ) -> NoisyRuntimeFusedRegionRecord:
306 relevant_members = left_members + boundary_members + right_members
308 global_target_qbits = tuple(
309 partition.local_to_global_qbits[local_qbit]
for local_qbit
in active_local_qbits
311 return NoisyRuntimeFusedRegionRecord(
312 partition_index=partition.partition_index,
313 candidate_kind=PHASE3_FUSION_KIND_NOISE_BOUNDARY,
314 classification=PHASE3_FUSION_CLASS_DEFERRED,
315 reason=
"explicit_noise_boundary",
316 partition_member_indices=tuple(
317 member.partition_member_index
for member
in relevant_members
319 canonical_operation_indices=tuple(
320 member.canonical_operation_index
for member
in relevant_members
322 operation_names=tuple(
323 descriptor_set.canonical_operation_for(m).name
for m
in relevant_members
325 global_target_qbits=global_target_qbits,
326 local_target_qbits=active_local_qbits,
327 member_count=len(relevant_members),
329 descriptor_set.canonical_operation_for(m).is_unitary
330 for m
in relevant_members
342 descriptor_set: NoisyPartitionDescriptorSet,
343 partition: NoisyPartitionDescriptor,
344 local_parameter_vector: np.ndarray,
349 ) -> tuple[NoisyRuntimeFusedRegionRecord, ...]:
351 fused_regions: list[NoisyRuntimeFusedRegionRecord] = []
352 for segment_index, (is_unitary, segment_members)
in enumerate(segments):
354 eligible_for_fusion = len(segment_members) >= 2
355 if allow_fusion
and eligible_for_fusion:
357 fused_kernel, active_local_qbits, global_target_qbits = (
362 local_parameter_vector,
363 runtime_path=runtime_path,
366 except NoisyRuntimeValidationError
as exc:
367 if exc.first_unsupported_condition !=
"fusion_qubit_span":
372 local_parameter_vector,
374 runtime_path=runtime_path,
376 fused_regions.append(
381 classification=PHASE3_FUSION_CLASS_DEFERRED,
382 reason=
"fusion_qubit_span",
387 rho.apply_local_unitary(
388 np.asarray(fused_kernel, dtype=np.complex128),
389 list(global_target_qbits),
391 except Exception
as exc:
394 category=
"unsupported_runtime_execution",
395 first_unsupported_condition=
"fused_partition_execution",
396 failure_stage=
"runtime_execution",
397 runtime_path=runtime_path,
399 "Fused partitioned runtime failed while executing partition {} " 400 "of workload '{}' on local span {}: {}".format(
401 partition.partition_index,
402 descriptor_set.workload_id,
403 list(active_local_qbits),
408 fused_regions.append(
413 classification=PHASE3_FUSION_CLASS_FUSED,
414 reason=
"eligible_unitary_island",
421 local_parameter_vector,
423 runtime_path=runtime_path,
425 fused_regions.append(
430 classification=PHASE3_FUSION_CLASS_SUPPORTED_UNFUSED,
433 if eligible_for_fusion
434 else "singleton_unitary_region" 443 local_parameter_vector,
445 runtime_path=runtime_path,
447 if segment_index == 0
or segment_index == len(segments) - 1:
449 left_is_unitary, left_members = segments[segment_index - 1]
450 right_is_unitary, right_members = segments[segment_index + 1]
451 if left_is_unitary
and right_is_unitary:
452 fused_regions.append(
461 return tuple(fused_regions)
def _build_noise_boundary_record
def _u3_unitary_from_local_parameter_slice
def _iter_member_segments
def _build_gate_matrix_for_member
def _execute_member_sequence
def runtime_validation_error
def _kernel_indices_for_fused_cnot
def _embed_single_qubit_gate
def _execute_partition_with_optional_fusion
: execute a partition with optional fusion explain: this function is used to execute a partition with...
def _build_unitary_region_record