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