1use 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#[derive(Debug, Clone)]
19pub struct NetworkTopologyOptimization {
20 pub num_nodes: usize,
22 pub potential_connections: Vec<(usize, usize)>,
24 pub connection_costs: Vec<f64>,
26 pub connection_capacities: Vec<f64>,
28 pub traffic_demands: Vec<Vec<f64>>,
30 pub reliability_requirements: Vec<f64>,
32 pub max_latency: f64,
34 pub redundancy_level: usize,
36 pub qos_constraints: Vec<IndustryConstraint>,
38}
39
40impl NetworkTopologyOptimization {
41 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()], traffic_demands,
74 reliability_requirements: vec![0.99; potential_connections.len()], max_latency: 100.0, redundancy_level: 2, qos_constraints: Vec::new(),
78 })
79 }
80
81 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 #[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 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 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 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 #[must_use]
134 pub fn calculate_network_latency(&self, topology: &NetworkTopology) -> f64 {
135 let mut distance_matrix = vec![vec![f64::INFINITY; self.num_nodes]; self.num_nodes];
137
138 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; distance_matrix[u][v] = latency;
148 distance_matrix[v][u] = latency;
149 }
150 }
151
152 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 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 #[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 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 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 for i in 0..num_connections {
271 string_var_map.insert(format!("x_{i}"), i);
272 }
273
274 for i in 0..num_connections {
276 builder.add_bias(i, self.connection_costs[i]);
278
279 let connectivity_benefit = -50.0; builder.add_bias(i, connectivity_benefit);
282 }
283
284 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 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 builder.add_coupling(var1, var2, -redundancy_penalty / 2.0);
303 }
304 }
305 }
306 }
307
308 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 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 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 let connectivity = self.calculate_connectivity(solution);
341 if connectivity < 0.8 {
342 return false;
344 }
345
346 let latency = self.calculate_network_latency(solution);
348 if latency > self.max_latency {
349 return false;
350 }
351
352 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 return false;
362 }
363 }
364 }
365
366 true
367 }
368}
369
370#[derive(Debug, Clone)]
372pub struct NetworkTopology {
373 pub active_connections: Vec<bool>,
375 pub total_cost: f64,
377 pub connectivity: f64,
379 pub average_latency: f64,
381 pub performance_metrics: TelecomMetrics,
383}
384
385#[derive(Debug, Clone)]
387pub struct TelecomMetrics {
388 pub throughput: f64,
390 pub packet_loss_rate: f64,
392 pub jitter: f64,
394 pub availability: f64,
396 pub mtbf: f64,
398 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 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 let num_active = active_connections.iter().filter(|&&x| x).count();
461 let performance_metrics = TelecomMetrics {
462 throughput: num_active as f64 * 10.0, packet_loss_rate: 0.001, jitter: 2.0, availability: 0.999, mtbf: 8760.0, coverage_area: num_active as f64 * 100.0, };
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#[derive(Debug, Clone)]
596pub struct SpectrumAllocation {
597 pub num_bands: usize,
599 pub num_regions: usize,
601 pub interference_matrix: Vec<Vec<f64>>,
603 pub service_demands: Vec<Vec<f64>>,
605 pub band_availability: Vec<Vec<bool>>,
607 pub regulatory_constraints: Vec<IndustryConstraint>,
609 pub qos_requirements: Vec<f64>,
611}
612
613impl SpectrumAllocation {
614 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 #[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#[derive(Debug, Clone)]
662pub struct SpectrumSolution {
663 pub band_assignments: Vec<Vec<bool>>,
665 pub total_interference: f64,
667 pub service_coverage: Vec<f64>,
669 pub spectrum_efficiency: f64,
671}
672
673#[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 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 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
725pub 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 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, 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 if size >= 6 {
756 let mut large_connections = Vec::new();
757 let mut large_costs = Vec::new();
758
759 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
781pub fn solve_network_topology(
783 problem: &NetworkTopologyOptimization,
784 params: Option<AnnealingParams>,
785) -> ApplicationResult<NetworkTopology> {
786 let (qubo, _var_map) = problem.to_qubo()?;
788
789 let ising = IsingModel::from_qubo(&qubo);
791
792 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 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 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); }
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 let invalid_connections = vec![(0, 1), (1, 5)]; 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()); assert!(invalid_network
926 .expect("failed to create invalid network in test")
927 .validate()
928 .is_err()); }
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}