1use crate::builder::Circuit;
7use quantrs2_core::{
9 error::{QuantRS2Error, QuantRS2Result},
10 gate::GateOp,
11 qubit::QubitId,
12};
13use scirs2_core::Complex64;
14use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16use std::f64::consts::PI;
17
18type C64 = Complex64;
20
21type Time = f64;
23
24type Frequency = f64;
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct Waveform {
30 pub samples: Vec<C64>,
32 pub sample_rate: f64,
34 pub duration: Time,
36}
37
38impl Waveform {
39 #[must_use]
41 pub fn new(samples: Vec<C64>, sample_rate: f64) -> Self {
42 let duration = (samples.len() as f64) / sample_rate;
43 Self {
44 samples,
45 sample_rate,
46 duration,
47 }
48 }
49
50 #[must_use]
52 pub fn gaussian(amplitude: f64, sigma: f64, duration: Time, sample_rate: f64) -> Self {
53 let n_samples = (duration * sample_rate) as usize;
54 let center = duration / 2.0;
55 let mut samples = Vec::with_capacity(n_samples);
56
57 for i in 0..n_samples {
58 let t = (i as f64) / sample_rate;
59 let envelope = amplitude * (-0.5 * ((t - center) / sigma).powi(2)).exp();
60 samples.push(C64::new(envelope, 0.0));
61 }
62
63 Self::new(samples, sample_rate)
64 }
65
66 #[must_use]
68 pub fn drag(amplitude: f64, sigma: f64, beta: f64, duration: Time, sample_rate: f64) -> Self {
69 let n_samples = (duration * sample_rate) as usize;
70 let center = duration / 2.0;
71 let mut samples = Vec::with_capacity(n_samples);
72
73 for i in 0..n_samples {
74 let t = (i as f64) / sample_rate;
75 let gaussian = amplitude * (-0.5 * ((t - center) / sigma).powi(2)).exp();
76 let derivative = -((t - center) / sigma.powi(2)) * gaussian;
77 let real_part = gaussian;
78 let imag_part = beta * derivative;
79 samples.push(C64::new(real_part, imag_part));
80 }
81
82 Self::new(samples, sample_rate)
83 }
84
85 #[must_use]
87 pub fn square(amplitude: f64, duration: Time, sample_rate: f64) -> Self {
88 let n_samples = (duration * sample_rate) as usize;
89 let samples = vec![C64::new(amplitude, 0.0); n_samples];
90 Self::new(samples, sample_rate)
91 }
92
93 pub fn modulate(&mut self, frequency: Frequency, phase: f64) {
95 for (i, sample) in self.samples.iter_mut().enumerate() {
96 let t = (i as f64) / self.sample_rate;
97 let rotation = C64::from_polar(1.0, (2.0 * PI * frequency).mul_add(t, phase));
98 *sample *= rotation;
99 }
100 }
101
102 pub fn scale(&mut self, factor: f64) {
104 for sample in &mut self.samples {
105 *sample *= factor;
106 }
107 }
108
109 pub fn max_amplitude(&self) -> f64 {
111 self.samples.iter().map(|s| s.norm()).fold(0.0, f64::max)
112 }
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
117pub enum Channel {
118 Drive(usize),
120 Measure(usize),
122 Control(usize, usize),
124 Aux(String),
126}
127
128#[derive(Debug, Clone)]
130pub enum PulseInstruction {
131 Play {
133 waveform: Waveform,
134 channel: Channel,
135 phase: f64,
136 },
137 SetFrequency {
139 channel: Channel,
140 frequency: Frequency,
141 },
142 SetPhase { channel: Channel, phase: f64 },
144 ShiftPhase { channel: Channel, phase: f64 },
146 Delay {
148 duration: Time,
149 channels: Vec<Channel>,
150 },
151 Barrier { channels: Vec<Channel> },
153 Acquire {
155 duration: Time,
156 channel: Channel,
157 memory_slot: usize,
158 },
159}
160
161#[derive(Debug, Clone)]
163pub struct PulseSchedule {
164 pub instructions: Vec<(Time, PulseInstruction)>,
166 pub duration: Time,
168 pub channels: Vec<Channel>,
170}
171
172impl Default for PulseSchedule {
173 fn default() -> Self {
174 Self::new()
175 }
176}
177
178impl PulseSchedule {
179 #[must_use]
181 pub const fn new() -> Self {
182 Self {
183 instructions: Vec::new(),
184 duration: 0.0,
185 channels: Vec::new(),
186 }
187 }
188
189 pub fn add_instruction(&mut self, time: Time, instruction: PulseInstruction) {
191 let inst_duration = match &instruction {
193 PulseInstruction::Play { waveform, .. } => waveform.duration,
194 PulseInstruction::Delay { duration, .. } => *duration,
195 PulseInstruction::Acquire { duration, .. } => *duration,
196 _ => 0.0,
197 };
198 self.duration = self.duration.max(time + inst_duration);
199
200 match &instruction {
202 PulseInstruction::Play { channel, .. }
203 | PulseInstruction::SetFrequency { channel, .. }
204 | PulseInstruction::SetPhase { channel, .. }
205 | PulseInstruction::ShiftPhase { channel, .. } => {
206 if !self.channels.contains(channel) {
207 self.channels.push(channel.clone());
208 }
209 }
210 PulseInstruction::Delay { channels, .. } | PulseInstruction::Barrier { channels } => {
211 for channel in channels {
212 if !self.channels.contains(channel) {
213 self.channels.push(channel.clone());
214 }
215 }
216 }
217 PulseInstruction::Acquire { channel, .. } => {
218 if !self.channels.contains(channel) {
219 self.channels.push(channel.clone());
220 }
221 }
222 }
223
224 self.instructions.push((time, instruction));
225 }
226
227 pub fn append(&mut self, other: &Self, time_offset: Time) {
229 for (time, instruction) in &other.instructions {
230 self.add_instruction(time + time_offset, instruction.clone());
231 }
232 }
233
234 pub fn align_parallel(&mut self, schedules: Vec<Self>) {
236 let start_time = self.duration;
237 let mut max_duration: f64 = 0.0;
238
239 for schedule in schedules {
240 self.append(&schedule, start_time);
241 max_duration = max_duration.max(schedule.duration);
242 }
243
244 self.duration = start_time + max_duration;
245 }
246}
247
248#[derive(Debug, Clone)]
250pub struct PulseCalibration {
251 pub gate_name: String,
253 pub qubits: Vec<QubitId>,
255 pub parameters: HashMap<String, f64>,
257 pub schedule: PulseSchedule,
259}
260
261pub struct PulseCompiler {
263 calibrations: HashMap<String, Vec<PulseCalibration>>,
265 device_config: DeviceConfig,
267}
268
269#[derive(Debug, Clone)]
271pub struct DeviceConfig {
272 pub qubit_frequencies: HashMap<usize, Frequency>,
274 pub coupling_strengths: HashMap<(usize, usize), f64>,
276 pub drive_amplitudes: HashMap<usize, f64>,
278 pub meas_frequencies: HashMap<usize, Frequency>,
280 pub sample_rate: f64,
282}
283
284impl DeviceConfig {
285 #[must_use]
287 pub fn default_config(num_qubits: usize) -> Self {
288 let mut config = Self {
289 qubit_frequencies: HashMap::new(),
290 coupling_strengths: HashMap::new(),
291 drive_amplitudes: HashMap::new(),
292 meas_frequencies: HashMap::new(),
293 sample_rate: 1.0, };
295
296 for i in 0..num_qubits {
298 config
299 .qubit_frequencies
300 .insert(i, (i as f64).mul_add(0.1, 5.0));
301 config
302 .meas_frequencies
303 .insert(i, (i as f64).mul_add(0.05, 6.5));
304 config.drive_amplitudes.insert(i, 0.1);
305 }
306
307 for i in 0..(num_qubits - 1) {
309 config.coupling_strengths.insert((i, i + 1), 0.01); }
311
312 config
313 }
314}
315
316impl PulseCompiler {
317 #[must_use]
319 pub fn new(device_config: DeviceConfig) -> Self {
320 let mut compiler = Self {
321 calibrations: HashMap::new(),
322 device_config,
323 };
324
325 compiler.add_default_calibrations();
327 compiler
328 }
329
330 fn add_default_calibrations(&mut self) {
332 self.add_single_qubit_calibrations();
334
335 self.add_two_qubit_calibrations();
337 }
338
339 fn add_single_qubit_calibrations(&mut self) {
341 let sample_rate = self.device_config.sample_rate;
342
343 let x_waveform = Waveform::gaussian(0.5, 10.0, 40.0, sample_rate);
345 let mut x_schedule = PulseSchedule::new();
346 x_schedule.add_instruction(
347 0.0,
348 PulseInstruction::Play {
349 waveform: x_waveform,
350 channel: Channel::Drive(0),
351 phase: 0.0,
352 },
353 );
354
355 self.calibrations.insert(
356 "X".to_string(),
357 vec![PulseCalibration {
358 gate_name: "X".to_string(),
359 qubits: vec![QubitId(0)],
360 parameters: HashMap::new(),
361 schedule: x_schedule,
362 }],
363 );
364
365 let y_waveform = Waveform::gaussian(0.5, 10.0, 40.0, sample_rate);
367 let mut y_schedule = PulseSchedule::new();
368 y_schedule.add_instruction(
369 0.0,
370 PulseInstruction::Play {
371 waveform: y_waveform,
372 channel: Channel::Drive(0),
373 phase: PI / 2.0,
374 },
375 );
376
377 self.calibrations.insert(
378 "Y".to_string(),
379 vec![PulseCalibration {
380 gate_name: "Y".to_string(),
381 qubits: vec![QubitId(0)],
382 parameters: HashMap::new(),
383 schedule: y_schedule,
384 }],
385 );
386 }
387
388 fn add_two_qubit_calibrations(&mut self) {
390 let sample_rate = self.device_config.sample_rate;
391
392 let cr_waveform = Waveform::square(0.1, 200.0, sample_rate);
394 let mut cnot_schedule = PulseSchedule::new();
395
396 cnot_schedule.add_instruction(
398 0.0,
399 PulseInstruction::Play {
400 waveform: cr_waveform,
401 channel: Channel::Control(0, 1),
402 phase: 0.0,
403 },
404 );
405
406 let echo_waveform = Waveform::gaussian(0.25, 10.0, 40.0, sample_rate);
408 cnot_schedule.add_instruction(
409 100.0,
410 PulseInstruction::Play {
411 waveform: echo_waveform,
412 channel: Channel::Drive(1),
413 phase: PI,
414 },
415 );
416
417 self.calibrations.insert(
418 "CNOT".to_string(),
419 vec![PulseCalibration {
420 gate_name: "CNOT".to_string(),
421 qubits: vec![QubitId(0), QubitId(1)],
422 parameters: HashMap::new(),
423 schedule: cnot_schedule,
424 }],
425 );
426 }
427
428 pub fn compile<const N: usize>(&self, circuit: &Circuit<N>) -> QuantRS2Result<PulseSchedule> {
430 let mut schedule = PulseSchedule::new();
431 let mut time = 0.0;
432
433 for gate in circuit.gates() {
434 let gate_schedule = self.compile_gate(gate.as_ref())?;
435 schedule.append(&gate_schedule, time);
436 time += gate_schedule.duration;
437 }
438
439 Ok(schedule)
440 }
441
442 fn compile_gate(&self, gate: &dyn GateOp) -> QuantRS2Result<PulseSchedule> {
444 let gate_name = gate.name();
445 let qubits = gate.qubits();
446
447 if let Some(calibrations) = self.calibrations.get(gate_name) {
449 for calib in calibrations {
450 if calib.qubits == qubits {
451 return Ok(self.instantiate_calibration(calib, qubits));
452 }
453 }
454 }
455
456 self.default_gate_schedule(gate)
458 }
459
460 fn instantiate_calibration(
462 &self,
463 calibration: &PulseCalibration,
464 qubits: Vec<QubitId>,
465 ) -> PulseSchedule {
466 let mut schedule = calibration.schedule.clone();
467
468 let mut remapped_instructions = Vec::new();
470 for (time, instruction) in schedule.instructions {
471 let remapped = match instruction {
472 PulseInstruction::Play {
473 waveform,
474 channel,
475 phase,
476 } => {
477 let new_channel = self.remap_channel(&channel, &qubits);
478 PulseInstruction::Play {
479 waveform,
480 channel: new_channel,
481 phase,
482 }
483 }
484 PulseInstruction::SetFrequency { channel, frequency } => {
485 let new_channel = self.remap_channel(&channel, &qubits);
486 PulseInstruction::SetFrequency {
487 channel: new_channel,
488 frequency,
489 }
490 }
491 _ => instruction,
493 };
494 remapped_instructions.push((time, remapped));
495 }
496
497 schedule.instructions = remapped_instructions;
498 schedule
499 }
500
501 fn remap_channel(&self, channel: &Channel, qubits: &[QubitId]) -> Channel {
503 match channel {
504 Channel::Drive(0) => Channel::Drive(qubits[0].id() as usize),
505 Channel::Drive(1) if qubits.len() > 1 => Channel::Drive(qubits[1].id() as usize),
506 Channel::Control(0, 1) if qubits.len() > 1 => {
507 Channel::Control(qubits[0].id() as usize, qubits[1].id() as usize)
508 }
509 _ => channel.clone(),
510 }
511 }
512
513 fn default_gate_schedule(&self, gate: &dyn GateOp) -> QuantRS2Result<PulseSchedule> {
515 let mut schedule = PulseSchedule::new();
516 let qubits = gate.qubits();
517
518 let waveform = Waveform::gaussian(0.5, 10.0, 40.0, self.device_config.sample_rate);
520
521 for qubit in qubits {
522 schedule.add_instruction(
523 0.0,
524 PulseInstruction::Play {
525 waveform: waveform.clone(),
526 channel: Channel::Drive(qubit.id() as usize),
527 phase: 0.0,
528 },
529 );
530 }
531
532 Ok(schedule)
533 }
534}
535
536pub struct PulseOptimizer {
538 target_fidelity: f64,
540 max_iterations: usize,
542}
543
544impl Default for PulseOptimizer {
545 fn default() -> Self {
546 Self::new()
547 }
548}
549
550impl PulseOptimizer {
551 #[must_use]
553 pub const fn new() -> Self {
554 Self {
555 target_fidelity: 0.999,
556 max_iterations: 100,
557 }
558 }
559
560 pub const fn optimize(&self, schedule: &mut PulseSchedule) -> QuantRS2Result<()> {
562 Ok(())
565 }
566
567 pub fn apply_drag_correction(&self, waveform: &mut Waveform, beta: f64) -> QuantRS2Result<()> {
569 let mut derivative = vec![C64::new(0.0, 0.0); waveform.samples.len()];
571
572 for i in 1..(waveform.samples.len() - 1) {
573 let dt = 1.0 / waveform.sample_rate;
574 derivative[i] = (waveform.samples[i + 1] - waveform.samples[i - 1]) / (2.0 * dt);
575 }
576
577 for (sample, deriv) in waveform.samples.iter_mut().zip(derivative.iter()) {
578 *sample += C64::new(0.0, beta * deriv.re);
579 }
580
581 Ok(())
582 }
583}
584
585#[cfg(test)]
586mod tests {
587 use super::*;
588 use quantrs2_core::gate::single::Hadamard;
589
590 #[test]
591 fn test_waveform_creation() {
592 let gaussian = Waveform::gaussian(1.0, 10.0, 40.0, 1.0);
593 assert_eq!(gaussian.samples.len(), 40);
594 assert!(gaussian.max_amplitude() <= 1.0);
595
596 let square = Waveform::square(0.5, 20.0, 1.0);
597 assert_eq!(square.samples.len(), 20);
598 assert_eq!(square.max_amplitude(), 0.5);
599 }
600
601 #[test]
602 fn test_drag_pulse() {
603 let drag = Waveform::drag(1.0, 10.0, 0.1, 40.0, 1.0);
604 assert_eq!(drag.samples.len(), 40);
605
606 let has_imag = drag.samples.iter().any(|s| s.im.abs() > 1e-10);
608 assert!(has_imag);
609 }
610
611 #[test]
612 fn test_pulse_schedule() {
613 let mut schedule = PulseSchedule::new();
614 let waveform = Waveform::gaussian(0.5, 10.0, 40.0, 1.0);
615
616 schedule.add_instruction(
617 0.0,
618 PulseInstruction::Play {
619 waveform: waveform.clone(),
620 channel: Channel::Drive(0),
621 phase: 0.0,
622 },
623 );
624
625 schedule.add_instruction(
626 50.0,
627 PulseInstruction::Play {
628 waveform,
629 channel: Channel::Drive(1),
630 phase: PI / 2.0,
631 },
632 );
633
634 assert_eq!(schedule.instructions.len(), 2);
635 assert_eq!(schedule.duration, 90.0); }
637
638 #[test]
639 fn test_pulse_compiler() {
640 let device_config = DeviceConfig::default_config(2);
641 let compiler = PulseCompiler::new(device_config);
642
643 let mut circuit = Circuit::<2>::new();
644 circuit
645 .add_gate(Hadamard { target: QubitId(0) })
646 .expect("add H gate to circuit");
647
648 let schedule = compiler
649 .compile(&circuit)
650 .expect("pulse compilation should succeed");
651 assert!(schedule.duration > 0.0);
652 }
653}