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