1use crate::{
7 error::{QuantRS2Error, QuantRS2Result},
8 gate::GateOp,
9 qubit::QubitId,
10};
11use rustc_hash::FxHashMap;
12use scirs2_core::ndarray::Array1;
13use scirs2_core::Complex64;
14use std::f64::consts::PI;
15
16#[derive(Debug, Clone, Copy, PartialEq)]
18pub enum PulseEnvelope {
19 Gaussian { sigma: f64 },
21 DRAG { sigma: f64, beta: f64 },
23 Square,
25 RaisedCosine,
27 HyperbolicSecant { width: f64 },
29 HermiteGaussian { n: usize, sigma: f64 },
31}
32
33impl PulseEnvelope {
34 pub fn evaluate(&self, t: f64) -> f64 {
36 match self {
37 Self::Gaussian { sigma } | Self::DRAG { sigma, beta: _ } => {
38 let t_norm = (t - 0.5) / sigma;
39 (-0.5 * t_norm * t_norm).exp()
40 }
41 Self::Square => {
42 if t >= 0.0 && t <= 1.0 {
43 1.0
44 } else {
45 0.0
46 }
47 }
48 Self::RaisedCosine => {
49 if t >= 0.0 && t <= 1.0 {
50 let phase = 2.0 * PI * t;
51 0.5 * (1.0 - phase.cos())
52 } else {
53 0.0
54 }
55 }
56 Self::HyperbolicSecant { width } => {
57 let t_scaled = (t - 0.5) / width;
58 1.0 / t_scaled.cosh()
59 }
60 Self::HermiteGaussian { n, sigma } => {
61 let t_norm = (t - 0.5) / sigma;
62 let gaussian = (-0.5 * t_norm * t_norm).exp();
63 let hermite = self.hermite_polynomial(*n, t_norm);
64 gaussian * hermite
65 }
66 }
67 }
68
69 fn hermite_polynomial(&self, n: usize, x: f64) -> f64 {
71 match n {
72 0 => 1.0,
73 1 => 2.0 * x,
74 _ => {
75 let mut h_prev_prev = 1.0;
76 let mut h_prev = 2.0 * x;
77 for i in 2..=n {
78 let h_curr = (2.0 * x).mul_add(h_prev, -(2.0 * (i - 1) as f64 * h_prev_prev));
79 h_prev_prev = h_prev;
80 h_prev = h_curr;
81 }
82 h_prev
83 }
84 }
85 }
86
87 pub fn drag_derivative(&self, t: f64) -> f64 {
89 match self {
90 Self::DRAG { sigma, beta } => {
91 let t_norm = (t - 0.5) / sigma;
92 let gaussian = (-0.5 * t_norm * t_norm).exp();
93 let derivative = -t_norm / sigma * gaussian;
94 beta * derivative
95 }
96 _ => 0.0,
97 }
98 }
99}
100
101pub struct Pulse {
103 pub duration: f64,
105 pub amplitude: f64,
107 pub frequency: f64,
109 pub phase: f64,
111 pub envelope: PulseEnvelope,
113 pub sample_rate: f64,
115 pub phase_modulation: Option<Box<dyn Fn(f64) -> f64 + Send + Sync>>,
117}
118
119impl std::fmt::Debug for Pulse {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 f.debug_struct("Pulse")
122 .field("duration", &self.duration)
123 .field("amplitude", &self.amplitude)
124 .field("frequency", &self.frequency)
125 .field("phase", &self.phase)
126 .field("envelope", &self.envelope)
127 .field("sample_rate", &self.sample_rate)
128 .field("phase_modulation", &self.phase_modulation.is_some())
129 .finish()
130 }
131}
132
133impl Clone for Pulse {
134 fn clone(&self) -> Self {
135 Self {
136 duration: self.duration,
137 amplitude: self.amplitude,
138 frequency: self.frequency,
139 phase: self.phase,
140 envelope: self.envelope.clone(),
141 sample_rate: self.sample_rate,
142 phase_modulation: None, }
144 }
145}
146
147impl Pulse {
148 pub fn new(
150 duration: f64,
151 amplitude: f64,
152 frequency: f64,
153 phase: f64,
154 envelope: PulseEnvelope,
155 sample_rate: f64,
156 ) -> Self {
157 Self {
158 duration,
159 amplitude,
160 frequency,
161 phase,
162 envelope,
163 sample_rate,
164 phase_modulation: None,
165 }
166 }
167
168 pub fn generate_waveform(&self) -> QuantRS2Result<Array1<Complex64>> {
170 let num_samples = (self.duration * self.sample_rate).ceil() as usize;
171 let dt = 1.0 / self.sample_rate;
172
173 let mut waveform = Array1::zeros(num_samples);
174
175 for i in 0..num_samples {
176 let t = i as f64 * dt;
177 let t_norm = t / self.duration;
178
179 let envelope_value = self.envelope.evaluate(t_norm);
180 let phase_mod = if let Some(ref phase_fn) = self.phase_modulation {
181 phase_fn(t)
182 } else {
183 0.0
184 };
185
186 let total_phase = (2.0 * PI * self.frequency).mul_add(t, self.phase) + phase_mod;
187 let complex_amplitude = Complex64::new(0.0, total_phase).exp();
188
189 waveform[i] = self.amplitude * envelope_value * complex_amplitude;
190 }
191
192 Ok(waveform)
193 }
194
195 pub fn generate_drag_waveform(&self) -> QuantRS2Result<(Array1<Complex64>, Array1<Complex64>)> {
197 let num_samples = (self.duration * self.sample_rate).ceil() as usize;
198 let dt = 1.0 / self.sample_rate;
199
200 let mut i_component = Array1::zeros(num_samples);
201 let mut q_component = Array1::zeros(num_samples);
202
203 for i in 0..num_samples {
204 let t = i as f64 * dt;
205 let t_norm = t / self.duration;
206
207 let envelope_value = self.envelope.evaluate(t_norm);
208 let drag_derivative = self.envelope.drag_derivative(t_norm);
209
210 let phase_mod = if let Some(ref phase_fn) = self.phase_modulation {
211 phase_fn(t)
212 } else {
213 0.0
214 };
215
216 let total_phase = (2.0 * PI * self.frequency).mul_add(t, self.phase) + phase_mod;
217
218 i_component[i] =
220 Complex64::new(self.amplitude * envelope_value * total_phase.cos(), 0.0);
221
222 q_component[i] = Complex64::new(
224 self.amplitude * (envelope_value * total_phase.sin() + drag_derivative),
225 0.0,
226 );
227 }
228
229 Ok((i_component, q_component))
230 }
231}
232
233#[derive(Debug, Clone)]
235pub struct QubitControlParams {
236 pub drive_frequency: f64,
238 pub anharmonicity: f64,
240 pub rabi_frequency: f64,
242 pub t1: f64,
244 pub t2: f64,
246 pub gate_time: f64,
248 pub pi_pulse_amplitude: f64,
250 pub drag_parameter: f64,
252}
253
254impl Default for QubitControlParams {
255 fn default() -> Self {
256 Self {
257 drive_frequency: 5.0, anharmonicity: -200.0, rabi_frequency: 20.0, t1: 50.0, t2: 30.0, gate_time: 25.0, pi_pulse_amplitude: 0.5,
264 drag_parameter: 0.5,
265 }
266 }
267}
268
269#[derive(Debug, Clone)]
271pub struct CouplingParams {
272 pub coupling_strength: f64,
274 pub crosstalk: f64,
276 pub zz_coupling: f64,
278}
279
280impl Default for CouplingParams {
281 fn default() -> Self {
282 Self {
283 coupling_strength: 10.0, crosstalk: 0.02, zz_coupling: 50.0, }
287 }
288}
289
290#[derive(Debug, Clone)]
292pub struct HardwareCalibration {
293 pub qubit_params: FxHashMap<QubitId, QubitControlParams>,
295 pub coupling_params: FxHashMap<(QubitId, QubitId), CouplingParams>,
297 pub flux_params: FxHashMap<QubitId, f64>,
299 pub readout_params: FxHashMap<QubitId, (f64, f64)>, pub timing_constraints: TimingConstraints,
303}
304
305#[derive(Debug, Clone)]
307pub struct TimingConstraints {
308 pub min_pulse_separation: f64,
310 pub max_pulse_duration: f64,
312 pub sample_rate: f64,
314 pub clock_resolution: f64,
316}
317
318impl Default for TimingConstraints {
319 fn default() -> Self {
320 Self {
321 min_pulse_separation: 2.0,
322 max_pulse_duration: 1000.0,
323 sample_rate: 2.0, clock_resolution: 0.5, }
326 }
327}
328
329impl Default for HardwareCalibration {
330 fn default() -> Self {
331 Self {
332 qubit_params: FxHashMap::default(),
333 coupling_params: FxHashMap::default(),
334 flux_params: FxHashMap::default(),
335 readout_params: FxHashMap::default(),
336 timing_constraints: TimingConstraints::default(),
337 }
338 }
339}
340
341#[derive(Debug, Clone)]
343pub struct PulseSequence {
344 pub pulses: Vec<(f64, QubitId, Pulse)>, pub duration: f64,
348 pub name: String,
350}
351
352impl PulseSequence {
353 pub const fn new(name: String) -> Self {
355 Self {
356 pulses: Vec::new(),
357 duration: 0.0,
358 name,
359 }
360 }
361
362 pub fn add_pulse(&mut self, start_time: f64, qubit: QubitId, pulse: Pulse) {
364 let end_time = start_time + pulse.duration;
365 if end_time > self.duration {
366 self.duration = end_time;
367 }
368 self.pulses.push((start_time, qubit, pulse));
369 }
370
371 pub fn get_qubit_pulses(&self, qubit: QubitId) -> Vec<&(f64, QubitId, Pulse)> {
373 self.pulses.iter().filter(|(_, q, _)| *q == qubit).collect()
374 }
375
376 pub fn check_overlaps(&self) -> QuantRS2Result<()> {
378 let mut qubit_timings: FxHashMap<QubitId, Vec<(f64, f64)>> = FxHashMap::default();
379
380 for (start_time, qubit, pulse) in &self.pulses {
381 let end_time = start_time + pulse.duration;
382 let timings = qubit_timings.entry(*qubit).or_default();
383
384 for &(existing_start, existing_end) in timings.iter() {
385 if start_time < &existing_end && end_time > existing_start {
386 return Err(QuantRS2Error::InvalidOperation(format!(
387 "Pulse overlap detected on qubit {qubit:?}"
388 )));
389 }
390 }
391
392 timings.push((*start_time, end_time));
393 }
394
395 Ok(())
396 }
397}
398
399pub struct PulseCompiler {
401 pub calibration: HardwareCalibration,
403 pub pulse_library: FxHashMap<String, Box<dyn Fn(&QubitControlParams) -> Pulse + Send + Sync>>,
405}
406
407impl std::fmt::Debug for PulseCompiler {
408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 f.debug_struct("PulseCompiler")
410 .field("calibration", &self.calibration)
411 .field(
412 "pulse_library_keys",
413 &self.pulse_library.keys().collect::<Vec<_>>(),
414 )
415 .finish()
416 }
417}
418
419impl PulseCompiler {
420 pub fn new(calibration: HardwareCalibration) -> Self {
422 let mut compiler = Self {
423 calibration,
424 pulse_library: FxHashMap::default(),
425 };
426
427 compiler.initialize_pulse_library();
428 compiler
429 }
430
431 fn initialize_pulse_library(&mut self) {
433 self.pulse_library.insert(
435 "X".to_string(),
436 Box::new(|params| {
437 Pulse::new(
438 params.gate_time,
439 params.pi_pulse_amplitude,
440 params.drive_frequency,
441 0.0, PulseEnvelope::DRAG {
443 sigma: params.gate_time / 6.0,
444 beta: params.drag_parameter,
445 },
446 2.0, )
448 }),
449 );
450
451 self.pulse_library.insert(
453 "Y".to_string(),
454 Box::new(|params| {
455 Pulse::new(
456 params.gate_time,
457 params.pi_pulse_amplitude,
458 params.drive_frequency,
459 PI / 2.0, PulseEnvelope::DRAG {
461 sigma: params.gate_time / 6.0,
462 beta: params.drag_parameter,
463 },
464 2.0,
465 )
466 }),
467 );
468
469 self.pulse_library.insert(
471 "H".to_string(),
472 Box::new(|params| {
473 Pulse::new(
476 params.gate_time,
477 params.pi_pulse_amplitude * 0.707, params.drive_frequency,
479 PI / 4.0, PulseEnvelope::DRAG {
481 sigma: params.gate_time / 6.0,
482 beta: params.drag_parameter,
483 },
484 2.0,
485 )
486 }),
487 );
488
489 self.pulse_library.insert(
491 "RZ".to_string(),
492 Box::new(|_params| {
493 Pulse::new(
495 0.0, 0.0, 0.0, 0.0, PulseEnvelope::Square,
500 2.0,
501 )
502 }),
503 );
504 }
505
506 pub fn compile_gate(
508 &self,
509 gate: &dyn GateOp,
510 qubits: &[QubitId],
511 ) -> QuantRS2Result<PulseSequence> {
512 let gate_name = gate.name();
513 let mut sequence = PulseSequence::new(gate_name.to_string());
514
515 match gate_name {
516 "X" | "Y" | "H" => {
517 if qubits.len() != 1 {
518 return Err(QuantRS2Error::InvalidOperation(
519 "Single-qubit gate requires exactly one qubit".to_string(),
520 ));
521 }
522
523 let qubit = qubits[0];
524 let default_params = QubitControlParams::default();
525 let params = self
526 .calibration
527 .qubit_params
528 .get(&qubit)
529 .unwrap_or(&default_params);
530
531 if let Some(pulse_fn) = self.pulse_library.get(gate_name) {
532 let pulse = pulse_fn(params);
533 sequence.add_pulse(0.0, qubit, pulse);
534 }
535 }
536 "CNOT" | "CX" => {
537 if qubits.len() != 2 {
538 return Err(QuantRS2Error::InvalidOperation(
539 "CNOT gate requires exactly two qubits".to_string(),
540 ));
541 }
542
543 let control = qubits[0];
544 let target = qubits[1];
545
546 let default_control_params = QubitControlParams::default();
550 let default_target_params = QubitControlParams::default();
551 let control_params = self
552 .calibration
553 .qubit_params
554 .get(&control)
555 .unwrap_or(&default_control_params);
556 let target_params = self
557 .calibration
558 .qubit_params
559 .get(&target)
560 .unwrap_or(&default_target_params);
561
562 let cr_pulse = Pulse::new(
564 50.0, 0.3 * control_params.pi_pulse_amplitude,
566 target_params.drive_frequency,
567 0.0,
568 PulseEnvelope::Square,
569 2.0,
570 );
571
572 sequence.add_pulse(0.0, control, cr_pulse);
573
574 let echo_pulse = Pulse::new(
576 25.0,
577 target_params.pi_pulse_amplitude,
578 target_params.drive_frequency,
579 PI, PulseEnvelope::DRAG {
581 sigma: 25.0 / 6.0,
582 beta: target_params.drag_parameter,
583 },
584 2.0,
585 );
586
587 sequence.add_pulse(25.0, target, echo_pulse);
588 }
589 _ => {
590 return Err(QuantRS2Error::InvalidOperation(format!(
591 "Gate '{gate_name}' not supported in pulse compiler"
592 )));
593 }
594 }
595
596 sequence.check_overlaps()?;
597 Ok(sequence)
598 }
599
600 pub fn optimize_sequence(&self, sequence: &mut PulseSequence) -> QuantRS2Result<()> {
602 let constraints = &self.calibration.timing_constraints;
604
605 for (start_time, _, _pulse) in &mut sequence.pulses {
607 *start_time =
608 (*start_time / constraints.clock_resolution).round() * constraints.clock_resolution;
609 }
610
611 sequence
613 .pulses
614 .sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
615
616 for i in 1..sequence.pulses.len() {
617 let prev_end = sequence.pulses[i - 1].0 + sequence.pulses[i - 1].2.duration;
618 let curr_start = sequence.pulses[i].0;
619
620 if curr_start - prev_end < constraints.min_pulse_separation {
621 sequence.pulses[i].0 = prev_end + constraints.min_pulse_separation;
622 }
623 }
624
625 if let Some((start_time, _, pulse)) = sequence.pulses.last() {
627 sequence.duration = start_time + pulse.duration;
628 }
629
630 Ok(())
631 }
632
633 pub fn add_qubit_calibration(&mut self, qubit: QubitId, params: QubitControlParams) {
635 self.calibration.qubit_params.insert(qubit, params);
636 }
637
638 pub fn add_coupling_calibration(&mut self, q1: QubitId, q2: QubitId, params: CouplingParams) {
640 self.calibration
641 .coupling_params
642 .insert((q1, q2), params.clone());
643 self.calibration.coupling_params.insert((q2, q1), params); }
645
646 pub fn generate_arbitrary_rotation(
648 &self,
649 qubit: QubitId,
650 theta: f64,
651 phi: f64,
652 ) -> QuantRS2Result<PulseSequence> {
653 let default_params = QubitControlParams::default();
654 let params = self
655 .calibration
656 .qubit_params
657 .get(&qubit)
658 .unwrap_or(&default_params);
659
660 let amplitude = (theta / PI) * params.pi_pulse_amplitude;
661
662 let pulse = Pulse::new(
663 params.gate_time,
664 amplitude,
665 params.drive_frequency,
666 phi,
667 PulseEnvelope::DRAG {
668 sigma: params.gate_time / 6.0,
669 beta: params.drag_parameter,
670 },
671 2.0,
672 );
673
674 let mut sequence = PulseSequence::new(format!("R({theta:.3}, {phi:.3})"));
675 sequence.add_pulse(0.0, qubit, pulse);
676
677 Ok(sequence)
678 }
679}
680
681#[derive(Debug, Clone)]
683pub struct PulseNoiseModel {
684 pub amplitude_noise: f64,
686 pub phase_noise: f64,
688 pub timing_jitter: f64,
690 pub flux_noise: f64,
692}
693
694impl Default for PulseNoiseModel {
695 fn default() -> Self {
696 Self {
697 amplitude_noise: 0.01, phase_noise: 0.01, timing_jitter: 0.1, flux_noise: 0.1, }
702 }
703}
704
705impl PulseNoiseModel {
706 pub fn apply_noise(
708 &self,
709 pulse: &mut Pulse,
710 rng: &mut dyn scirs2_core::random::RngCore,
711 ) -> QuantRS2Result<()> {
712 use scirs2_core::random::prelude::*;
713
714 let amplitude_factor = rng
716 .gen_range(0.0_f64..1.0_f64)
717 .mul_add(self.amplitude_noise, 1.0_f64);
718 pulse.amplitude *= amplitude_factor;
719
720 let phase_shift = rng.gen_range(0.0_f64..1.0_f64) * self.phase_noise;
722 pulse.phase += phase_shift;
723
724 let freq_shift = rng.gen_range(0.0_f64..1.0_f64) * self.flux_noise / 1000.0; pulse.frequency += freq_shift;
727
728 Ok(())
729 }
730
731 pub fn apply_timing_jitter(
733 &self,
734 sequence: &mut PulseSequence,
735 rng: &mut dyn scirs2_core::random::RngCore,
736 ) -> QuantRS2Result<()> {
737 use scirs2_core::random::prelude::*;
738
739 for (start_time, _, _) in &mut sequence.pulses {
740 let jitter = rng.gen_range(0.0..1.0) * self.timing_jitter;
741 *start_time += jitter;
742 }
743
744 Ok(())
745 }
746}
747
748#[cfg(test)]
749mod tests {
750 use super::*;
751 use crate::gate::{multi::CNOT, single::PauliX};
752
753 #[test]
754 fn test_pulse_envelope_gaussian() {
755 let envelope = PulseEnvelope::Gaussian { sigma: 0.1 };
756
757 let center_value = envelope.evaluate(0.5);
759 assert!((center_value - 1.0).abs() < 1e-10);
760
761 let left_value = envelope.evaluate(0.3);
763 let right_value = envelope.evaluate(0.7);
764 assert!((left_value - right_value).abs() < 1e-10);
765 }
766
767 #[test]
768 fn test_pulse_waveform_generation() {
769 let pulse = Pulse::new(
770 10.0, 0.5, 5.0, 0.0, PulseEnvelope::Gaussian { sigma: 0.1 },
775 2.0, );
777
778 let waveform = pulse
779 .generate_waveform()
780 .expect("Failed to generate waveform");
781 assert_eq!(waveform.len(), 20); let max_amplitude = waveform.iter().map(|x| x.norm()).fold(0.0, f64::max);
785 assert!(max_amplitude > 0.0);
786 }
787
788 #[test]
789 fn test_drag_pulse_generation() {
790 let pulse = Pulse::new(
791 20.0,
792 1.0,
793 5.0,
794 0.0,
795 PulseEnvelope::DRAG {
796 sigma: 0.1,
797 beta: 0.5,
798 },
799 2.0,
800 );
801
802 let (i_comp, q_comp) = pulse
803 .generate_drag_waveform()
804 .expect("Failed to generate DRAG waveform");
805 assert_eq!(i_comp.len(), 40);
806 assert_eq!(q_comp.len(), 40);
807
808 let i_max = i_comp.iter().map(|x| x.norm()).fold(0.0, f64::max);
810 let q_max = q_comp.iter().map(|x| x.norm()).fold(0.0, f64::max);
811 assert!(i_max > 0.0);
812 assert!(q_max > 0.0);
813 }
814
815 #[test]
816 fn test_pulse_compiler_single_qubit() {
817 let calibration = HardwareCalibration::default();
818 let compiler = PulseCompiler::new(calibration);
819
820 let gate = PauliX { target: QubitId(0) };
821 let qubits = vec![QubitId(0)];
822
823 let sequence = compiler
824 .compile_gate(&gate, &qubits)
825 .expect("Failed to compile single qubit gate");
826 assert_eq!(sequence.pulses.len(), 1);
827 assert_eq!(sequence.pulses[0].1, QubitId(0));
828 }
829
830 #[test]
831 fn test_pulse_compiler_cnot() {
832 let calibration = HardwareCalibration::default();
833 let compiler = PulseCompiler::new(calibration);
834
835 let gate = CNOT {
836 control: QubitId(0),
837 target: QubitId(1),
838 };
839 let qubits = vec![QubitId(0), QubitId(1)];
840
841 let sequence = compiler
842 .compile_gate(&gate, &qubits)
843 .expect("Failed to compile CNOT gate");
844 assert!(sequence.pulses.len() >= 2); }
846
847 #[test]
848 fn test_pulse_sequence_overlap_detection() {
849 let mut sequence = PulseSequence::new("test".to_string());
850
851 let pulse1 = Pulse::new(10.0, 0.5, 5.0, 0.0, PulseEnvelope::Square, 2.0);
852 let pulse2 = Pulse::new(10.0, 0.5, 5.0, 0.0, PulseEnvelope::Square, 2.0);
853
854 sequence.add_pulse(0.0, QubitId(0), pulse1);
855 sequence.add_pulse(5.0, QubitId(0), pulse2); assert!(sequence.check_overlaps().is_err());
858 }
859
860 #[test]
861 fn test_pulse_sequence_no_overlap() {
862 let mut sequence = PulseSequence::new("test".to_string());
863
864 let pulse1 = Pulse::new(10.0, 0.5, 5.0, 0.0, PulseEnvelope::Square, 2.0);
865 let pulse2 = Pulse::new(10.0, 0.5, 5.0, 0.0, PulseEnvelope::Square, 2.0);
866
867 sequence.add_pulse(0.0, QubitId(0), pulse1);
868 sequence.add_pulse(15.0, QubitId(0), pulse2); assert!(sequence.check_overlaps().is_ok());
871 }
872
873 #[test]
874 fn test_arbitrary_rotation_compilation() {
875 let calibration = HardwareCalibration::default();
876 let compiler = PulseCompiler::new(calibration);
877
878 let theta = PI / 4.0; let phi = PI / 2.0; let sequence = compiler
882 .generate_arbitrary_rotation(QubitId(0), theta, phi)
883 .expect("Failed to generate arbitrary rotation");
884 assert_eq!(sequence.pulses.len(), 1);
885
886 let (_, _, pulse) = &sequence.pulses[0];
887 assert!((pulse.phase - phi).abs() < 1e-10);
888 }
889
890 #[test]
891 fn test_pulse_optimization() {
892 let mut calibration = HardwareCalibration::default();
893 calibration.timing_constraints.clock_resolution = 1.0; calibration.timing_constraints.min_pulse_separation = 5.0; let compiler = PulseCompiler::new(calibration);
897
898 let mut sequence = PulseSequence::new("test".to_string());
899 let pulse = Pulse::new(10.0, 0.5, 5.0, 0.0, PulseEnvelope::Square, 2.0);
900
901 sequence.add_pulse(2.7, QubitId(0), pulse); compiler
904 .optimize_sequence(&mut sequence)
905 .expect("Failed to optimize sequence");
906
907 assert!((sequence.pulses[0].0 - 3.0).abs() < 1e-10);
909 }
910}