quantrs2_anneal/applications/
energy.rs

1//! Energy Industry Optimization
2//!
3//! This module provides optimization solutions for the energy industry,
4//! including grid optimization, renewable energy management, load balancing,
5//! and energy trading optimization.
6
7use super::{
8    ApplicationError, ApplicationResult, IndustryConstraint, IndustryObjective, IndustrySolution,
9    OptimizationProblem,
10};
11use crate::ising::IsingModel;
12use crate::qubo::{QuboBuilder, QuboFormulation};
13use crate::simulator::{AnnealingParams, ClassicalAnnealingSimulator};
14use std::collections::HashMap;
15
16use std::fmt::Write;
17/// Smart Grid Optimization Problem
18#[derive(Debug, Clone)]
19pub struct SmartGridOptimization {
20    /// Number of generators
21    pub num_generators: usize,
22    /// Number of demand nodes
23    pub num_demand_nodes: usize,
24    /// Generator capacities (MW)
25    pub generator_capacities: Vec<f64>,
26    /// Generator marginal costs ($/`MWh`)
27    pub generator_costs: Vec<f64>,
28    /// Generator ramp rates (MW/hour)
29    pub generator_ramp_rates: Vec<f64>,
30    /// Demand forecasts by node and time period
31    pub demand_forecasts: Vec<Vec<f64>>,
32    /// Transmission line capacities
33    pub transmission_capacities: HashMap<(usize, usize), f64>,
34    /// Transmission line costs
35    pub transmission_costs: HashMap<(usize, usize), f64>,
36    /// Time periods to optimize
37    pub num_time_periods: usize,
38    /// Renewable energy availability
39    pub renewable_availability: Vec<Vec<f64>>,
40    /// Energy storage systems
41    pub storage_systems: Vec<EnergyStorageSystem>,
42    /// Grid constraints
43    pub grid_constraints: Vec<IndustryConstraint>,
44}
45
46/// Energy Storage System
47#[derive(Debug, Clone)]
48pub struct EnergyStorageSystem {
49    /// Storage capacity (`MWh`)
50    pub capacity: f64,
51    /// Maximum charge/discharge rate (MW)
52    pub max_power: f64,
53    /// Round-trip efficiency
54    pub efficiency: f64,
55    /// Location (node index)
56    pub location: usize,
57    /// Operating cost ($/`MWh`)
58    pub operating_cost: f64,
59}
60
61impl SmartGridOptimization {
62    /// Create a new smart grid optimization problem
63    pub fn new(
64        num_generators: usize,
65        num_demand_nodes: usize,
66        generator_capacities: Vec<f64>,
67        generator_costs: Vec<f64>,
68        demand_forecasts: Vec<Vec<f64>>,
69        num_time_periods: usize,
70    ) -> ApplicationResult<Self> {
71        if generator_capacities.len() != num_generators {
72            return Err(ApplicationError::InvalidConfiguration(
73                "Generator capacities must match number of generators".to_string(),
74            ));
75        }
76
77        if generator_costs.len() != num_generators {
78            return Err(ApplicationError::InvalidConfiguration(
79                "Generator costs must match number of generators".to_string(),
80            ));
81        }
82
83        if demand_forecasts.len() != num_demand_nodes {
84            return Err(ApplicationError::InvalidConfiguration(
85                "Demand forecasts must match number of demand nodes".to_string(),
86            ));
87        }
88
89        Ok(Self {
90            num_generators,
91            num_demand_nodes,
92            generator_capacities,
93            generator_costs,
94            generator_ramp_rates: vec![50.0; num_generators], // Default ramp rate
95            demand_forecasts,
96            transmission_capacities: HashMap::new(),
97            transmission_costs: HashMap::new(),
98            num_time_periods,
99            renewable_availability: vec![vec![0.0; num_time_periods]; num_generators],
100            storage_systems: Vec::new(),
101            grid_constraints: Vec::new(),
102        })
103    }
104
105    /// Add transmission line
106    pub fn add_transmission_line(
107        &mut self,
108        from_node: usize,
109        to_node: usize,
110        capacity: f64,
111        cost: f64,
112    ) -> ApplicationResult<()> {
113        if from_node >= self.num_demand_nodes || to_node >= self.num_demand_nodes {
114            return Err(ApplicationError::InvalidConfiguration(
115                "Node indices out of bounds".to_string(),
116            ));
117        }
118
119        self.transmission_capacities
120            .insert((from_node, to_node), capacity);
121        self.transmission_costs.insert((from_node, to_node), cost);
122        Ok(())
123    }
124
125    /// Add energy storage system
126    pub fn add_storage_system(&mut self, storage: EnergyStorageSystem) -> ApplicationResult<()> {
127        if storage.location >= self.num_demand_nodes {
128            return Err(ApplicationError::InvalidConfiguration(
129                "Storage location out of bounds".to_string(),
130            ));
131        }
132
133        self.storage_systems.push(storage);
134        Ok(())
135    }
136
137    /// Set renewable availability profiles
138    pub fn set_renewable_availability(
139        &mut self,
140        availability: Vec<Vec<f64>>,
141    ) -> ApplicationResult<()> {
142        if availability.len() != self.num_generators {
143            return Err(ApplicationError::InvalidConfiguration(
144                "Renewable availability must match number of generators".to_string(),
145            ));
146        }
147
148        self.renewable_availability = availability;
149        Ok(())
150    }
151
152    /// Calculate total demand for time period
153    #[must_use]
154    pub fn calculate_total_demand(&self, time_period: usize) -> f64 {
155        if time_period >= self.num_time_periods {
156            return 0.0;
157        }
158
159        self.demand_forecasts
160            .iter()
161            .map(|node_demands| node_demands.get(time_period).unwrap_or(&0.0))
162            .sum()
163    }
164
165    /// Calculate generation cost
166    #[must_use]
167    pub fn calculate_generation_cost(&self, solution: &GridSolution) -> f64 {
168        let mut total_cost = 0.0;
169
170        for t in 0..self.num_time_periods {
171            for g in 0..self.num_generators {
172                let generation = solution.generation_schedule[g][t];
173                total_cost += generation * self.generator_costs[g];
174            }
175        }
176
177        total_cost
178    }
179
180    /// Calculate transmission cost
181    #[must_use]
182    pub fn calculate_transmission_cost(&self, solution: &GridSolution) -> f64 {
183        let mut total_cost = 0.0;
184
185        for t in 0..self.num_time_periods {
186            for ((from, to), &cost) in &self.transmission_costs {
187                if let Some(flow) = solution.transmission_flows.get(&(*from, *to)) {
188                    if let Some(time_flows) = flow.get(t) {
189                        total_cost += time_flows * cost;
190                    }
191                }
192            }
193        }
194
195        total_cost
196    }
197}
198
199impl OptimizationProblem for SmartGridOptimization {
200    type Solution = GridSolution;
201    type ObjectiveValue = f64;
202
203    fn description(&self) -> String {
204        format!(
205            "Smart Grid Optimization with {} generators, {} nodes, {} time periods",
206            self.num_generators, self.num_demand_nodes, self.num_time_periods
207        )
208    }
209
210    fn size_metrics(&self) -> HashMap<String, usize> {
211        let mut metrics = HashMap::new();
212        metrics.insert("num_generators".to_string(), self.num_generators);
213        metrics.insert("num_demand_nodes".to_string(), self.num_demand_nodes);
214        metrics.insert("num_time_periods".to_string(), self.num_time_periods);
215        metrics.insert(
216            "num_transmission_lines".to_string(),
217            self.transmission_capacities.len(),
218        );
219        metrics.insert(
220            "num_storage_systems".to_string(),
221            self.storage_systems.len(),
222        );
223        metrics
224    }
225
226    fn validate(&self) -> ApplicationResult<()> {
227        if self.num_generators == 0 {
228            return Err(ApplicationError::DataValidationError(
229                "At least one generator required".to_string(),
230            ));
231        }
232
233        if self.num_demand_nodes == 0 {
234            return Err(ApplicationError::DataValidationError(
235                "At least one demand node required".to_string(),
236            ));
237        }
238
239        if self.num_time_periods == 0 {
240            return Err(ApplicationError::DataValidationError(
241                "At least one time period required".to_string(),
242            ));
243        }
244
245        // Check that total generation capacity exceeds peak demand
246        let total_capacity: f64 = self.generator_capacities.iter().sum();
247        let peak_demand = (0..self.num_time_periods)
248            .map(|t| self.calculate_total_demand(t))
249            .fold(0.0, f64::max);
250
251        if total_capacity < peak_demand {
252            return Err(ApplicationError::DataValidationError(
253                "Insufficient generation capacity to meet peak demand".to_string(),
254            ));
255        }
256
257        Ok(())
258    }
259
260    fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
261        let mut builder = QuboBuilder::new();
262        let precision = 10; // Discretization for continuous variables
263
264        // Binary variables: g[gen][time][level] = 1 if generator gen at time time produces level
265        let mut var_counter = 0;
266        let mut gen_vars = HashMap::new();
267        let mut string_var_map = HashMap::new();
268
269        for g in 0..self.num_generators {
270            for t in 0..self.num_time_periods {
271                for level in 0..precision {
272                    let var_name = format!("g_{g}_{t}_{level}");
273                    gen_vars.insert((g, t, level), var_counter);
274                    string_var_map.insert(var_name, var_counter);
275                    var_counter += 1;
276                }
277            }
278        }
279
280        // Objective: minimize generation cost
281        for g in 0..self.num_generators {
282            for t in 0..self.num_time_periods {
283                for level in 0..precision {
284                    let generation =
285                        f64::from(level) * self.generator_capacities[g] / f64::from(precision);
286                    let cost = generation * self.generator_costs[g];
287                    let var_idx = gen_vars[&(g, t, level)];
288                    builder.add_bias(var_idx, cost);
289                }
290            }
291        }
292
293        // Constraint: exactly one generation level per generator per time
294        let constraint_penalty = 10_000.0;
295        for g in 0..self.num_generators {
296            for t in 0..self.num_time_periods {
297                let mut level_vars = Vec::new();
298                for level in 0..precision {
299                    level_vars.push(gen_vars[&(g, t, level)]);
300                }
301
302                // Add penalty for not selecting exactly one level
303                for &var1 in &level_vars {
304                    builder.add_bias(var1, -constraint_penalty);
305                    for &var2 in &level_vars {
306                        if var1 != var2 {
307                            builder.add_coupling(var1, var2, constraint_penalty);
308                        }
309                    }
310                }
311            }
312        }
313
314        // Constraint: supply meets demand
315        for t in 0..self.num_time_periods {
316            let total_demand = self.calculate_total_demand(t);
317            let mut supply_vars = Vec::new();
318            let mut supply_coeffs = Vec::new();
319
320            for g in 0..self.num_generators {
321                for level in 0..precision {
322                    let generation =
323                        f64::from(level) * self.generator_capacities[g] / f64::from(precision);
324                    supply_vars.push(gen_vars[&(g, t, level)]);
325                    supply_coeffs.push(generation);
326                }
327            }
328
329            // Penalty for supply-demand mismatch
330            let mismatch_penalty = 50_000.0;
331            for (i, &var1) in supply_vars.iter().enumerate() {
332                let coeff1 = supply_coeffs[i];
333                builder.add_bias(var1, -2.0 * mismatch_penalty * total_demand * coeff1);
334
335                for (j, &var2) in supply_vars.iter().enumerate() {
336                    let coeff2 = supply_coeffs[j];
337                    builder.add_coupling(var1, var2, mismatch_penalty * coeff1 * coeff2);
338                }
339            }
340        }
341
342        Ok((builder.build(), string_var_map))
343    }
344
345    fn evaluate_solution(
346        &self,
347        solution: &Self::Solution,
348    ) -> ApplicationResult<Self::ObjectiveValue> {
349        let generation_cost = self.calculate_generation_cost(solution);
350        let transmission_cost = self.calculate_transmission_cost(solution);
351
352        Ok(generation_cost + transmission_cost)
353    }
354
355    fn is_feasible(&self, solution: &Self::Solution) -> bool {
356        // Check supply-demand balance
357        for t in 0..self.num_time_periods {
358            let total_generation: f64 = solution
359                .generation_schedule
360                .iter()
361                .map(|gen_schedule| gen_schedule.get(t).unwrap_or(&0.0))
362                .sum();
363            let total_demand = self.calculate_total_demand(t);
364
365            if (total_generation - total_demand).abs() > 1e-6 {
366                return false;
367            }
368        }
369
370        // Check generator capacity constraints
371        for (g, gen_schedule) in solution.generation_schedule.iter().enumerate() {
372            for &generation in gen_schedule {
373                if generation > self.generator_capacities[g] {
374                    return false;
375                }
376            }
377        }
378
379        // Check transmission capacity constraints
380        for ((from, to), flows) in &solution.transmission_flows {
381            if let Some(&capacity) = self.transmission_capacities.get(&(*from, *to)) {
382                for &flow in flows {
383                    if flow > capacity {
384                        return false;
385                    }
386                }
387            }
388        }
389
390        true
391    }
392}
393
394/// Grid optimization solution
395#[derive(Debug, Clone)]
396pub struct GridSolution {
397    /// Generation schedule \[generator\]\[time\]
398    pub generation_schedule: Vec<Vec<f64>>,
399    /// Transmission flows \[line\]\[time\]
400    pub transmission_flows: HashMap<(usize, usize), Vec<f64>>,
401    /// Storage operations \[storage\]\[time\]
402    pub storage_operations: Vec<Vec<f64>>,
403    /// Total cost
404    pub total_cost: f64,
405    /// Grid performance metrics
406    pub grid_metrics: GridMetrics,
407}
408
409/// Grid performance metrics
410#[derive(Debug, Clone)]
411pub struct GridMetrics {
412    /// Load factor
413    pub load_factor: f64,
414    /// Peak demand (MW)
415    pub peak_demand: f64,
416    /// Total energy generated (`MWh`)
417    pub total_energy: f64,
418    /// Renewable penetration
419    pub renewable_penetration: f64,
420    /// Grid efficiency
421    pub efficiency: f64,
422    /// Reliability metrics
423    pub reliability_score: f64,
424}
425
426impl IndustrySolution for GridSolution {
427    type Problem = SmartGridOptimization;
428
429    fn from_binary(problem: &Self::Problem, binary_solution: &[i8]) -> ApplicationResult<Self> {
430        let precision = 10;
431        let mut generation_schedule =
432            vec![vec![0.0; problem.num_time_periods]; problem.num_generators];
433        let mut var_idx = 0;
434
435        // Decode generation schedule
436        for g in 0..problem.num_generators {
437            for t in 0..problem.num_time_periods {
438                for level in 0..precision {
439                    if var_idx < binary_solution.len() && binary_solution[var_idx] == 1 {
440                        generation_schedule[g][t] = f64::from(level)
441                            * problem.generator_capacities[g]
442                            / f64::from(precision);
443                        break;
444                    }
445                    var_idx += 1;
446                }
447            }
448        }
449
450        // Simple transmission flow calculation (would be more complex in practice)
451        let mut transmission_flows = HashMap::new();
452        for ((from, to), _) in &problem.transmission_capacities {
453            transmission_flows.insert((*from, *to), vec![0.0; problem.num_time_periods]);
454        }
455
456        // Storage operations (simplified)
457        let storage_operations =
458            vec![vec![0.0; problem.num_time_periods]; problem.storage_systems.len()];
459
460        // Calculate metrics
461        let total_energy: f64 = generation_schedule.iter().flat_map(|gen| gen.iter()).sum();
462
463        let peak_demand = (0..problem.num_time_periods)
464            .map(|t| problem.calculate_total_demand(t))
465            .fold(0.0, f64::max);
466
467        let average_demand = total_energy / problem.num_time_periods as f64;
468        let load_factor = if peak_demand > 0.0 {
469            average_demand / peak_demand
470        } else {
471            0.0
472        };
473
474        let grid_metrics = GridMetrics {
475            load_factor,
476            peak_demand,
477            total_energy,
478            renewable_penetration: 0.0, // Simplified
479            efficiency: 0.95,           // Simplified
480            reliability_score: 0.99,    // Simplified
481        };
482
483        let total_cost = problem.calculate_generation_cost(&Self {
484            generation_schedule: generation_schedule.clone(),
485            transmission_flows: transmission_flows.clone(),
486            storage_operations: storage_operations.clone(),
487            total_cost: 0.0,
488            grid_metrics: grid_metrics.clone(),
489        });
490
491        Ok(Self {
492            generation_schedule,
493            transmission_flows,
494            storage_operations,
495            total_cost,
496            grid_metrics,
497        })
498    }
499
500    fn summary(&self) -> HashMap<String, String> {
501        let mut summary = HashMap::new();
502        summary.insert("type".to_string(), "Smart Grid Optimization".to_string());
503        summary.insert("total_cost".to_string(), format!("${:.2}", self.total_cost));
504        summary.insert(
505            "peak_demand".to_string(),
506            format!("{:.1} MW", self.grid_metrics.peak_demand),
507        );
508        summary.insert(
509            "total_energy".to_string(),
510            format!("{:.1} MWh", self.grid_metrics.total_energy),
511        );
512        summary.insert(
513            "load_factor".to_string(),
514            format!("{:.1}%", self.grid_metrics.load_factor * 100.0),
515        );
516        summary.insert(
517            "efficiency".to_string(),
518            format!("{:.1}%", self.grid_metrics.efficiency * 100.0),
519        );
520        summary.insert(
521            "reliability".to_string(),
522            format!("{:.2}%", self.grid_metrics.reliability_score * 100.0),
523        );
524        summary
525    }
526
527    fn metrics(&self) -> HashMap<String, f64> {
528        let mut metrics = HashMap::new();
529        metrics.insert("total_cost".to_string(), self.total_cost);
530        metrics.insert("load_factor".to_string(), self.grid_metrics.load_factor);
531        metrics.insert("peak_demand".to_string(), self.grid_metrics.peak_demand);
532        metrics.insert("total_energy".to_string(), self.grid_metrics.total_energy);
533        metrics.insert(
534            "renewable_penetration".to_string(),
535            self.grid_metrics.renewable_penetration,
536        );
537        metrics.insert("efficiency".to_string(), self.grid_metrics.efficiency);
538        metrics.insert(
539            "reliability_score".to_string(),
540            self.grid_metrics.reliability_score,
541        );
542
543        // Calculate additional operational metrics
544        let num_active_generators = self
545            .generation_schedule
546            .iter()
547            .filter(|schedule| schedule.iter().any(|&gen| gen > 1e-6))
548            .count();
549        metrics.insert(
550            "active_generators".to_string(),
551            num_active_generators as f64,
552        );
553
554        let max_generation: f64 = self
555            .generation_schedule
556            .iter()
557            .flat_map(|schedule| schedule.iter())
558            .fold(0.0, |a, &b| a.max(b));
559        metrics.insert("max_generation".to_string(), max_generation);
560
561        metrics
562    }
563
564    fn export_format(&self) -> ApplicationResult<String> {
565        let mut output = String::new();
566        output.push_str("# Smart Grid Optimization Solution\n\n");
567
568        output.push_str("## Solution Summary\n");
569        let _ = writeln!(output, "Total Cost: ${:.2}", self.total_cost);
570        let _ = write!(
571            output,
572            "Peak Demand: {:.1} MW\n",
573            self.grid_metrics.peak_demand
574        );
575        let _ = write!(
576            output,
577            "Total Energy: {:.1} MWh\n",
578            self.grid_metrics.total_energy
579        );
580        let _ = write!(
581            output,
582            "Load Factor: {:.1}%\n",
583            self.grid_metrics.load_factor * 100.0
584        );
585        let _ = write!(
586            output,
587            "Grid Efficiency: {:.1}%\n",
588            self.grid_metrics.efficiency * 100.0
589        );
590
591        output.push_str("\n## Generation Schedule\n");
592        for (g, schedule) in self.generation_schedule.iter().enumerate() {
593            let _ = write!(output, "Generator {}: ", g + 1);
594            for (t, &generation) in schedule.iter().enumerate() {
595                if generation > 1e-6 {
596                    let _ = write!(output, "T{}: {:.1}MW ", t + 1, generation);
597                }
598            }
599            output.push('\n');
600        }
601
602        output.push_str("\n## Grid Performance Metrics\n");
603        for (key, value) in self.metrics() {
604            let _ = writeln!(output, "{key}: {value:.3}");
605        }
606
607        Ok(output)
608    }
609}
610
611/// Renewable Energy Integration Problem
612#[derive(Debug, Clone)]
613pub struct RenewableEnergyIntegration {
614    /// Solar farm locations and capacities
615    pub solar_farms: Vec<SolarFarm>,
616    /// Wind farm locations and capacities
617    pub wind_farms: Vec<WindFarm>,
618    /// Battery storage systems
619    pub battery_systems: Vec<BatterySystem>,
620    /// Demand profiles
621    pub demand_profile: Vec<f64>,
622    /// Grid connection costs
623    pub connection_costs: Vec<f64>,
624    /// Curtailment penalty
625    pub curtailment_penalty: f64,
626}
627
628/// Solar farm specification
629#[derive(Debug, Clone)]
630pub struct SolarFarm {
631    /// Capacity (MW)
632    pub capacity: f64,
633    /// Solar irradiance profile
634    pub irradiance_profile: Vec<f64>,
635    /// Efficiency
636    pub efficiency: f64,
637    /// Installation cost ($/MW)
638    pub installation_cost: f64,
639}
640
641/// Wind farm specification
642#[derive(Debug, Clone)]
643pub struct WindFarm {
644    /// Capacity (MW)
645    pub capacity: f64,
646    /// Wind speed profile
647    pub wind_profile: Vec<f64>,
648    /// Power curve coefficients
649    pub power_curve: Vec<f64>,
650    /// Installation cost ($/MW)
651    pub installation_cost: f64,
652}
653
654/// Battery energy storage system
655#[derive(Debug, Clone)]
656pub struct BatterySystem {
657    /// Energy capacity (`MWh`)
658    pub energy_capacity: f64,
659    /// Power capacity (MW)
660    pub power_capacity: f64,
661    /// Round-trip efficiency
662    pub efficiency: f64,
663    /// Installation cost ($/`MWh`)
664    pub installation_cost: f64,
665    /// Cycle life
666    pub cycle_life: usize,
667}
668
669/// Binary wrapper for Smart Grid Optimization that works with binary solutions
670#[derive(Debug, Clone)]
671pub struct BinarySmartGridOptimization {
672    inner: SmartGridOptimization,
673}
674
675impl BinarySmartGridOptimization {
676    #[must_use]
677    pub const fn new(inner: SmartGridOptimization) -> Self {
678        Self { inner }
679    }
680}
681
682impl OptimizationProblem for BinarySmartGridOptimization {
683    type Solution = Vec<i8>;
684    type ObjectiveValue = f64;
685
686    fn description(&self) -> String {
687        self.inner.description()
688    }
689
690    fn size_metrics(&self) -> HashMap<String, usize> {
691        self.inner.size_metrics()
692    }
693
694    fn validate(&self) -> ApplicationResult<()> {
695        self.inner.validate()
696    }
697
698    fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
699        self.inner.to_qubo()
700    }
701
702    fn evaluate_solution(
703        &self,
704        solution: &Self::Solution,
705    ) -> ApplicationResult<Self::ObjectiveValue> {
706        // Convert binary solution to GridSolution for evaluation
707        let grid_solution = GridSolution::from_binary(&self.inner, solution)?;
708        self.inner.evaluate_solution(&grid_solution)
709    }
710
711    fn is_feasible(&self, solution: &Self::Solution) -> bool {
712        // Convert binary solution to GridSolution for feasibility check
713        if let Ok(grid_solution) = GridSolution::from_binary(&self.inner, solution) {
714            self.inner.is_feasible(&grid_solution)
715        } else {
716            false
717        }
718    }
719}
720
721/// Create benchmark energy problems
722pub fn create_benchmark_problems(
723    size: usize,
724) -> ApplicationResult<Vec<Box<dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>>>>
725{
726    let mut problems = Vec::new();
727
728    // Problem 1: Small grid optimization
729    let generator_capacities = vec![100.0, 150.0, 200.0];
730    let generator_costs = vec![30.0, 25.0, 40.0]; // $/MWh
731    let demand_forecasts = vec![
732        vec![50.0, 80.0, 120.0, 100.0], // Node 1
733        vec![30.0, 60.0, 90.0, 70.0],   // Node 2
734    ];
735
736    let grid_problem = SmartGridOptimization::new(
737        3, // generators
738        2, // demand nodes
739        generator_capacities,
740        generator_costs,
741        demand_forecasts,
742        4, // time periods
743    )?;
744
745    problems.push(Box::new(BinarySmartGridOptimization::new(grid_problem))
746        as Box<
747            dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>,
748        >);
749
750    // Problem 2: Larger grid with storage
751    if size >= 5 {
752        let large_capacities = (0..size).map(|i| (i as f64).mul_add(20.0, 50.0)).collect();
753        let large_costs = (0..size).map(|i| (i as f64).mul_add(5.0, 20.0)).collect();
754        let large_demands = (0..3)
755            .map(|_| (0..6).map(|t| f64::from(t).mul_add(15.0, 30.0)).collect())
756            .collect();
757
758        let mut large_grid =
759            SmartGridOptimization::new(size, 3, large_capacities, large_costs, large_demands, 6)?;
760
761        // Add storage systems
762        for i in 0..2 {
763            large_grid.add_storage_system(EnergyStorageSystem {
764                capacity: 100.0,
765                max_power: 50.0,
766                efficiency: 0.90,
767                location: i,
768                operating_cost: 5.0,
769            })?;
770        }
771
772        problems.push(Box::new(BinarySmartGridOptimization::new(large_grid))
773            as Box<
774                dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>,
775            >);
776    }
777
778    Ok(problems)
779}
780
781/// Solve grid optimization using quantum annealing
782pub fn solve_grid_optimization(
783    problem: &SmartGridOptimization,
784    params: Option<AnnealingParams>,
785) -> ApplicationResult<GridSolution> {
786    // Convert to QUBO
787    let (qubo, _var_map) = problem.to_qubo()?;
788
789    // Convert to Ising
790    let ising = IsingModel::from_qubo(&qubo);
791
792    // Set up annealing parameters
793    let annealing_params = params.unwrap_or_else(|| {
794        let mut p = AnnealingParams::default();
795        p.num_sweeps = 20_000;
796        p.num_repetitions = 30;
797        p.initial_temperature = 4.0;
798        p.final_temperature = 0.001;
799        p
800    });
801
802    // Solve with classical annealing
803    let simulator = ClassicalAnnealingSimulator::new(annealing_params)
804        .map_err(|e| ApplicationError::OptimizationError(e.to_string()))?;
805
806    let result = simulator
807        .solve(&ising)
808        .map_err(|e| ApplicationError::OptimizationError(e.to_string()))?;
809
810    // Convert solution back to grid solution
811    GridSolution::from_binary(problem, &result.best_spins)
812}
813
814#[cfg(test)]
815mod tests {
816    use super::*;
817
818    #[test]
819    fn test_grid_optimization_creation() {
820        let generator_capacities = vec![100.0, 150.0];
821        let generator_costs = vec![30.0, 25.0];
822        let demand_forecasts = vec![vec![50.0, 80.0], vec![30.0, 60.0]];
823
824        let grid = SmartGridOptimization::new(
825            2,
826            2,
827            generator_capacities,
828            generator_costs,
829            demand_forecasts,
830            2,
831        )
832        .expect("failed to create smart grid in test");
833
834        assert_eq!(grid.num_generators, 2);
835        assert_eq!(grid.num_demand_nodes, 2);
836        assert_eq!(grid.num_time_periods, 2);
837    }
838
839    #[test]
840    fn test_transmission_line_addition() {
841        let mut grid = SmartGridOptimization::new(
842            2,
843            3,
844            vec![100.0, 150.0],
845            vec![30.0, 25.0],
846            vec![vec![50.0], vec![30.0], vec![20.0]],
847            1,
848        )
849        .expect("failed to create smart grid in test");
850
851        assert!(grid.add_transmission_line(0, 1, 100.0, 2.0).is_ok());
852        assert!(grid.add_transmission_line(1, 2, 150.0, 3.0).is_ok());
853        assert_eq!(grid.transmission_capacities.len(), 2);
854    }
855
856    #[test]
857    fn test_storage_system_addition() {
858        let mut grid = SmartGridOptimization::new(
859            2,
860            2,
861            vec![100.0, 150.0],
862            vec![30.0, 25.0],
863            vec![vec![50.0], vec![30.0]],
864            1,
865        )
866        .expect("failed to create smart grid in test");
867
868        let storage = EnergyStorageSystem {
869            capacity: 100.0,
870            max_power: 50.0,
871            efficiency: 0.90,
872            location: 0,
873            operating_cost: 5.0,
874        };
875
876        assert!(grid.add_storage_system(storage).is_ok());
877        assert_eq!(grid.storage_systems.len(), 1);
878    }
879
880    #[test]
881    fn test_demand_calculation() {
882        let grid = SmartGridOptimization::new(
883            2,
884            2,
885            vec![100.0, 150.0],
886            vec![30.0, 25.0],
887            vec![vec![50.0, 80.0], vec![30.0, 60.0]],
888            2,
889        )
890        .expect("failed to create smart grid in test");
891
892        assert_eq!(grid.calculate_total_demand(0), 80.0); // 50 + 30
893        assert_eq!(grid.calculate_total_demand(1), 140.0); // 80 + 60
894    }
895
896    #[test]
897    fn test_grid_validation() {
898        let grid = SmartGridOptimization::new(
899            2,
900            2,
901            vec![100.0, 150.0],
902            vec![30.0, 25.0],
903            vec![vec![50.0, 80.0], vec![30.0, 60.0]],
904            2,
905        )
906        .expect("failed to create smart grid in test");
907
908        assert!(grid.validate().is_ok());
909
910        // Test insufficient capacity
911        let insufficient_grid = SmartGridOptimization::new(
912            1,
913            2,
914            vec![50.0], // Not enough for peak demand of 140
915            vec![30.0],
916            vec![vec![50.0, 80.0], vec![30.0, 60.0]],
917            2,
918        )
919        .expect("failed to create insufficient grid in test");
920
921        assert!(insufficient_grid.validate().is_err());
922    }
923
924    #[test]
925    fn test_benchmark_problems() {
926        let problems =
927            create_benchmark_problems(5).expect("failed to create benchmark problems in test");
928        assert_eq!(problems.len(), 2);
929
930        for problem in &problems {
931            assert!(problem.validate().is_ok());
932            let metrics = problem.size_metrics();
933            assert!(metrics.contains_key("num_generators"));
934            assert!(metrics.contains_key("num_demand_nodes"));
935        }
936    }
937
938    #[test]
939    fn test_solar_farm() {
940        let solar = SolarFarm {
941            capacity: 100.0,
942            irradiance_profile: vec![0.0, 0.3, 0.8, 1.0, 0.9, 0.5, 0.0],
943            efficiency: 0.20,
944            installation_cost: 1000.0,
945        };
946
947        assert_eq!(solar.capacity, 100.0);
948        assert_eq!(solar.irradiance_profile.len(), 7);
949    }
950
951    #[test]
952    fn test_battery_system() {
953        let battery = BatterySystem {
954            energy_capacity: 50.0,
955            power_capacity: 25.0,
956            efficiency: 0.95,
957            installation_cost: 500.0,
958            cycle_life: 5000,
959        };
960
961        assert_eq!(battery.energy_capacity, 50.0);
962        assert_eq!(battery.efficiency, 0.95);
963    }
964}