Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions crates/pecos-engines/src/noise/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1432,8 +1432,21 @@ impl GeneralNoiseModel {
/// Calculate the two-qubit gate error rate based on the rotation angle
///
/// with additional support for asymmetric scaling and power-law scaling
///
/// Angles are reduced modulo a full turn into `(-pi, pi]` before selecting
/// the signed coefficients. Thus an unsigned angle such as `7*pi/4` uses
/// the negative branch at `-pi/4`; both half-turn endpoints use `+pi`.
#[must_use]
pub fn p2_angle_error_rate(&self, angle: f64) -> f64 {
let angle = angle % std::f64::consts::TAU;
let angle = if angle > std::f64::consts::PI {
angle - std::f64::consts::TAU
} else if angle <= -std::f64::consts::PI {
angle + std::f64::consts::TAU
} else {
angle
};

// Normalize angle by π - convert to a value in [0, 1] range
let theta = angle.abs() / std::f64::consts::PI;

Expand Down Expand Up @@ -3762,6 +3775,51 @@ mod tests {
);
}

#[test]
fn test_rzz_error_rate_signed_periodicity() {
use std::f64::consts::{FRAC_PI_2, FRAC_PI_4, PI, TAU};

let mut model = GeneralNoiseModel::builder()
.with_p2(0.01)
.with_p2_angle_params(2.0, 0.1, 3.0, 0.2)
.with_p2_angle_power(2.0)
.build();
let noise = model
.as_any_mut()
.downcast_mut::<GeneralNoiseModel>()
.unwrap();

// Distinct coefficients expose selection of the wrong sign branch.
for (angle, scaling) in [
(0.0, 0.15),
(FRAC_PI_4, 0.3875),
(-FRAC_PI_4, 0.225),
(FRAC_PI_2, 0.95),
(-FRAC_PI_2, 0.6),
(PI, 3.2),
(-PI, 3.2),
] {
for turns in [-4.0, -1.0, 0.0, 1.0, 4.0] {
let input = angle + turns * TAU;
let actual = noise.p2_angle_error_rate(input);
let expected = 0.01 * scaling;
assert!(
(actual - expected).abs() < 1e-12,
"angle {input}: expected {expected}, got {actual}"
);
}
}

// Avoid adding pi before reducing: that can erase tiny signed inputs.
assert!((noise.p2_angle_error_rate(-1e-20) - 0.001).abs() < 1e-12);
assert!((noise.p2_angle_error_rate(1e-20) - 0.002).abs() < 1e-12);
// The positive half-turn endpoint must not absorb neighboring angles.
assert!((noise.p2_angle_error_rate(PI.next_up()) - 0.021).abs() < 1e-12);
assert!((noise.p2_angle_error_rate(PI.next_down()) - 0.032).abs() < 1e-12);
assert!((noise.p2_angle_error_rate((-PI).next_up()) - 0.021).abs() < 1e-12);
assert!((noise.p2_angle_error_rate((-PI).next_down()) - 0.032).abs() < 1e-12);
}

#[test]
fn test_noiseless_gates() {
// Create a noise model and mark RZ as a noiseless gate
Expand Down
26 changes: 25 additions & 1 deletion exp/pecos-neo/src/noise/two_qubit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ impl AngleScaling {

/// Calculate the scaling factor for a given angle.
///
/// Uses signed radians in [-pi, pi] for asymmetric scaling.
/// Uses signed radians in `(-pi, pi]` for asymmetric scaling. Unsigned
/// angles above pi use the negative branch; a half turn uses positive pi.
/// The magnitude is normalized by pi before applying power.
///
/// Formula: `offset + linear*|θ/π| + scale*|θ/π|^power`
Expand Down Expand Up @@ -812,6 +813,29 @@ mod tests {
assert!((scaling.scale(Angle64::HALF_TURN) - 1.2).abs() < 1e-10);
}

#[test]
fn test_asymmetric_angle_scaling_wrapped_boundaries() {
use std::f64::consts::{FRAC_PI_4, PI, TAU};

let scaling = AngleScaling::asymmetric(0.1, 0.0, 2.0, 0.2, 0.0, 3.0, 2.0);
for (angle, expected) in [
(0.0, 0.15),
(FRAC_PI_4, 0.3875),
(-FRAC_PI_4, 0.225),
(PI, 3.2),
(-PI, 3.2),
] {
for turns in [-4.0, -1.0, 0.0, 1.0, 4.0] {
let input = angle + turns * TAU;
let actual = scaling.scale(Angle64::from_radians(input));
assert!(
(actual - expected).abs() < 1e-12,
"angle {input}: expected {expected}, got {actual}"
);
}
}
}

#[test]
fn test_polynomial_angle_scaling() {
// Test the full polynomial formula: a + b*|θ/π| + c*|θ/π|^d
Expand Down
Loading