From 225e4396a766242bca5163320d82e84749cf2b59 Mon Sep 17 00:00:00 2001 From: xxxyj <48207890+xxxyj@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:21:40 +0200 Subject: [PATCH 1/7] Updated fast mwpm decoder to use diagonal terms in graph and include optimized_round argument --- .../decoder_examples/mwpm_decoders.py | 73 +++++++++++++++---- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/src/qce_interp/decoder_examples/mwpm_decoders.py b/src/qce_interp/decoder_examples/mwpm_decoders.py index 7666331..6e5e355 100644 --- a/src/qce_interp/decoder_examples/mwpm_decoders.py +++ b/src/qce_interp/decoder_examples/mwpm_decoders.py @@ -368,12 +368,15 @@ def __init__( initial_state_container: InitialStateContainer = InitialStateContainer.empty(), contains_qubit_refocusing: bool = True, optimize: bool = True, + optimized_round: int = 10, max_optimization_shots: int = 1000, ): self._error_identifier: IErrorDetectionIdentifier = error_identifier self._initial_state_container: InitialStateContainer = initial_state_container self.qec_rounds = qec_rounds self._contains_qubit_refocusing: bool = contains_qubit_refocusing + self._optimized_round = optimized_round + self._optimization_idx = list(self.qec_rounds).index(optimized_round) # binary initial state self.initial_state = np.sum(self._initial_state_container.as_array) % 2 @@ -399,14 +402,16 @@ def __init__( self.observables = csc_matrix(create_standard_diagonal_matrix(self.distance).tolist()) # uniform weights by default - self.space_like_weights = 1 - self.time_like_weights = 1 + self.space_like_weights = np.ones(self.distance) + self.time_like_weights = np.ones(self.distance - 1) + self.left_diagonal_weights = np.ones(self.distance - 2) + self.right_diagonal_weights = np.ones(self.distance - 2) if optimize: - self.optimize_weights(max_shots=max_optimization_shots) + self.optimize_weights(max_shots=max_optimization_shots, round=optimized_round, round_idx=self._optimization_idx) # endregion # region Interface Methods - def get_fidelity(self, cycle_stabilizer_count: int, target_state: np.ndarray = None, qec_round_idx: int = None, max_shots: int = None) -> float: + def get_fidelity(self, cycle_stabilizer_count: int, qec_round_idx: int = None, max_shots: int = None, target_state = None) -> float: """ Output shape: (1) - N is the number of measurement repetitions. @@ -421,13 +426,49 @@ def get_fidelity(self, cycle_stabilizer_count: int, target_state: np.ndarray = N if (max_shots is not None) and (num_shots > max_shots): num_shots = max_shots - matching = Matching( + matching_ref = Matching( self.H, weights=self.space_like_weights, repetitions=cycle_stabilizer_count + 1, timelike_weights=self.time_like_weights, faults_matrix=self.observables, ) + matching = Matching() + # copy the graph + for i, edge in enumerate(matching_ref.edges()): + if i < ((cycle_stabilizer_count + 1) * self.distance): + # data qubits (p) + data_q_idx = i % self.distance + matching.add_edge(edge[0], edge[1], weight=self.space_like_weights[data_q_idx], + fault_ids=edge[2]['fault_ids']) # [0] [1] are nodes, [2] is property + else: + # ancilla qubits (q) + ancilla_q_idx = (i - (cycle_stabilizer_count + 1) * self.distance) % (self.distance - 1) + matching.add_edge(edge[0], edge[1], weight=self.time_like_weights[ancilla_q_idx], + fault_ids=edge[2]['fault_ids']) + # diagonal edges, order is A1R1, A2R1, A1R2, A2R2, A1R3, A2R3... Here we don't consider the order of CZ gates + for ancilla_q_idx in range(self.distance - 1): + for round_ in range(cycle_stabilizer_count): # The last round is assumed perfect + node_idx = ancilla_q_idx + round_ * (self.distance - 1) + if ancilla_q_idx == 0: # the first ancilla + # add right edge + matching.add_edge(node_idx, node_idx + self.distance, + weight=self.right_diagonal_weights[ancilla_q_idx], + fault_ids=ancilla_q_idx + 1) # data qb idx = idx+1 + elif ancilla_q_idx == self.distance - 2: # the last ancilla + # add left edge + matching.add_edge(node_idx, node_idx + self.distance - 2, + weight=self.left_diagonal_weights[ancilla_q_idx - 1], + fault_ids=ancilla_q_idx) # data qb idx = idx + else: + # add left and right edges + matching.add_edge(node_idx, node_idx + self.distance - 2, + weight=self.left_diagonal_weights[ancilla_q_idx - 1], fault_ids=ancilla_q_idx) + matching.add_edge(node_idx, node_idx + self.distance, + weight=self.right_diagonal_weights[ancilla_q_idx], fault_ids=ancilla_q_idx + 1) + + matching.set_boundary_nodes( + {(self.distance - 1) * (cycle_stabilizer_count + 1)}) # last node as the boundary node corrections = matching.decode_batch(self.all_defects[qec_round_idx][:num_shots]) corrected_outcomes = (corrections + self.all_data_qubit_outcomes[qec_round_idx][:num_shots]) % 2 @@ -435,7 +476,8 @@ def get_fidelity(self, cycle_stabilizer_count: int, target_state: np.ndarray = N error_rate = num_error / num_shots # correct for echo pulses if self._contains_qubit_refocusing: - error_rate = num_error / num_shots if (cycle_stabilizer_count % 2 == 1 or cycle_stabilizer_count == 0) else 1 - num_error / num_shots + error_rate = num_error / num_shots if ( + cycle_stabilizer_count % 2 == 1 or cycle_stabilizer_count == 0) else 1 - num_error / num_shots # correct for initial states error_rate = error_rate if self.initial_state == 0 else 1 - error_rate @@ -454,7 +496,7 @@ def get_fidelity_uncertainty(self, cycle_stabilizer_count: int, target_state: np # endregion # region Class Methods - def get_error_rate_for_optimizer(self, concatenated_weights: np.ndarray, qec_round: int, max_shots: int = 1000) -> float: + def get_error_rate_for_optimizer(self, concatenated_weights: np.ndarray, qec_round: int, qec_round_idx: int, max_shots: int = 1000) -> float: """ Set weights and get the error rate :param concatenated_weights: 1d array: [space_like_weights, time_like_weights] @@ -463,30 +505,33 @@ def get_error_rate_for_optimizer(self, concatenated_weights: np.ndarray, qec_rou :return: """ self.space_like_weights = concatenated_weights[:self.distance] - self.time_like_weights = concatenated_weights[self.distance:] - error_rate = 1.0 - self.get_fidelity(qec_round, max_shots=max_shots) + self.time_like_weights = concatenated_weights[self.distance: 2 * self.distance - 1] + self.left_diagonal_weights = concatenated_weights[2 * self.distance - 1: 3 * self.distance - 3] + self.right_diagonal_weights = concatenated_weights[3 * self.distance - 3:] + error_rate = 1.0 - self.get_fidelity(qec_round, qec_round_idx, max_shots=max_shots) return error_rate - def optimize_weights(self, max_shots: int = 1000): + def optimize_weights(self, round: int = 10, round_idx: int = 0, max_shots: int = 1000): ''' Optimize and set the weights ''' - _optimized_round = 10 initial_weights = np.ones( - self.distance * 2 - 1) * 0.02 # uniform weights for d data qubits and d-1 ancila qubits + self.distance * 4 - 5) * 0.02 # uniform weights for d data qubits and d-1 ancila qubits sigma0 = 10 * 0.25 # determines the optimization step size. cma suggests 15*0.25, here use smaller step size result = cma.fmin( self.get_error_rate_for_optimizer, initial_weights, sigma0, options={'bounds': [0, 15]}, - args=(_optimized_round, max_shots), + args=(round, round_idx, max_shots), eval_initial_x=True, ) concatenated_weights = result[0] self.space_like_weights = concatenated_weights[:self.distance] - self.time_like_weights = concatenated_weights[self.distance:] + self.time_like_weights = concatenated_weights[self.distance: 2 * self.distance - 1] + self.left_diagonal_weights = concatenated_weights[2 * self.distance - 1: 3 * self.distance - 3] + self.right_diagonal_weights = concatenated_weights[3 * self.distance - 3:] # endregion From db2f1a4d453a3822b40c86eb538828372c136d9a Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Wed, 21 May 2025 15:46:47 +0200 Subject: [PATCH 2/7] Added get_ternary_stabilizer_classification method to ErrorDetectionIdentifier. Added argument for 'odd_weight_and_refocusing', relevant for edge cases in stability experiment. --- .../intrf_error_identifier.py | 46 +++++++++++++++++++ .../intrf_state_classification.py | 38 ++++++++++++++- .../test_state_classification.py | 7 ++- 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/src/qce_interp/interface_definitions/intrf_error_identifier.py b/src/qce_interp/interface_definitions/intrf_error_identifier.py index 50b3982..f347aea 100644 --- a/src/qce_interp/interface_definitions/intrf_error_identifier.py +++ b/src/qce_interp/interface_definitions/intrf_error_identifier.py @@ -146,6 +146,17 @@ def get_binary_projected_classification(self, cycle_stabilizer_count: int) -> ND """ raise InterfaceMethodException + @abstractmethod + def get_ternary_stabilizer_classification(self, cycle_stabilizer_count: int) -> NDArray[np.int_]: + """ + Output shape: (N, M, S) + - N is the number of measurement repetitions. + - M is the number of stabilizer repetitions. + - S is the number of stabilizer qubits. + :return: Tensor of ternary-classification at specific cycle. + """ + raise InterfaceMethodException + @abstractmethod def get_ternary_projected_classification(self, cycle_stabilizer_count: int) -> NDArray[np.int_]: """ @@ -523,6 +534,7 @@ def get_defect_stabilizer_classification(self, cycle_stabilizer_count: int) -> N sub_defect_classification: NDArray[np.int_] = IStateClassifierContainer.calculate_defect( m=sub_parity_classification, initial_condition=state_classifier.expected_parity.value, + odd_weight_and_refocusing=state_classifier.odd_weight_and_refocusing, ) result[:, :, i] = sub_defect_classification # (N, M(+1), S) @@ -575,6 +587,40 @@ def get_binary_projected_classification(self, cycle_stabilizer_count: int) -> ND result = result.transpose((1, 2, 0)) return result + @lru_cache(maxsize=None) + def get_ternary_stabilizer_classification(self, cycle_stabilizer_count: int) -> NDArray[np.int_]: + """ + Output shape: (N, M, S) + - N is the number of measurement repetitions. + - M is the number of stabilizer repetitions. + - S is the number of stabilizer qubits. + :return: Tensor of ternary-classification at specific cycle. + """ + # Data allocation + any_qubit_id: IQubitID = self.involved_stabilizer_qubit_ids[0] + stabilizer_acquisition_indices: NDArray[np.int_] = self._index_kernel.get_stabilizer_and_projected_cycle_acquisition_indices(qubit_id=any_qubit_id, cycle_stabilizer_count=cycle_stabilizer_count) + post_selection_mask = self.get_post_selection_mask(cycle_stabilizer_count=cycle_stabilizer_count) + stabilizer_acquisition_indices = stabilizer_acquisition_indices[post_selection_mask] + + # Prepare output shape + n, m = stabilizer_acquisition_indices.shape + s: int = len(self.involved_stabilizer_qubit_ids) + # Guard clause, return empty array at 0 qec rounds + if stabilizer_acquisition_indices.size == 0: + return np.empty(shape=(n, m, s)) + + result: NDArray[np.int_] = np.zeros(shape=(s, n, m), dtype=np.int_) + for i, qubit_id in enumerate(self.involved_stabilizer_qubit_ids): + state_classifier: IStateClassifierContainer = self._classifier_lookup[qubit_id] + state_classifier: IStateClassifierContainer = state_classifier.reshape( + container=state_classifier, + index_slices=stabilizer_acquisition_indices, + ) + result[i, :, :] = state_classifier.get_ternary_classification() + # (N, M, S) Transpose + result = result.transpose((1, 2, 0)) + return result + @lru_cache(maxsize=None) def get_ternary_projected_classification(self, cycle_stabilizer_count: int) -> NDArray[np.int_]: """ diff --git a/src/qce_interp/interface_definitions/intrf_state_classification.py b/src/qce_interp/interface_definitions/intrf_state_classification.py index 5a09c8b..302c339 100644 --- a/src/qce_interp/interface_definitions/intrf_state_classification.py +++ b/src/qce_interp/interface_definitions/intrf_state_classification.py @@ -731,6 +731,14 @@ def expected_parity(self) -> ParityType: def stabilizer_reset(self) -> bool: """:return: Boolean whether parity resets each round.""" raise InterfaceMethodException + + @property + def odd_weight_and_refocusing(self) -> bool: + """ + :return: Boolean whether parity is based on odd-number of element and elements are (flipped) refocused each round. + Mainly relevant for (weight-1) edge-stabilizers in 1D stability experiment. + """ + raise InterfaceMethodException # endregion # region Interface Methods @@ -787,7 +795,7 @@ def calculate_parity(m: np.ndarray) -> np.ndarray: return result @staticmethod - def calculate_defect(m: np.ndarray, initial_condition: int = +1) -> np.ndarray: + def calculate_defect(m: np.ndarray, initial_condition: int = +1, odd_weight_and_refocusing: bool = False) -> np.ndarray: """ Calculate the derivative of a tensor of +1 and -1 values using the formula p[n+1] = m[n+1] * m[n], with p[0] = (initial condition) * m[0]. The operation is performed along the @@ -795,9 +803,13 @@ def calculate_defect(m: np.ndarray, initial_condition: int = +1) -> np.ndarray: :param m: Input tensor with arbitrary shape. :param initial_condition: Initial condition after taking derivative. (For p[0]) + :param odd_weight_and_refocusing: If stabilizer defect originates from an odd-weight parity AND data qubits get refocused every round, the definition of defect is inverted. :return: First derivative of m -> p. """ - return IStateClassifierContainer.calculate_derivative(m=m, initial_condition=initial_condition) + result = IStateClassifierContainer.calculate_derivative(m=m, initial_condition=initial_condition) + if odd_weight_and_refocusing: + result *= -1 + return result @staticmethod def calculate_derivative(m: np.ndarray, initial_condition: int = +1) -> np.ndarray: @@ -844,6 +856,7 @@ class StateClassifierContainer(IStateClassifierContainer): state_classification: NDArray[np.int_] _expected_parity: ParityType = field(default=ParityType.EVEN) _stabilizer_reset: bool = field(default=False) + _odd_weight_and_refocusing: bool = field(default=False) # region Interface Properties @property @@ -855,6 +868,14 @@ def expected_parity(self) -> ParityType: def stabilizer_reset(self) -> bool: """:return: Boolean whether parity resets each round.""" return self._stabilizer_reset + + @property + def odd_weight_and_refocusing(self) -> bool: + """ + :return: Boolean whether parity is based on odd-number of element and elements are (flipped) refocused each round. + Mainly relevant for (weight-1) edge-stabilizers in 1D stability experiment. + """ + return self._odd_weight_and_refocusing # endregion # region Class Methods @@ -884,6 +905,7 @@ def get_defect_classification(self) -> NDArray[np.int_]: return IStateClassifierContainer.calculate_defect( m=self.get_parity_classification(), initial_condition=self.expected_parity.value, + odd_weight_and_refocusing=self.odd_weight_and_refocusing, ) def get_defect_rate(self) -> float: @@ -911,6 +933,7 @@ class ShotsClassifierContainer(IStateClassifierContainer): decision_boundaries: DecisionBoundaries _expected_parity: ParityType = field(default=ParityType.EVEN) _stabilizer_reset: bool = field(default=False) + _odd_weight_and_refocusing: bool = field(default=False) # region Interface Properties @property @@ -922,6 +945,14 @@ def expected_parity(self) -> ParityType: def stabilizer_reset(self) -> bool: """:return: Boolean whether parity resets each round.""" return self._stabilizer_reset + + @property + def odd_weight_and_refocusing(self) -> bool: + """ + :return: Boolean whether parity is based on odd-number of element and elements are (flipped) refocused each round. + Mainly relevant for (weight-1) edge-stabilizers in 1D stability experiment. + """ + return self._odd_weight_and_refocusing # endregion # region Class Properties @@ -936,6 +967,7 @@ def state_classifier(self) -> StateClassifierContainer: state_classification=self._process_tensor(self.shots, self.decision_boundaries.get_binary_predictions), _expected_parity=self.expected_parity, _stabilizer_reset=self.stabilizer_reset, + _odd_weight_and_refocusing=self.odd_weight_and_refocusing, ) @property @@ -945,6 +977,7 @@ def state_classifier_ternary(self) -> StateClassifierContainer: state_classification=self._process_tensor(self.shots, self.decision_boundaries.get_predictions), _expected_parity=self.expected_parity, _stabilizer_reset=self.stabilizer_reset, + _odd_weight_and_refocusing=self.odd_weight_and_refocusing, ) # endregion @@ -981,6 +1014,7 @@ def reshape(cls, container: TStateClassifierContainer, index_slices: NDArray[np. decision_boundaries=container.decision_boundaries, _expected_parity=container.expected_parity, _stabilizer_reset=container.stabilizer_reset, + _odd_weight_and_refocusing=container.odd_weight_and_refocusing, ) # endregion diff --git a/tests/interface_definitions/test_state_classification.py b/tests/interface_definitions/test_state_classification.py index 6aa3885..8f6f43d 100644 --- a/tests/interface_definitions/test_state_classification.py +++ b/tests/interface_definitions/test_state_classification.py @@ -285,7 +285,8 @@ def test_conversion_parity_to_defect_tensor(self): ]) defect = IStateClassifierContainer.calculate_defect( m=parities, - initial_condition=-1 + initial_condition=-1, + odd_weight_and_refocusing=False, ) assert_array_equal( defect, @@ -305,7 +306,8 @@ def test_conversion_parity_to_defect_tensor(self): ]) defect = IStateClassifierContainer.calculate_defect( m=parities, - initial_condition=-1 + initial_condition=-1, + odd_weight_and_refocusing=False, ) assert_array_equal( defect, @@ -374,6 +376,7 @@ def test_random_parity_defect_classification(self): IStateClassifierContainer.calculate_defect( IStateClassifierContainer.binary_to_eigenvalue(p), initial_condition=initial_condition, + odd_weight_and_refocusing=False, ) ) From c6d5d940c5443de805a0bbc4dcc1178c748af3b1 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Wed, 21 May 2025 16:28:46 +0200 Subject: [PATCH 3/7] Added missing implementation of get_ternary_stabilizer_classification for LabeledErrorDetectionIdentifier --- .../interface_definitions/intrf_error_identifier.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/qce_interp/interface_definitions/intrf_error_identifier.py b/src/qce_interp/interface_definitions/intrf_error_identifier.py index f347aea..5237e8b 100644 --- a/src/qce_interp/interface_definitions/intrf_error_identifier.py +++ b/src/qce_interp/interface_definitions/intrf_error_identifier.py @@ -1102,6 +1102,18 @@ def get_binary_stabilizer_classification(self, cycle_stabilizer_count: int) -> N cycle_stabilizer_count=cycle_stabilizer_count, ) + def get_ternary_stabilizer_classification(self, cycle_stabilizer_count: int) -> NDArray[np.int_]: + """ + Output shape: (N, M, S) + - N is the number of measurement repetitions. + - M is the number of stabilizer repetitions. + - S is the number of stabilizer qubits. + :return: Tensor of ternary-classification at specific cycle. + """ + return self._error_detection_identifier.get_ternary_stabilizer_classification( + cycle_stabilizer_count=cycle_stabilizer_count, + ) + def get_labeled_binary_stabilizer_classification(self, cycle_stabilizer_count: int) -> DataArray: """ Retrieves binary classification data for stabilizer qubits in a labeled, structured format. From 2a18937d61afb5d68dcfc5de4b04172b5a56bd77 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Wed, 28 May 2025 15:56:48 +0200 Subject: [PATCH 4/7] Updated initial state to expectation method by including the edge case of odd-weight-stabilizers --- src/qce_interp/utilities/expected_parities.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/qce_interp/utilities/expected_parities.py b/src/qce_interp/utilities/expected_parities.py index cc4ef64..2e791f7 100644 --- a/src/qce_interp/utilities/expected_parities.py +++ b/src/qce_interp/utilities/expected_parities.py @@ -34,7 +34,12 @@ def initial_state_to_expected_parity(initial_state: InitialStateContainer, parit assert len(computed_parity) == len(involved_ancilla_qubit_ids), f"Expects parity to be defined for each involved ancilla qubit. Instead {len(computed_parity)} out of {len(involved_ancilla_qubit_ids)} are present." for qubit_id, parity in zip(involved_ancilla_qubit_ids, computed_parity): - even_parity: bool = parity != +1 if inverse_parity else parity == +1 + # Determine even/odd weight parity + parity_group = parity_layout.get_parity_group(element=qubit_id)[0] + odd_weight_stabilizer: bool = qubit_id == parity_group.ancilla_id and len(parity_group.data_ids) % 2 != 0 + # Switch parity + switch_parity: bool = inverse_parity ^ odd_weight_stabilizer + even_parity: bool = parity != +1 if switch_parity else parity == +1 if even_parity: result[qubit_id] = ParityType.EVEN else: From cbc934f8fd1f45ddf836ca06977cd944557ea4de Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Wed, 28 May 2025 15:57:25 +0200 Subject: [PATCH 5/7] Fixed calculation of intersection in shot classification and corresponding edge-case handling. --- .../intrf_state_classification.py | 69 ++++++++++++++----- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/src/qce_interp/interface_definitions/intrf_state_classification.py b/src/qce_interp/interface_definitions/intrf_state_classification.py index 302c339..1471222 100644 --- a/src/qce_interp/interface_definitions/intrf_state_classification.py +++ b/src/qce_interp/interface_definitions/intrf_state_classification.py @@ -266,23 +266,58 @@ def _calculate_intersection(coef1: Vec2D, intercept1: float, coef2: Vec2D, inter """ :return: Intersection point of two linear equations defined by coefficients and intercepts. """ - denominator: float = (-coef1.x / coef1.y + coef2.x / coef2.y) - numerator: float = (-intercept2 / coef2.y + intercept1 / coef1.y) - # Deal with possible zero-division error - if denominator != 0: - _x: float = numerator / denominator - elif denominator == 0 and numerator == 0: - _x: float = 1.0 - else: - warn(f"[ZeroDivisionError] During intersect calculation of {coef1} and {coef2}.") - denominator = 1e-6 - _x: float = numerator / denominator - - _y: float = -coef1.x / coef1.y * _x - intercept1 / coef1.y - return Vec2D( - x=_x, - y=_y, - ) + """ + Compute the (x, y) coordinates where the two lines + + coef1.x * x + coef1.y * y + intercept1 = 0 + coef2.x * x + coef2.y * y + intercept2 = 0 + + intersect. + + Parameters + ---------- + coef1, coef2 : Vec2D + Line coefficients (a, b), i.e. normal-vector components. + intercept1, intercept2 : float + Line intercepts *c* (constant terms). + + Returns + ------- + Vec2D + Intersection point. + + Raises + ------ + ValueError + If the lines are parallel (det ≈ 0) or coincident, so no unique + intersection exists. + """ + # Unpack coefficients + a1, b1 = coef1.x, coef1.y + a2, b2 = coef2.x, coef2.y + + # Determinant of the 2×2 system + det: float = a1 * b2 - a2 * b1 + tol: float = 1e-12 + + if abs(det) < tol: + # Parallel or coincident — inspect intercepts for distinction + if abs(a1 * intercept2 - a2 * intercept1) < tol and \ + abs(b1 * intercept2 - b2 * intercept1) < tol: + # raise ValueError("Lines are coincident; infinite intersections.") + return Vec2D(x=0.0, y=0.0) + # raise ValueError("Lines are parallel; no unique intersection.") + return Vec2D(x=0.0, y=0.0) + + # Solve A x = b (A = [[a1, b1], [a2, b2]], b = [-c1, -c2]) + A = np.array([ + [a1, b1], + [a2, b2], + ], dtype=float) + b = np.array([-intercept1, -intercept2], dtype=float) + x, y = np.linalg.solve(A, b) + + return Vec2D.from_vector(np.array([x, y])) @staticmethod def _calculate_intersection_binary_case(coef1: Vec2D, intercept1: float): From f7d148ea57d2f9ad6509a450d91c3dc80330ebc5 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Fri, 30 May 2025 14:54:54 +0200 Subject: [PATCH 6/7] Updated initial state to expected parity method. Handling odd-weight stabilizers --- src/qce_interp/utilities/expected_parities.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qce_interp/utilities/expected_parities.py b/src/qce_interp/utilities/expected_parities.py index 2e791f7..6f2f2bf 100644 --- a/src/qce_interp/utilities/expected_parities.py +++ b/src/qce_interp/utilities/expected_parities.py @@ -11,7 +11,7 @@ from qce_interp.interface_definitions.intrf_error_identifier import ErrorDetectionIdentifier -def initial_state_to_expected_parity(initial_state: InitialStateContainer, parity_layout: ISurfaceCodeLayer, involved_data_qubit_ids: List[IQubitID], involved_ancilla_qubit_ids: List[IQubitID], inverse_parity: bool = False) -> Dict[IQubitID, ParityType]: +def initial_state_to_expected_parity(initial_state: InitialStateContainer, parity_layout: ISurfaceCodeLayer, involved_data_qubit_ids: List[IQubitID], involved_ancilla_qubit_ids: List[IQubitID], inverse_parity: bool = False, stabilizer_active: bool = True) -> Dict[IQubitID, ParityType]: # Data allocation result: Dict[IQubitID, ParityType] = {} parity_index_lookup: Dict[IQubitID, NDArray[np.int_]] = ErrorDetectionIdentifier.get_parity_index_lookup( @@ -38,7 +38,7 @@ def initial_state_to_expected_parity(initial_state: InitialStateContainer, parit parity_group = parity_layout.get_parity_group(element=qubit_id)[0] odd_weight_stabilizer: bool = qubit_id == parity_group.ancilla_id and len(parity_group.data_ids) % 2 != 0 # Switch parity - switch_parity: bool = inverse_parity ^ odd_weight_stabilizer + switch_parity: bool = inverse_parity ^ (odd_weight_stabilizer and stabilizer_active) even_parity: bool = parity != +1 if switch_parity else parity == +1 if even_parity: result[qubit_id] = ParityType.EVEN From 6695b891fe1a971cffc89e587eed5f0967179768 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:43:19 +0200 Subject: [PATCH 7/7] Updated CI dropping python 3.11 support for now since xarray is not up to date there --- .github/workflows/python_test_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python_test_build.yml b/.github/workflows/python_test_build.yml index 8632e79..517cbf4 100644 --- a/.github/workflows/python_test_build.yml +++ b/.github/workflows/python_test_build.yml @@ -18,7 +18,7 @@ jobs: max-parallel: 4 fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v2