Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3b2bbda

Browse files
authoredAug 13, 2024
Fix noisy_simulator panic when initializing with non-square Kraus matrices (microsoft#1826)
When initializing an `Operation`, the first thing we do is transposing the matrices, `nalgebra` panics internally when transposing non-square matrices. This PR moves the transposition operation after the dimensions check to gracefully handle that case and adds a unit test to check for this in the future. Notes: 1. This bug was introduced in the performance improvement PR. 2. It doesn't affect well-formed programs; it just affects programs using ill-formed Kraus operators.
1 parent a8b00c8 commit 3b2bbda

File tree

2 files changed

+28
-14
lines changed

2 files changed

+28
-14
lines changed
 

Diff for: ‎noisy_simulator/src/operation.rs

+14-14
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,6 @@ impl Operation {
5353
/// Matrices must be of dimension 2^k x 2^k, where k is an integer.
5454
/// Returns `None` if the kraus matrices are ill formed.
5555
pub fn new(mut kraus_operators: Vec<SquareMatrix>) -> Result<Self, Error> {
56-
// Performance note: Because `nalgebra` stores its matrices in column major
57-
// form, we use `gemv_tr` in the `apply_kernel` function when multiplying to avoid
58-
// incurring cache misses. That is why we transpose all Kraus operators when they
59-
// enter the simulator:
60-
// `gemv(1, matrix, vec, 0)` is equivalent to `gemv_tr(1, matrix_tr, vec, 0)`,
61-
// but the later has much better performance.
62-
//
63-
// SAFETY of transposing: all these matrices are only consumed by the
64-
// `kernel.rs/apply_kernel` function which effectively transposes them
65-
// back when multypling, so it is safe to do this transformation.
66-
for kraus_operator in &mut kraus_operators {
67-
kraus_operator.transpose_mut();
68-
}
69-
7056
let (dim, _) = kraus_operators
7157
.first()
7258
.ok_or(Error::FailedToConstructOperation(
@@ -91,6 +77,20 @@ impl Operation {
9177
}
9278
}
9379

80+
// Performance note: Because `nalgebra` stores its matrices in column major
81+
// form, we use `gemv_tr` in the `apply_kernel` function when multiplying to avoid
82+
// incurring cache misses. That is why we transpose all Kraus operators when they
83+
// enter the simulator:
84+
// `gemv(1, matrix, vec, 0)` is equivalent to `gemv_tr(1, matrix_tr, vec, 0)`,
85+
// but the later has much better performance.
86+
//
87+
// SAFETY of transposing: all these matrices are only consumed by the
88+
// `kernel.rs/apply_kernel` function which effectively transposes them
89+
// back when multypling, so it is safe to do this transformation.
90+
for kraus_operator in &mut kraus_operators {
91+
kraus_operator.transpose_mut();
92+
}
93+
9494
// Performance note: The effect_matrix = Σᵢ (kᵢ† ⋅ k), but due to performance
9595
// reasons described above we want to store its transpose. But (A ⋅ B)^T = B^T ⋅ A^T.
9696
// Therefore, we have to swap the order of the factors.

Diff for: ‎noisy_simulator/src/operation/tests.rs

+14
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ fn check_operation_number_of_qubits_is_computed_correctly() {
4646
assert_eq!(1, op.number_of_qubits());
4747
}
4848

49+
#[test]
50+
fn check_non_square_kraus_operator_does_not_panic() {
51+
let op = operation!(
52+
[
53+
0., 0.;
54+
]
55+
);
56+
57+
assert!(matches!(
58+
op,
59+
Err(crate::Error::FailedToConstructOperation(_))
60+
));
61+
}
62+
4963
/// Check that the inner matrices of the instrument are constructed correctly.
5064
#[test]
5165
fn check_effect_matrix_is_computed_correctly() {

0 commit comments

Comments
 (0)
Please sign in to comment.