quantrs2_anneal/applications/
telecommunications.rs

1//! Telecommunications Industry Optimization
2//!
3//! This module provides optimization solutions for the telecommunications industry,
4//! including network topology optimization, traffic routing, infrastructure placement,
5//! spectrum allocation, and quality of service 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/// Network Topology Optimization Problem
18#[derive(Debug, Clone)]
19pub struct NetworkTopologyOptimization {
20    /// Number of network nodes
21    pub num_nodes: usize,
22    /// Potential connections between nodes
23    pub potential_connections: Vec<(usize, usize)>,
24    /// Connection costs
25    pub connection_costs: Vec<f64>,
26    /// Connection capacities
27    pub connection_capacities: Vec<f64>,
28    /// Traffic demand matrix \[source\]\[destination\]
29    pub traffic_demands: Vec<Vec<f64>>,
30    /// Reliability requirements for connections
31    pub reliability_requirements: Vec<f64>,
32    /// Maximum network latency allowed
33    pub max_latency: f64,
34    /// Redundancy requirements
35    pub redundancy_level: usize,
36    /// Quality of Service constraints
37    pub qos_constraints: Vec<IndustryConstraint>,
38}
39
40impl NetworkTopologyOptimization {
41    /// Create a new network topology optimization problem
42    pub fn new(
43        num_nodes: usize,
44        potential_connections: Vec<(usize, usize)>,
45        connection_costs: Vec<f64>,
46        traffic_demands: Vec<Vec<f64>>,
47    ) -> ApplicationResult<Self> {
48        if connection_costs.len() != potential_connections.len() {
49            return Err(ApplicationError::InvalidConfiguration(
50                "Connection costs must match number of potential connections".to_string(),
51            ));
52        }
53
54        if traffic_demands.len() != num_nodes {
55            return Err(ApplicationError::InvalidConfiguration(
56                "Traffic demand matrix dimension mismatch".to_string(),
57            ));
58        }
59
60        for row in &traffic_demands {
61            if row.len() != num_nodes {
62                return Err(ApplicationError::InvalidConfiguration(
63                    "Traffic demand matrix is not square".to_string(),
64                ));
65            }
66        }
67
68        Ok(Self {
69            num_nodes,
70            potential_connections: potential_connections.clone(),
71            connection_costs,
72            connection_capacities: vec![100.0; potential_connections.len()], // Default capacity
73            traffic_demands,
74            reliability_requirements: vec![0.99; potential_connections.len()], // 99% reliability
75            max_latency: 100.0,                                                // 100ms max latency
76            redundancy_level: 2,                                               // Dual redundancy
77            qos_constraints: Vec::new(),
78        })
79    }
80
81    /// Set connection capacities
82    pub fn set_connection_capacities(&mut self, capacities: Vec<f64>) -> ApplicationResult<()> {
83        if capacities.len() != self.potential_connections.len() {
84            return Err(ApplicationError::InvalidConfiguration(
85                "Capacities must match number of potential connections".to_string(),
86            ));
87        }
88
89        self.connection_capacities = capacities;
90        Ok(())
91    }
92
93    /// Calculate network connectivity
94    #[must_use]
95    pub fn calculate_connectivity(&self, topology: &NetworkTopology) -> f64 {
96        let mut connectivity_matrix = vec![vec![false; self.num_nodes]; self.num_nodes];
97
98        // Mark direct connections
99        for (i, &active) in topology.active_connections.iter().enumerate() {
100            if active {
101                let (u, v) = self.potential_connections[i];
102                connectivity_matrix[u][v] = true;
103                connectivity_matrix[v][u] = true;
104            }
105        }
106
107        // Floyd-Warshall to find all-pairs connectivity
108        for k in 0..self.num_nodes {
109            for i in 0..self.num_nodes {
110                for j in 0..self.num_nodes {
111                    connectivity_matrix[i][j] |=
112                        connectivity_matrix[i][k] && connectivity_matrix[k][j];
113                }
114            }
115        }
116
117        // Calculate connectivity ratio
118        let mut connected_pairs = 0;
119        let total_pairs = self.num_nodes * (self.num_nodes - 1);
120
121        for i in 0..self.num_nodes {
122            for j in 0..self.num_nodes {
123                if i != j && connectivity_matrix[i][j] {
124                    connected_pairs += 1;
125                }
126            }
127        }
128
129        f64::from(connected_pairs) / total_pairs as f64
130    }
131
132    /// Calculate network latency
133    #[must_use]
134    pub fn calculate_network_latency(&self, topology: &NetworkTopology) -> f64 {
135        // Simplified latency calculation based on path lengths
136        let mut distance_matrix = vec![vec![f64::INFINITY; self.num_nodes]; self.num_nodes];
137
138        // Initialize distances
139        for i in 0..self.num_nodes {
140            distance_matrix[i][i] = 0.0;
141        }
142
143        for (i, &active) in topology.active_connections.iter().enumerate() {
144            if active {
145                let (u, v) = self.potential_connections[i];
146                let latency = 1.0; // Simplified: 1ms per hop
147                distance_matrix[u][v] = latency;
148                distance_matrix[v][u] = latency;
149            }
150        }
151
152        // Floyd-Warshall for shortest paths
153        for k in 0..self.num_nodes {
154            for i in 0..self.num_nodes {
155                for j in 0..self.num_nodes {
156                    if distance_matrix[i][k] + distance_matrix[k][j] < distance_matrix[i][j] {
157                        distance_matrix[i][j] = distance_matrix[i][k] + distance_matrix[k][j];
158                    }
159                }
160            }
161        }
162
163        // Average latency for all connected pairs
164        let mut total_latency = 0.0;
165        let mut connected_pairs = 0;
166
167        for i in 0..self.num_nodes {
168            for j in 0..self.num_nodes {
169                if i != j && distance_matrix[i][j] < f64::INFINITY {
170                    total_latency += distance_matrix[i][j];
171                    connected_pairs += 1;
172                }
173            }
174        }
175
176        if connected_pairs > 0 {
177            total_latency / f64::from(connected_pairs)
178        } else {
179            f64::INFINITY
180        }
181    }
182
183    /// Calculate total network cost
184    #[must_use]
185    pub fn calculate_total_cost(&self, topology: &NetworkTopology) -> f64 {
186        topology
187            .active_connections
188            .iter()
189            .enumerate()
190            .filter(|(_, &active)| active)
191            .map(|(i, _)| self.connection_costs[i])
192            .sum()
193    }
194}
195
196impl OptimizationProblem for NetworkTopologyOptimization {
197    type Solution = NetworkTopology;
198    type ObjectiveValue = f64;
199
200    fn description(&self) -> String {
201        format!(
202            "Network topology optimization with {} nodes and {} potential connections",
203            self.num_nodes,
204            self.potential_connections.len()
205        )
206    }
207
208    fn size_metrics(&self) -> HashMap<String, usize> {
209        let mut metrics = HashMap::new();
210        metrics.insert("num_nodes".to_string(), self.num_nodes);
211        metrics.insert(
212            "num_potential_connections".to_string(),
213            self.potential_connections.len(),
214        );
215        metrics.insert(
216            "num_qos_constraints".to_string(),
217            self.qos_constraints.len(),
218        );
219        metrics
220    }
221
222    fn validate(&self) -> ApplicationResult<()> {
223        if self.num_nodes < 2 {
224            return Err(ApplicationError::DataValidationError(
225                "At least 2 nodes required".to_string(),
226            ));
227        }
228
229        if self.potential_connections.is_empty() {
230            return Err(ApplicationError::DataValidationError(
231                "At least one potential connection required".to_string(),
232            ));
233        }
234
235        // Validate node indices in connections
236        for &(u, v) in &self.potential_connections {
237            if u >= self.num_nodes || v >= self.num_nodes {
238                return Err(ApplicationError::DataValidationError(
239                    "Connection references invalid node index".to_string(),
240                ));
241            }
242        }
243
244        // Check positive costs and capacities
245        for &cost in &self.connection_costs {
246            if cost < 0.0 {
247                return Err(ApplicationError::DataValidationError(
248                    "Connection costs must be non-negative".to_string(),
249                ));
250            }
251        }
252
253        for &capacity in &self.connection_capacities {
254            if capacity <= 0.0 {
255                return Err(ApplicationError::DataValidationError(
256                    "Connection capacities must be positive".to_string(),
257                ));
258            }
259        }
260
261        Ok(())
262    }
263
264    fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
265        let mut builder = QuboBuilder::new();
266        let num_connections = self.potential_connections.len();
267        let mut string_var_map = HashMap::new();
268
269        // Create string variable mapping
270        for i in 0..num_connections {
271            string_var_map.insert(format!("x_{i}"), i);
272        }
273
274        // Binary variables: x[i] = 1 if connection i is active
275        for i in 0..num_connections {
276            // Cost term
277            builder.add_bias(i, self.connection_costs[i]);
278
279            // Connectivity benefit (negative to encourage connections)
280            let connectivity_benefit = -50.0; // Encourage network connectivity
281            builder.add_bias(i, connectivity_benefit);
282        }
283
284        // Redundancy constraints: ensure multiple paths between critical nodes
285        let redundancy_penalty = 1000.0;
286        for node in 0..self.num_nodes {
287            let mut adjacent_connections = Vec::new();
288
289            for (conn_idx, &(u, v)) in self.potential_connections.iter().enumerate() {
290                if u == node || v == node {
291                    adjacent_connections.push(conn_idx);
292                }
293            }
294
295            // Penalty for having too few connections per node
296            if adjacent_connections.len() >= self.redundancy_level {
297                for i in 0..adjacent_connections.len() {
298                    for j in (i + 1)..adjacent_connections.len() {
299                        let var1 = adjacent_connections[i];
300                        let var2 = adjacent_connections[j];
301                        // Encourage having multiple connections per node
302                        builder.add_coupling(var1, var2, -redundancy_penalty / 2.0);
303                    }
304                }
305            }
306        }
307
308        // Traffic capacity constraints
309        let capacity_penalty = 500.0;
310        for (conn_idx, &(u, v)) in self.potential_connections.iter().enumerate() {
311            let traffic_demand = self.traffic_demands[u][v] + self.traffic_demands[v][u];
312            let capacity = self.connection_capacities[conn_idx];
313
314            if traffic_demand > capacity {
315                // Penalty for insufficient capacity
316                builder.add_bias(conn_idx, capacity_penalty * (traffic_demand - capacity));
317            }
318        }
319
320        Ok((builder.build(), string_var_map))
321    }
322
323    fn evaluate_solution(
324        &self,
325        solution: &Self::Solution,
326    ) -> ApplicationResult<Self::ObjectiveValue> {
327        let total_cost = self.calculate_total_cost(solution);
328        let connectivity = self.calculate_connectivity(solution);
329        let latency = self.calculate_network_latency(solution);
330
331        // Multi-objective: minimize cost and latency, maximize connectivity
332        let connectivity_weight = 100.0;
333        let latency_penalty = 10.0;
334
335        Ok(total_cost + latency_penalty * latency - connectivity_weight * connectivity)
336    }
337
338    fn is_feasible(&self, solution: &Self::Solution) -> bool {
339        // Check connectivity requirements
340        let connectivity = self.calculate_connectivity(solution);
341        if connectivity < 0.8 {
342            // At least 80% connectivity
343            return false;
344        }
345
346        // Check latency constraints
347        let latency = self.calculate_network_latency(solution);
348        if latency > self.max_latency {
349            return false;
350        }
351
352        // Check traffic capacity constraints
353        for (conn_idx, &active) in solution.active_connections.iter().enumerate() {
354            if active {
355                let (u, v) = self.potential_connections[conn_idx];
356                let traffic_demand = self.traffic_demands[u][v] + self.traffic_demands[v][u];
357                let capacity = self.connection_capacities[conn_idx];
358
359                if traffic_demand > capacity * 1.1 {
360                    // Allow 10% overutilization
361                    return false;
362                }
363            }
364        }
365
366        true
367    }
368}
369
370/// Network Topology Solution
371#[derive(Debug, Clone)]
372pub struct NetworkTopology {
373    /// Which connections are active
374    pub active_connections: Vec<bool>,
375    /// Total network cost
376    pub total_cost: f64,
377    /// Network connectivity ratio
378    pub connectivity: f64,
379    /// Average network latency
380    pub average_latency: f64,
381    /// Network performance metrics
382    pub performance_metrics: TelecomMetrics,
383}
384
385/// Telecommunications performance metrics
386#[derive(Debug, Clone)]
387pub struct TelecomMetrics {
388    /// Network throughput (Gbps)
389    pub throughput: f64,
390    /// Packet loss rate
391    pub packet_loss_rate: f64,
392    /// Jitter (ms)
393    pub jitter: f64,
394    /// Network availability
395    pub availability: f64,
396    /// Mean time between failures (hours)
397    pub mtbf: f64,
398    /// Coverage area (km²)
399    pub coverage_area: f64,
400}
401
402impl IndustrySolution for NetworkTopology {
403    type Problem = NetworkTopologyOptimization;
404
405    fn from_binary(problem: &Self::Problem, binary_solution: &[i8]) -> ApplicationResult<Self> {
406        let num_connections = problem.potential_connections.len();
407        let mut active_connections = vec![false; num_connections];
408
409        // Decode binary solution
410        for i in 0..num_connections.min(binary_solution.len()) {
411            active_connections[i] = binary_solution[i] == 1;
412        }
413
414        let total_cost = problem.calculate_total_cost(&Self {
415            active_connections: active_connections.clone(),
416            total_cost: 0.0,
417            connectivity: 0.0,
418            average_latency: 0.0,
419            performance_metrics: TelecomMetrics {
420                throughput: 0.0,
421                packet_loss_rate: 0.0,
422                jitter: 0.0,
423                availability: 0.0,
424                mtbf: 0.0,
425                coverage_area: 0.0,
426            },
427        });
428
429        let connectivity = problem.calculate_connectivity(&Self {
430            active_connections: active_connections.clone(),
431            total_cost: 0.0,
432            connectivity: 0.0,
433            average_latency: 0.0,
434            performance_metrics: TelecomMetrics {
435                throughput: 0.0,
436                packet_loss_rate: 0.0,
437                jitter: 0.0,
438                availability: 0.0,
439                mtbf: 0.0,
440                coverage_area: 0.0,
441            },
442        });
443
444        let average_latency = problem.calculate_network_latency(&Self {
445            active_connections: active_connections.clone(),
446            total_cost: 0.0,
447            connectivity: 0.0,
448            average_latency: 0.0,
449            performance_metrics: TelecomMetrics {
450                throughput: 0.0,
451                packet_loss_rate: 0.0,
452                jitter: 0.0,
453                availability: 0.0,
454                mtbf: 0.0,
455                coverage_area: 0.0,
456            },
457        });
458
459        // Calculate performance metrics
460        let num_active = active_connections.iter().filter(|&&x| x).count();
461        let performance_metrics = TelecomMetrics {
462            throughput: num_active as f64 * 10.0, // 10 Gbps per connection
463            packet_loss_rate: 0.001,              // 0.1% packet loss
464            jitter: 2.0,                          // 2ms jitter
465            availability: 0.999,                  // 99.9% availability
466            mtbf: 8760.0,                         // 1 year MTBF
467            coverage_area: num_active as f64 * 100.0, // 100 km² per connection
468        };
469
470        Ok(Self {
471            active_connections,
472            total_cost,
473            connectivity,
474            average_latency,
475            performance_metrics,
476        })
477    }
478
479    fn summary(&self) -> HashMap<String, String> {
480        let mut summary = HashMap::new();
481        summary.insert("type".to_string(), "Network Topology".to_string());
482        summary.insert("total_cost".to_string(), format!("${:.2}", self.total_cost));
483        summary.insert(
484            "connectivity".to_string(),
485            format!("{:.1}%", self.connectivity * 100.0),
486        );
487        summary.insert(
488            "average_latency".to_string(),
489            format!("{:.1} ms", self.average_latency),
490        );
491        summary.insert(
492            "throughput".to_string(),
493            format!("{:.1} Gbps", self.performance_metrics.throughput),
494        );
495        summary.insert(
496            "availability".to_string(),
497            format!("{:.3}%", self.performance_metrics.availability * 100.0),
498        );
499
500        let num_active = self.active_connections.iter().filter(|&&x| x).count();
501        summary.insert("active_connections".to_string(), num_active.to_string());
502
503        summary
504    }
505
506    fn metrics(&self) -> HashMap<String, f64> {
507        let mut metrics = HashMap::new();
508        metrics.insert("total_cost".to_string(), self.total_cost);
509        metrics.insert("connectivity".to_string(), self.connectivity);
510        metrics.insert("average_latency".to_string(), self.average_latency);
511        metrics.insert(
512            "throughput".to_string(),
513            self.performance_metrics.throughput,
514        );
515        metrics.insert(
516            "packet_loss_rate".to_string(),
517            self.performance_metrics.packet_loss_rate,
518        );
519        metrics.insert("jitter".to_string(), self.performance_metrics.jitter);
520        metrics.insert(
521            "availability".to_string(),
522            self.performance_metrics.availability,
523        );
524        metrics.insert("mtbf".to_string(), self.performance_metrics.mtbf);
525        metrics.insert(
526            "coverage_area".to_string(),
527            self.performance_metrics.coverage_area,
528        );
529
530        let num_active = self.active_connections.iter().filter(|&&x| x).count();
531        metrics.insert("active_connections".to_string(), num_active as f64);
532
533        metrics
534    }
535
536    fn export_format(&self) -> ApplicationResult<String> {
537        let mut output = String::new();
538        output.push_str("# Network Topology Optimization Report\n\n");
539
540        output.push_str("## Network Summary\n");
541        writeln!(output, "Total Cost: ${:.2}", self.total_cost)
542            .expect("writing to String is infallible");
543        writeln!(output, "Connectivity: {:.1}%", self.connectivity * 100.0)
544            .expect("writing to String is infallible");
545        writeln!(output, "Average Latency: {:.1} ms", self.average_latency)
546            .expect("writing to String is infallible");
547
548        output.push_str("\n## Performance Metrics\n");
549        write!(
550            output,
551            "Throughput: {:.1} Gbps\n",
552            self.performance_metrics.throughput
553        )
554        .expect("writing to String is infallible");
555        write!(
556            output,
557            "Packet Loss Rate: {:.3}%\n",
558            self.performance_metrics.packet_loss_rate * 100.0
559        )
560        .expect("writing to String is infallible");
561        write!(
562            output,
563            "Jitter: {:.1} ms\n",
564            self.performance_metrics.jitter
565        )
566        .expect("writing to String is infallible");
567        write!(
568            output,
569            "Availability: {:.3}%\n",
570            self.performance_metrics.availability * 100.0
571        )
572        .expect("writing to String is infallible");
573        writeln!(output, "MTBF: {:.1} hours", self.performance_metrics.mtbf)
574            .expect("writing to String is infallible");
575        write!(
576            output,
577            "Coverage Area: {:.1} km²\n",
578            self.performance_metrics.coverage_area
579        )
580        .expect("writing to String is infallible");
581
582        output.push_str("\n## Active Connections\n");
583        for (i, &active) in self.active_connections.iter().enumerate() {
584            if active {
585                writeln!(output, "Connection {}: Active", i + 1)
586                    .expect("writing to String is infallible");
587            }
588        }
589
590        Ok(output)
591    }
592}
593
594/// Spectrum Allocation Optimization Problem
595#[derive(Debug, Clone)]
596pub struct SpectrumAllocation {
597    /// Number of frequency bands
598    pub num_bands: usize,
599    /// Number of geographic regions
600    pub num_regions: usize,
601    /// Interference matrix between bands
602    pub interference_matrix: Vec<Vec<f64>>,
603    /// Service demand by region and band
604    pub service_demands: Vec<Vec<f64>>,
605    /// Band availability by region
606    pub band_availability: Vec<Vec<bool>>,
607    /// Regulatory constraints
608    pub regulatory_constraints: Vec<IndustryConstraint>,
609    /// Quality of Service requirements
610    pub qos_requirements: Vec<f64>,
611}
612
613impl SpectrumAllocation {
614    /// Create new spectrum allocation problem
615    pub fn new(
616        num_bands: usize,
617        num_regions: usize,
618        interference_matrix: Vec<Vec<f64>>,
619        service_demands: Vec<Vec<f64>>,
620    ) -> ApplicationResult<Self> {
621        if interference_matrix.len() != num_bands {
622            return Err(ApplicationError::InvalidConfiguration(
623                "Interference matrix dimension mismatch".to_string(),
624            ));
625        }
626
627        Ok(Self {
628            num_bands,
629            num_regions,
630            interference_matrix,
631            service_demands,
632            band_availability: vec![vec![true; num_bands]; num_regions],
633            regulatory_constraints: Vec::new(),
634            qos_requirements: vec![0.95; num_regions],
635        })
636    }
637
638    /// Calculate interference for a given allocation
639    #[must_use]
640    pub fn calculate_interference(&self, allocation: &SpectrumSolution) -> f64 {
641        let mut total_interference = 0.0;
642
643        for region in 0..self.num_regions {
644            for band1 in 0..self.num_bands {
645                for band2 in 0..self.num_bands {
646                    if band1 != band2
647                        && allocation.band_assignments[region][band1]
648                        && allocation.band_assignments[region][band2]
649                    {
650                        total_interference += self.interference_matrix[band1][band2];
651                    }
652                }
653            }
654        }
655
656        total_interference
657    }
658}
659
660/// Spectrum Allocation Solution
661#[derive(Debug, Clone)]
662pub struct SpectrumSolution {
663    /// Band assignments \[region\]\[band\] = assigned
664    pub band_assignments: Vec<Vec<bool>>,
665    /// Total interference
666    pub total_interference: f64,
667    /// Service coverage achieved
668    pub service_coverage: Vec<f64>,
669    /// Spectrum utilization efficiency
670    pub spectrum_efficiency: f64,
671}
672
673/// Binary wrapper for Network Topology Optimization that works with binary solutions
674#[derive(Debug, Clone)]
675pub struct BinaryNetworkTopologyOptimization {
676    inner: NetworkTopologyOptimization,
677}
678
679impl BinaryNetworkTopologyOptimization {
680    #[must_use]
681    pub const fn new(inner: NetworkTopologyOptimization) -> Self {
682        Self { inner }
683    }
684}
685
686impl OptimizationProblem for BinaryNetworkTopologyOptimization {
687    type Solution = Vec<i8>;
688    type ObjectiveValue = f64;
689
690    fn description(&self) -> String {
691        self.inner.description()
692    }
693
694    fn size_metrics(&self) -> HashMap<String, usize> {
695        self.inner.size_metrics()
696    }
697
698    fn validate(&self) -> ApplicationResult<()> {
699        self.inner.validate()
700    }
701
702    fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
703        self.inner.to_qubo()
704    }
705
706    fn evaluate_solution(
707        &self,
708        solution: &Self::Solution,
709    ) -> ApplicationResult<Self::ObjectiveValue> {
710        // Convert binary solution to NetworkTopology for evaluation
711        let topology_solution = NetworkTopology::from_binary(&self.inner, solution)?;
712        self.inner.evaluate_solution(&topology_solution)
713    }
714
715    fn is_feasible(&self, solution: &Self::Solution) -> bool {
716        // Convert binary solution to NetworkTopology for feasibility check
717        if let Ok(topology_solution) = NetworkTopology::from_binary(&self.inner, solution) {
718            self.inner.is_feasible(&topology_solution)
719        } else {
720            false
721        }
722    }
723}
724
725/// Create benchmark telecommunications problems
726pub fn create_benchmark_problems(
727    size: usize,
728) -> ApplicationResult<Vec<Box<dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>>>>
729{
730    let mut problems = Vec::new();
731
732    // Problem 1: Small network topology optimization
733    let potential_connections = vec![(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)];
734    let connection_costs = vec![10.0, 15.0, 8.0, 12.0, 9.0];
735    let traffic_demands = vec![
736        vec![0.0, 5.0, 3.0, 2.0],
737        vec![5.0, 0.0, 4.0, 6.0],
738        vec![3.0, 4.0, 0.0, 3.0],
739        vec![2.0, 6.0, 3.0, 0.0],
740    ];
741
742    let network_problem = NetworkTopologyOptimization::new(
743        4, // nodes
744        potential_connections,
745        connection_costs,
746        traffic_demands,
747    )?;
748
749    problems.push(
750        Box::new(BinaryNetworkTopologyOptimization::new(network_problem))
751            as Box<dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>>,
752    );
753
754    // Problem 2: Larger network for more complex scenarios
755    if size >= 6 {
756        let mut large_connections = Vec::new();
757        let mut large_costs = Vec::new();
758
759        // Create a more connected network
760        for i in 0..size {
761            for j in (i + 1)..size {
762                large_connections.push((i, j));
763                large_costs.push(((i + j) as f64).mul_add(1.5, 5.0));
764            }
765        }
766
767        let large_demands = vec![vec![2.0; size]; size];
768
769        let large_network =
770            NetworkTopologyOptimization::new(size, large_connections, large_costs, large_demands)?;
771
772        problems.push(
773            Box::new(BinaryNetworkTopologyOptimization::new(large_network))
774                as Box<dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>>,
775        );
776    }
777
778    Ok(problems)
779}
780
781/// Solve network topology optimization using quantum annealing
782pub fn solve_network_topology(
783    problem: &NetworkTopologyOptimization,
784    params: Option<AnnealingParams>,
785) -> ApplicationResult<NetworkTopology> {
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 = 25_000;
796        p.num_repetitions = 40;
797        p.initial_temperature = 5.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 network topology
811    NetworkTopology::from_binary(problem, &result.best_spins)
812}
813
814#[cfg(test)]
815mod tests {
816    use super::*;
817
818    #[test]
819    fn test_network_topology_creation() {
820        let connections = vec![(0, 1), (1, 2), (0, 2)];
821        let costs = vec![10.0, 15.0, 12.0];
822        let demands = vec![
823            vec![0.0, 5.0, 3.0],
824            vec![5.0, 0.0, 4.0],
825            vec![3.0, 4.0, 0.0],
826        ];
827
828        let network = NetworkTopologyOptimization::new(3, connections, costs, demands)
829            .expect("failed to create network topology in test");
830        assert_eq!(network.num_nodes, 3);
831        assert_eq!(network.potential_connections.len(), 3);
832    }
833
834    #[test]
835    fn test_network_connectivity_calculation() {
836        let connections = vec![(0, 1), (1, 2)];
837        let costs = vec![10.0, 15.0];
838        let demands = vec![
839            vec![0.0, 1.0, 0.0],
840            vec![1.0, 0.0, 1.0],
841            vec![0.0, 1.0, 0.0],
842        ];
843
844        let network = NetworkTopologyOptimization::new(3, connections, costs, demands)
845            .expect("failed to create network topology in test");
846
847        let topology = NetworkTopology {
848            active_connections: vec![true, true],
849            total_cost: 25.0,
850            connectivity: 0.0,
851            average_latency: 0.0,
852            performance_metrics: TelecomMetrics {
853                throughput: 0.0,
854                packet_loss_rate: 0.0,
855                jitter: 0.0,
856                availability: 0.0,
857                mtbf: 0.0,
858                coverage_area: 0.0,
859            },
860        };
861
862        let connectivity = network.calculate_connectivity(&topology);
863        assert_eq!(connectivity, 1.0); // All nodes connected
864    }
865
866    #[test]
867    fn test_network_cost_calculation() {
868        let connections = vec![(0, 1), (1, 2)];
869        let costs = vec![10.0, 15.0];
870        let demands = vec![vec![0.0; 3]; 3];
871
872        let network = NetworkTopologyOptimization::new(3, connections, costs, demands)
873            .expect("failed to create network topology in test");
874
875        let topology = NetworkTopology {
876            active_connections: vec![true, false],
877            total_cost: 0.0,
878            connectivity: 0.0,
879            average_latency: 0.0,
880            performance_metrics: TelecomMetrics {
881                throughput: 0.0,
882                packet_loss_rate: 0.0,
883                jitter: 0.0,
884                availability: 0.0,
885                mtbf: 0.0,
886                coverage_area: 0.0,
887            },
888        };
889
890        let cost = network.calculate_total_cost(&topology);
891        assert_eq!(cost, 10.0);
892    }
893
894    #[test]
895    fn test_spectrum_allocation_creation() {
896        let interference_matrix = vec![
897            vec![0.0, 0.3, 0.1],
898            vec![0.3, 0.0, 0.4],
899            vec![0.1, 0.4, 0.0],
900        ];
901        let demands = vec![vec![1.0, 2.0, 3.0], vec![2.0, 1.0, 2.0]];
902
903        let spectrum = SpectrumAllocation::new(3, 2, interference_matrix, demands)
904            .expect("failed to create spectrum allocation in test");
905        assert_eq!(spectrum.num_bands, 3);
906        assert_eq!(spectrum.num_regions, 2);
907    }
908
909    #[test]
910    fn test_network_validation() {
911        let connections = vec![(0, 1), (1, 2)];
912        let costs = vec![10.0, 15.0];
913        let demands = vec![vec![0.0; 3]; 3];
914
915        let network = NetworkTopologyOptimization::new(3, connections, costs, demands.clone())
916            .expect("failed to create network topology in test");
917        assert!(network.validate().is_ok());
918
919        // Test invalid network (node index out of bounds)
920        let invalid_connections = vec![(0, 1), (1, 5)]; // Node 5 doesn't exist
921        let invalid_costs = vec![10.0, 15.0];
922        let invalid_network =
923            NetworkTopologyOptimization::new(3, invalid_connections, invalid_costs, demands);
924        assert!(invalid_network.is_ok()); // Created successfully
925        assert!(invalid_network
926            .expect("failed to create invalid network in test")
927            .validate()
928            .is_err()); // But validation fails
929    }
930
931    #[test]
932    fn test_benchmark_problems() {
933        let problems =
934            create_benchmark_problems(6).expect("failed to create benchmark problems in test");
935        assert_eq!(problems.len(), 2);
936
937        for problem in &problems {
938            assert!(problem.validate().is_ok());
939        }
940    }
941}