quantum_diffusion/
quantum_diffusion.rs

1//! Quantum Diffusion Model Example
2//!
3//! This example demonstrates quantum diffusion models for generative modeling,
4//! including DDPM-style models and score-based diffusion.
5
6use quantrs2_ml::autodiff::optimizers::Adam;
7use quantrs2_ml::prelude::*;
8use scirs2_core::ndarray::{s, Array1, Array2};
9use scirs2_core::random::prelude::*;
10
11fn main() -> Result<()> {
12    println!("=== Quantum Diffusion Model Demo ===\n");
13
14    // Step 1: Demonstrate noise schedules
15    println!("1. Comparing Noise Schedules...");
16    compare_noise_schedules()?;
17
18    // Step 2: Train quantum diffusion model on simple data
19    println!("\n2. Training Quantum Diffusion Model...");
20    train_diffusion_model()?;
21
22    // Step 3: Generate samples
23    println!("\n3. Generating New Samples...");
24    generate_samples()?;
25
26    // Step 4: Score-based diffusion
27    println!("\n4. Score-Based Diffusion Demo...");
28    score_diffusion_demo()?;
29
30    // Step 5: Demonstrate diffusion process
31    println!("\n5. Visualizing Diffusion Process...");
32    visualize_diffusion_process()?;
33
34    println!("\n=== Diffusion Model Demo Complete ===");
35
36    Ok(())
37}
38
39/// Compare different noise schedules
40fn compare_noise_schedules() -> Result<()> {
41    let num_timesteps = 100;
42
43    let schedules = vec![
44        (
45            "Linear",
46            NoiseSchedule::Linear {
47                beta_start: 0.0001,
48                beta_end: 0.02,
49            },
50        ),
51        ("Cosine", NoiseSchedule::Cosine { s: 0.008 }),
52        (
53            "Quadratic",
54            NoiseSchedule::Quadratic {
55                beta_start: 0.0001,
56                beta_end: 0.02,
57            },
58        ),
59        (
60            "Sigmoid",
61            NoiseSchedule::Sigmoid {
62                beta_start: 0.0001,
63                beta_end: 0.02,
64            },
65        ),
66    ];
67
68    println!("   Noise levels at different timesteps:");
69    println!("   Time     Linear   Cosine   Quadratic  Sigmoid");
70
71    for t in (0..=100).step_by(20) {
72        let t_idx = (t * (num_timesteps - 1) / 100).min(num_timesteps - 1);
73        print!("   t={t:3}%: ");
74
75        for (_, schedule) in &schedules {
76            let model = QuantumDiffusionModel::new(2, 4, num_timesteps, *schedule)?;
77            print!("{:8.4} ", model.betas()[t_idx]);
78        }
79        println!();
80    }
81
82    Ok(())
83}
84
85/// Train a quantum diffusion model
86fn train_diffusion_model() -> Result<()> {
87    // Generate synthetic 2D data (two moons)
88    let num_samples = 200;
89    let data = generate_two_moons(num_samples);
90
91    println!("   Generated {num_samples} samples of 2D two-moons data");
92
93    // Create diffusion model
94    let mut model = QuantumDiffusionModel::new(
95        2,  // data dimension
96        4,  // num qubits
97        50, // timesteps
98        NoiseSchedule::Cosine { s: 0.008 },
99    )?;
100
101    println!("   Created quantum diffusion model:");
102    println!("   - Data dimension: 2");
103    println!("   - Qubits: 4");
104    println!("   - Timesteps: 50");
105    println!("   - Schedule: Cosine");
106
107    // Train model
108    let mut optimizer = Adam::new(0.001);
109    let epochs = 100;
110    let batch_size = 32;
111
112    println!("\n   Training for {epochs} epochs...");
113    let losses = model.train(&data, &mut optimizer, epochs, batch_size)?;
114
115    // Print training statistics
116    println!("\n   Training Statistics:");
117    println!("   - Initial loss: {:.4}", losses[0]);
118    println!("   - Final loss: {:.4}", losses.last().unwrap());
119    println!(
120        "   - Improvement: {:.2}%",
121        (1.0 - losses.last().unwrap() / losses[0]) * 100.0
122    );
123
124    Ok(())
125}
126
127/// Generate samples from trained model
128fn generate_samples() -> Result<()> {
129    // Create a simple trained model
130    let model = QuantumDiffusionModel::new(
131        2,  // data dimension
132        4,  // num qubits
133        50, // timesteps
134        NoiseSchedule::Linear {
135            beta_start: 0.0001,
136            beta_end: 0.02,
137        },
138    )?;
139
140    // Generate samples
141    let num_samples = 10;
142    println!("   Generating {num_samples} samples...");
143
144    let samples = model.generate(num_samples)?;
145
146    println!("\n   Generated samples:");
147    for i in 0..num_samples.min(5) {
148        println!(
149            "   Sample {}: [{:.3}, {:.3}]",
150            i + 1,
151            samples[[i, 0]],
152            samples[[i, 1]]
153        );
154    }
155
156    // Compute statistics
157    let mean = samples.mean_axis(scirs2_core::ndarray::Axis(0)).unwrap();
158    let std = samples.std_axis(scirs2_core::ndarray::Axis(0), 0.0);
159
160    println!("\n   Sample statistics:");
161    println!("   - Mean: [{:.3}, {:.3}]", mean[0], mean[1]);
162    println!("   - Std:  [{:.3}, {:.3}]", std[0], std[1]);
163
164    Ok(())
165}
166
167/// Score-based diffusion demonstration
168fn score_diffusion_demo() -> Result<()> {
169    // Create score-based model
170    let model = QuantumScoreDiffusion::new(
171        2,  // data dimension
172        4,  // num qubits
173        10, // noise levels
174    )?;
175
176    println!("   Created quantum score-based diffusion model");
177    println!("   - Noise levels: {:?}", model.noise_levels());
178
179    // Test score estimation
180    let x = Array1::from_vec(vec![0.5, -0.3]);
181    let noise_level = 0.1;
182
183    let score = model.estimate_score(&x, noise_level)?;
184    println!("\n   Score estimation:");
185    println!("   - Input: [{:.3}, {:.3}]", x[0], x[1]);
186    println!("   - Noise level: {noise_level:.3}");
187    println!("   - Estimated score: [{:.3}, {:.3}]", score[0], score[1]);
188
189    // Langevin sampling
190    println!("\n   Langevin sampling:");
191    let init = Array1::from_vec(vec![2.0, 2.0]);
192    let num_steps = 100;
193    let step_size = 0.01;
194
195    let sample = model.langevin_sample(init.clone(), noise_level, num_steps, step_size)?;
196
197    println!("   - Initial: [{:.3}, {:.3}]", init[0], init[1]);
198    println!(
199        "   - After {} steps: [{:.3}, {:.3}]",
200        num_steps, sample[0], sample[1]
201    );
202    println!(
203        "   - Distance moved: {:.3}",
204        (sample[0] - init[0]).hypot(sample[1] - init[1])
205    );
206
207    Ok(())
208}
209
210/// Visualize the diffusion process
211fn visualize_diffusion_process() -> Result<()> {
212    let model = QuantumDiffusionModel::new(
213        2,  // data dimension
214        4,  // num qubits
215        20, // fewer timesteps for visualization
216        NoiseSchedule::Linear {
217            beta_start: 0.0001,
218            beta_end: 0.02,
219        },
220    )?;
221
222    // Start with a clear data point
223    let x0 = Array1::from_vec(vec![1.0, 0.5]);
224
225    println!("   Forward diffusion process:");
226    println!("   t=0 (original): [{:.3}, {:.3}]", x0[0], x0[1]);
227
228    // Show forward diffusion at different timesteps
229    for t in [5, 10, 15, 19] {
230        let (xt, _) = model.forward_diffusion(&x0, t)?;
231        let noise_level = (1.0 - model.alphas_cumprod()[t]).sqrt();
232        println!(
233            "   t={:2} (noise={:.3}): [{:.3}, {:.3}]",
234            t, noise_level, xt[0], xt[1]
235        );
236    }
237
238    println!("\n   Reverse diffusion process:");
239
240    // Start from noise
241    let mut xt = Array1::from_vec(vec![
242        2.0f64.mul_add(thread_rng().gen::<f64>(), -1.0),
243        2.0f64.mul_add(thread_rng().gen::<f64>(), -1.0),
244    ]);
245
246    println!("   t=19 (pure noise): [{:.3}, {:.3}]", xt[0], xt[1]);
247
248    // Show reverse diffusion
249    for t in [15, 10, 5, 0] {
250        xt = model.reverse_diffusion_step(&xt, t)?;
251        println!("   t={:2} (denoised): [{:.3}, {:.3}]", t, xt[0], xt[1]);
252    }
253
254    println!("\n   This demonstrates how diffusion models:");
255    println!("   1. Gradually add noise to data (forward process)");
256    println!("   2. Learn to reverse this process (backward process)");
257    println!("   3. Generate new samples by denoising random noise");
258
259    Ok(())
260}
261
262/// Generate two-moons dataset
263fn generate_two_moons(n_samples: usize) -> Array2<f64> {
264    let mut data = Array2::zeros((n_samples, 2));
265    let n_samples_per_moon = n_samples / 2;
266
267    // First moon
268    for i in 0..n_samples_per_moon {
269        let angle = std::f64::consts::PI * i as f64 / n_samples_per_moon as f64;
270        data[[i, 0]] = 0.1f64.mul_add(2.0f64.mul_add(thread_rng().gen::<f64>(), -1.0), angle.cos());
271        data[[i, 1]] = 0.1f64.mul_add(2.0f64.mul_add(thread_rng().gen::<f64>(), -1.0), angle.sin());
272    }
273
274    // Second moon (shifted and flipped)
275    for i in 0..n_samples_per_moon {
276        let idx = n_samples_per_moon + i;
277        let angle = std::f64::consts::PI * i as f64 / n_samples_per_moon as f64;
278        data[[idx, 0]] = 0.1f64.mul_add(
279            2.0f64.mul_add(thread_rng().gen::<f64>(), -1.0),
280            1.0 - angle.cos(),
281        );
282        data[[idx, 1]] = 0.1f64.mul_add(
283            2.0f64.mul_add(thread_rng().gen::<f64>(), -1.0),
284            0.5 - angle.sin(),
285        );
286    }
287
288    data
289}
290
291/// Advanced diffusion techniques demonstration
292fn advanced_diffusion_demo() -> Result<()> {
293    println!("\n6. Advanced Diffusion Techniques:");
294
295    // Conditional generation
296    println!("\n   a) Conditional Generation:");
297    let model = QuantumDiffusionModel::new(4, 4, 50, NoiseSchedule::Cosine { s: 0.008 })?;
298    let condition = Array1::from_vec(vec![0.5, -0.5]);
299    let conditional_samples = model.conditional_generate(&condition, 5)?;
300
301    println!(
302        "   Generated {} conditional samples",
303        conditional_samples.nrows()
304    );
305    println!("   Condition: [{:.3}, {:.3}]", condition[0], condition[1]);
306
307    // Variational diffusion
308    println!("\n   b) Variational Diffusion Model:");
309    let vdm = QuantumVariationalDiffusion::new(
310        4, // data_dim
311        2, // latent_dim
312        4, // num_qubits
313    )?;
314
315    let x = Array1::from_vec(vec![0.1, 0.2, 0.3, 0.4]);
316    let (mean, log_var) = vdm.encode(&x)?;
317
318    println!("   Encoded data to latent space:");
319    println!("   - Input: {:?}", x.as_slice().unwrap());
320    println!("   - Latent mean: [{:.3}, {:.3}]", mean[0], mean[1]);
321    println!(
322        "   - Latent log_var: [{:.3}, {:.3}]",
323        log_var[0], log_var[1]
324    );
325
326    Ok(())
327}