1#![allow(dead_code)]
63
64use serde::{Deserialize, Serialize};
65use std::collections::HashMap;
66use trustformers_core::{
67 errors::{Result, TrustformersError},
68 tensor::Tensor,
69 traits::Optimizer,
70};
71
72use crate::{common::StateMemoryStats, traits::StatefulOptimizer};
73
74#[derive(Clone, Debug, Serialize, Deserialize)]
76pub struct DeepDistributedQPConfig {
77 pub learning_rate: f32,
79
80 pub num_consensus_nodes: usize,
82
83 pub max_iterations: usize,
85
86 pub tolerance: f32,
88
89 pub relaxation_parameter: f32,
91
92 pub penalty_parameter: f32,
94
95 pub step_size: f32,
97
98 pub adaptive_step_size: bool,
100
101 pub network_hidden_dims: Vec<usize>,
103
104 pub warm_start: bool,
106
107 pub consensus_frequency: usize,
109
110 pub max_problem_size: usize,
112}
113
114impl Default for DeepDistributedQPConfig {
115 fn default() -> Self {
116 Self {
117 learning_rate: 1e-3,
118 num_consensus_nodes: 4,
119 max_iterations: 100,
120 tolerance: 1e-6,
121 relaxation_parameter: 1.6,
122 penalty_parameter: 1.0,
123 step_size: 1.0,
124 adaptive_step_size: true,
125 network_hidden_dims: vec![64, 32],
126 warm_start: true,
127 consensus_frequency: 10,
128 max_problem_size: 10000,
129 }
130 }
131}
132
133#[derive(Clone, Debug)]
135struct ConsensusNode {
136 local_variables: Tensor,
138
139 dual_variables: Tensor,
141
142 constraint_residuals: Tensor,
144
145 consensus_error: f32,
147
148 node_id: usize,
150}
151
152#[derive(Clone, Debug)]
154struct PolicyNetwork {
155 weights: Vec<Tensor>,
157
158 biases: Vec<Tensor>,
160
161 input_mean: Tensor,
163 input_std: Tensor,
164
165 output_scale: f32,
167}
168
169#[derive(Clone, Debug)]
171pub struct DeepDistributedQPState {
172 consensus_nodes: Vec<ConsensusNode>,
174
175 policy_network: Option<PolicyNetwork>,
177
178 previous_solution: Option<Tensor>,
180
181 problem_matrix_p: Option<Tensor>,
183 problem_vector_q: Option<Tensor>,
184 constraint_matrix_a: Option<Tensor>,
185 constraint_vector_b: Option<Tensor>,
186
187 iteration: usize,
189
190 convergence_history: Vec<f32>,
192
193 solve_times: Vec<f32>,
195
196 problem_size: usize,
198}
199
200#[derive(Clone, Debug)]
206pub struct DeepDistributedQP {
207 config: DeepDistributedQPConfig,
208 states: HashMap<String, DeepDistributedQPState>,
209 step: usize,
210 memory_stats: StateMemoryStats,
211
212 global_consensus: Option<Tensor>,
214
215 problems_solved: usize,
217
218 cumulative_speedup: f32,
220}
221
222impl DeepDistributedQP {
223 pub fn new(
225 learning_rate: f32,
226 num_consensus_nodes: usize,
227 max_iterations: usize,
228 tolerance: f32,
229 ) -> Self {
230 Self {
231 config: DeepDistributedQPConfig {
232 learning_rate,
233 num_consensus_nodes,
234 max_iterations,
235 tolerance,
236 ..Default::default()
237 },
238 states: HashMap::new(),
239 step: 0,
240 memory_stats: StateMemoryStats {
241 momentum_elements: 0,
242 variance_elements: 0,
243 third_moment_elements: 0,
244 total_bytes: 0,
245 num_parameters: 0,
246 },
247 global_consensus: None,
248 problems_solved: 0,
249 cumulative_speedup: 1.0,
250 }
251 }
252
253 pub fn for_large_scale() -> Self {
255 Self {
256 config: DeepDistributedQPConfig {
257 learning_rate: 5e-4,
258 num_consensus_nodes: 8,
259 max_iterations: 500,
260 tolerance: 1e-8,
261 relaxation_parameter: 1.8,
262 penalty_parameter: 0.5,
263 step_size: 0.8,
264 adaptive_step_size: true,
265 network_hidden_dims: vec![128, 64, 32],
266 warm_start: true,
267 consensus_frequency: 5,
268 max_problem_size: 50000,
269 },
270 states: HashMap::new(),
271 step: 0,
272 memory_stats: StateMemoryStats {
273 momentum_elements: 0,
274 variance_elements: 0,
275 third_moment_elements: 0,
276 total_bytes: 0,
277 num_parameters: 0,
278 },
279 global_consensus: None,
280 problems_solved: 0,
281 cumulative_speedup: 1.0,
282 }
283 }
284
285 pub fn for_portfolio_optimization() -> Self {
287 Self {
288 config: DeepDistributedQPConfig {
289 learning_rate: 1e-3,
290 num_consensus_nodes: 6,
291 max_iterations: 200,
292 tolerance: 1e-7,
293 relaxation_parameter: 1.5,
294 penalty_parameter: 2.0,
295 step_size: 1.2,
296 adaptive_step_size: true,
297 network_hidden_dims: vec![64, 32, 16],
298 warm_start: true,
299 consensus_frequency: 15,
300 max_problem_size: 5000,
301 },
302 states: HashMap::new(),
303 step: 0,
304 memory_stats: StateMemoryStats {
305 momentum_elements: 0,
306 variance_elements: 0,
307 third_moment_elements: 0,
308 total_bytes: 0,
309 num_parameters: 0,
310 },
311 global_consensus: None,
312 problems_solved: 0,
313 cumulative_speedup: 1.0,
314 }
315 }
316
317 pub fn with_config(config: DeepDistributedQPConfig) -> Self {
319 Self {
320 config,
321 states: HashMap::new(),
322 step: 0,
323 memory_stats: StateMemoryStats {
324 momentum_elements: 0,
325 variance_elements: 0,
326 third_moment_elements: 0,
327 total_bytes: 0,
328 num_parameters: 0,
329 },
330 global_consensus: None,
331 problems_solved: 0,
332 cumulative_speedup: 1.0,
333 }
334 }
335
336 fn initialize_consensus_nodes(&self, problem_size: usize) -> Result<Vec<ConsensusNode>> {
338 let mut nodes = Vec::with_capacity(self.config.num_consensus_nodes);
339
340 for node_id in 0..self.config.num_consensus_nodes {
341 nodes.push(ConsensusNode {
342 local_variables: Tensor::zeros(&[problem_size])?,
343 dual_variables: Tensor::zeros(&[problem_size])?,
344 constraint_residuals: Tensor::zeros(&[problem_size])?,
345 consensus_error: f32::INFINITY,
346 node_id,
347 });
348 }
349
350 Ok(nodes)
351 }
352
353 fn create_policy_network(&self, input_size: usize) -> Result<PolicyNetwork> {
355 let mut weights = Vec::new();
356 let mut biases = Vec::new();
357
358 let mut prev_size = input_size;
359 for &hidden_size in &self.config.network_hidden_dims {
360 let scale = (2.0 / (prev_size + hidden_size) as f32).sqrt();
362 let weight = Tensor::randn(&[prev_size, hidden_size])?.mul_scalar(scale)?;
363 let bias = Tensor::zeros(&[hidden_size])?;
364
365 weights.push(weight);
366 biases.push(bias);
367 prev_size = hidden_size;
368 }
369
370 let output_weight = Tensor::randn(&[prev_size, 1])?.mul_scalar(0.01)?;
372 let output_bias = Tensor::zeros(&[1])?;
373 weights.push(output_weight);
374 biases.push(output_bias);
375
376 Ok(PolicyNetwork {
377 weights,
378 biases,
379 input_mean: Tensor::zeros(&[input_size])?,
380 input_std: Tensor::ones(&[input_size])?,
381 output_scale: 1.0,
382 })
383 }
384
385 fn policy_forward(&self, network: &PolicyNetwork, input: &Tensor) -> Result<Tensor> {
387 let normalized_input = input.sub(&network.input_mean)?.div(&network.input_std)?;
389
390 let input_shape = normalized_input.shape();
392 let batch_size = 1;
393 let feature_size = input_shape.iter().product::<usize>();
394 let reshaped_input = normalized_input.reshape(&[batch_size, feature_size])?;
395
396 let mut x = reshaped_input;
397
398 for i in 0..network.weights.len() - 1 {
400 x = x.matmul(&network.weights[i])?.add(&network.biases[i])?;
401 x = x.relu()?; }
403
404 let output_idx = network.weights.len() - 1;
406 x = x.matmul(&network.weights[output_idx])?.add(&network.biases[output_idx])?;
407
408 let output = x.mul_scalar(network.output_scale)?;
410
411 let final_output = if output.shape().len() == 2 && output.shape()[0] == 1 {
413 output.reshape(&[output.shape()[1]])?
414 } else {
415 output
416 };
417
418 Ok(final_output)
419 }
420
421 fn operator_splitting_update(
423 &self,
424 node: &mut ConsensusNode,
425 gradient: &Tensor,
426 step_size: f32,
427 ) -> Result<()> {
428 let gradient_step = node.local_variables.sub(&gradient.mul_scalar(step_size)?)?;
430
431 let threshold = step_size * self.config.penalty_parameter;
433 node.local_variables = self.soft_threshold(&gradient_step, threshold)?;
434
435 let constraint_violation = node.constraint_residuals.clone(); node.dual_variables = node
438 .dual_variables
439 .add(&constraint_violation.mul_scalar(self.config.penalty_parameter)?)?;
440
441 Ok(())
442 }
443
444 fn soft_threshold(&self, input: &Tensor, threshold: f32) -> Result<Tensor> {
446 let positive_part = input.sub_scalar(threshold)?.relu()?;
447 let negative_part = input.add_scalar(threshold)?.neg()?.relu()?.neg()?;
448 positive_part.add(&negative_part)
449 }
450
451 fn consensus_update(&self, nodes: &mut [ConsensusNode]) -> Result<f32> {
453 let num_nodes = nodes.len();
454 if num_nodes < 2 {
455 return Ok(0.0);
456 }
457
458 let mut consensus_sum = nodes[0].local_variables.clone();
460 for node in nodes.iter().skip(1) {
461 consensus_sum = consensus_sum.add(&node.local_variables)?;
462 }
463 let consensus_avg = consensus_sum.div_scalar(num_nodes as f32)?;
464
465 let mut total_consensus_error = 0.0f32;
467 for node in nodes.iter_mut() {
468 let consensus_diff = consensus_avg.sub(&node.local_variables)?;
469 let consensus_error = consensus_diff.norm()?;
470
471 let update = consensus_diff.mul_scalar(self.config.relaxation_parameter)?;
473 node.local_variables = node.local_variables.add(&update.mul_scalar(0.1)?)?; node.consensus_error = consensus_error;
476 total_consensus_error += consensus_error;
477 }
478
479 Ok(total_consensus_error / num_nodes as f32)
480 }
481
482 fn adaptive_step_size(
484 &self,
485 network: &PolicyNetwork,
486 node: &ConsensusNode,
487 gradient: &Tensor,
488 ) -> Result<f32> {
489 let grad_norm = gradient.norm()?;
491 let var_norm = node.local_variables.norm()?;
492 let dual_norm = node.dual_variables.norm()?;
493 let consensus_error = node.consensus_error;
494
495 let features =
496 Tensor::from_slice(&[grad_norm, var_norm, dual_norm, consensus_error], &[4])?;
497
498 let step_size_tensor = self.policy_forward(network, &features)?;
500 let step_size = if step_size_tensor.shape().iter().product::<usize>() == 1 {
501 step_size_tensor.data()?[0]
503 } else {
504 step_size_tensor.data()?[0]
506 };
507
508 let step_size = step_size.clamp(0.001, 2.0);
510
511 Ok(step_size)
512 }
513
514 fn solve_distributed_qp(&mut self, param_id: &str, gradient: &Tensor) -> Result<Tensor> {
516 let problem_size = gradient.len();
517
518 let param_key = param_id.to_string();
520 let state_exists = self.states.contains_key(¶m_key);
521
522 if !state_exists {
523 let consensus_nodes = self.initialize_consensus_nodes(problem_size).unwrap_or_default();
524 let new_state = DeepDistributedQPState {
525 consensus_nodes,
526 policy_network: None,
527 previous_solution: None,
528 problem_matrix_p: None,
529 problem_vector_q: Some(gradient.clone()),
530 constraint_matrix_a: None,
531 constraint_vector_b: None,
532 iteration: 0,
533 convergence_history: Vec::new(),
534 solve_times: Vec::new(),
535 problem_size,
536 };
537 self.states.insert(param_key.clone(), new_state);
538 }
539
540 let state = self.states.get_mut(¶m_key).ok_or_else(|| {
541 TrustformersError::invalid_state(
542 "state must exist for param_key after insert".to_string(),
543 )
544 })?;
545
546 let needs_policy_network = state.policy_network.is_none();
548 let needs_consensus_nodes = state.consensus_nodes.is_empty();
549 let _ = state; if needs_policy_network {
552 let policy_network = self.create_policy_network(4)?; let state = self.states.get_mut(¶m_key).ok_or_else(|| {
554 TrustformersError::invalid_state(
555 "state must exist for param_key after insert".to_string(),
556 )
557 })?;
558 state.policy_network = Some(policy_network);
559 }
560
561 if needs_consensus_nodes {
562 let consensus_nodes = self.initialize_consensus_nodes(problem_size)?;
563 let state = self.states.get_mut(¶m_key).ok_or_else(|| {
564 TrustformersError::invalid_state(
565 "state must exist for param_key after insert".to_string(),
566 )
567 })?;
568 state.consensus_nodes = consensus_nodes;
569 }
570
571 let state = self.states.get_mut(¶m_key).ok_or_else(|| {
572 TrustformersError::invalid_state(
573 "state must exist for param_key after insert".to_string(),
574 )
575 })?;
576
577 if let (true, Some(prev_solution)) =
579 (self.config.warm_start, state.previous_solution.as_ref())
580 {
581 for node in &mut state.consensus_nodes {
582 node.local_variables = prev_solution.clone();
583 }
584 }
585
586 let start_time = std::time::Instant::now();
587 let mut _converged = false;
588 for iteration in 0..self.config.max_iterations {
590 let state = self.states.get_mut(¶m_key).ok_or_else(|| {
592 TrustformersError::invalid_state(
593 "state must exist for param_key after insert".to_string(),
594 )
595 })?;
596 state.iteration = iteration;
597
598 let adaptive_step = self.config.adaptive_step_size;
600 let consensus_frequency = self.config.consensus_frequency;
601 let tolerance = self.config.tolerance;
602 let step_size = self.config.step_size;
603
604 let mut consensus_nodes = state.consensus_nodes.clone();
606 let policy_network = state.policy_network.clone();
607 let _ = state; for node in &mut consensus_nodes {
611 let actual_step_size = if adaptive_step {
613 if let Some(ref network) = policy_network {
614 self.adaptive_step_size(network, node, gradient)?
615 } else {
616 step_size
617 }
618 } else {
619 step_size
620 };
621
622 self.operator_splitting_update(node, gradient, actual_step_size)?;
624 }
625
626 let state = self.states.get_mut(¶m_key).ok_or_else(|| {
628 TrustformersError::invalid_state(
629 "state must exist for param_key after insert".to_string(),
630 )
631 })?;
632 state.consensus_nodes = consensus_nodes;
633 let _ = state;
634
635 if iteration % consensus_frequency == 0 {
637 let state = self.states.get_mut(¶m_key).ok_or_else(|| {
638 TrustformersError::invalid_state(
639 "state must exist for param_key after insert".to_string(),
640 )
641 })?;
642 let mut nodes = state.consensus_nodes.clone();
643 let _ = state;
644
645 let consensus_error = self.consensus_update(&mut nodes)?;
646
647 let state = self.states.get_mut(¶m_key).ok_or_else(|| {
648 TrustformersError::invalid_state(
649 "state must exist for param_key after insert".to_string(),
650 )
651 })?;
652 state.consensus_nodes = nodes;
653 state.convergence_history.push(consensus_error);
654 let _ = state;
655
656 if consensus_error < tolerance {
658 _converged = true;
659 break;
660 }
661 }
662 }
663
664 let solve_time = start_time.elapsed().as_secs_f32();
665 let state = self.states.get_mut(¶m_key).ok_or_else(|| {
666 TrustformersError::invalid_state(
667 "state must exist for param_key after insert".to_string(),
668 )
669 })?;
670 state.solve_times.push(solve_time);
671
672 let mut solution = state.consensus_nodes[0].local_variables.clone();
674 for node in state.consensus_nodes.iter().skip(1) {
675 solution = solution.add(&node.local_variables)?;
676 }
677 solution = solution.div_scalar(state.consensus_nodes.len() as f32)?;
678
679 state.previous_solution = Some(solution.clone());
681
682 self.problems_solved += 1;
683
684 let baseline_time = solve_time * 2.0; let current_speedup = baseline_time / solve_time.max(1e-6);
687 self.cumulative_speedup = (self.cumulative_speedup * (self.problems_solved - 1) as f32
688 + current_speedup)
689 / self.problems_solved as f32;
690
691 Ok(solution)
692 }
693
694 pub fn qp_solver_stats(&self) -> HashMap<String, (usize, f32, f32, bool)> {
696 self.states
697 .iter()
698 .map(|(name, state)| {
699 let avg_solve_time = if !state.solve_times.is_empty() {
700 state.solve_times.iter().sum::<f32>() / state.solve_times.len() as f32
701 } else {
702 0.0
703 };
704
705 let last_consensus_error =
706 state.convergence_history.last().copied().unwrap_or(f32::INFINITY);
707 let converged = last_consensus_error < self.config.tolerance;
708
709 (
710 name.clone(),
711 (
712 state.iteration,
713 avg_solve_time,
714 last_consensus_error,
715 converged,
716 ),
717 )
718 })
719 .collect()
720 }
721
722 pub fn cumulative_speedup(&self) -> f32 {
724 self.cumulative_speedup
725 }
726
727 pub fn distributed_memory_usage(&self) -> usize {
729 self.states
730 .values()
731 .map(|state| {
732 let nodes_memory = state
733 .consensus_nodes
734 .iter()
735 .map(|node| {
736 node.local_variables.memory_usage()
737 + node.dual_variables.memory_usage()
738 + node.constraint_residuals.memory_usage()
739 })
740 .sum::<usize>();
741
742 let network_memory = if let Some(ref network) = state.policy_network {
743 network.weights.iter().map(|w| w.memory_usage()).sum::<usize>()
744 + network.biases.iter().map(|b| b.memory_usage()).sum::<usize>()
745 + network.input_mean.memory_usage()
746 + network.input_std.memory_usage()
747 } else {
748 0
749 };
750
751 nodes_memory + network_memory
752 })
753 .sum()
754 }
755}
756
757impl Optimizer for DeepDistributedQP {
758 fn update(&mut self, parameter: &mut Tensor, gradient: &Tensor) -> Result<()> {
759 let param_id = format!(
762 "param_{}_{:?}_{}",
763 self.states.len(),
764 parameter.shape(),
765 parameter
766 .data_f32()
767 .unwrap_or_default()
768 .get(0..5)
769 .unwrap_or(&[])
770 .iter()
771 .fold(0u64, |acc, &x| acc.wrapping_add(x.to_bits() as u64))
772 );
773 let qp_solution = self.solve_distributed_qp(¶m_id, gradient)?;
774
775 let update = qp_solution.mul_scalar(self.config.learning_rate)?;
777 *parameter = parameter.sub(&update)?;
778
779 Ok(())
780 }
781
782 fn zero_grad(&mut self) {
783 for state in self.states.values_mut() {
785 state.problem_vector_q = None;
786 }
787 }
788
789 fn step(&mut self) {
790 self.step += 1;
791 }
792
793 fn get_lr(&self) -> f32 {
794 self.config.learning_rate
795 }
796
797 fn set_lr(&mut self, lr: f32) {
798 self.config.learning_rate = lr;
799 }
800}
801
802impl StatefulOptimizer for DeepDistributedQP {
803 type Config = DeepDistributedQPConfig;
804 type State = StateMemoryStats;
805
806 fn config(&self) -> &Self::Config {
807 &self.config
808 }
809
810 fn state(&self) -> &Self::State {
811 &self.memory_stats
812 }
813
814 fn state_mut(&mut self) -> &mut Self::State {
815 &mut self.memory_stats
816 }
817
818 fn state_dict(&self) -> Result<HashMap<String, Tensor>> {
819 let mut state_dict = HashMap::new();
820 state_dict.insert("step".to_string(), Tensor::scalar(self.step as f32)?);
821 state_dict.insert(
822 "problems_solved".to_string(),
823 Tensor::scalar(self.problems_solved as f32)?,
824 );
825 Ok(state_dict)
826 }
827
828 fn load_state_dict(&mut self, state: HashMap<String, Tensor>) -> Result<()> {
829 if let Some(step_tensor) = state.get("step") {
830 self.step = step_tensor.to_scalar()? as usize;
831 }
832 if let Some(problems_tensor) = state.get("problems_solved") {
833 self.problems_solved = problems_tensor.to_scalar()? as usize;
834 }
835 Ok(())
836 }
837
838 fn memory_usage(&self) -> StateMemoryStats {
839 self.memory_stats.clone()
840 }
841
842 fn reset_state(&mut self) {
843 self.states.clear();
844 self.step = 0;
845 self.problems_solved = 0;
846 self.cumulative_speedup = 1.0;
847 self.global_consensus = None;
848 }
849
850 fn num_parameters(&self) -> usize {
851 self.states.len()
852 }
853}
854
855impl DeepDistributedQP {
857 pub fn num_workers(&self) -> usize {
859 self.config.num_consensus_nodes
860 }
861
862 pub fn learning_rate(&self) -> f32 {
864 self.config.learning_rate
865 }
866
867 pub fn communication_rounds(&self) -> usize {
869 self.config.max_iterations / self.config.consensus_frequency
870 }
871
872 pub fn synchronization_overhead(&self) -> f32 {
874 1.0 / self.config.consensus_frequency as f32
875 }
876
877 pub fn solve_qp(
879 &mut self,
880 problem_id: &str,
881 p: &Tensor, q: &Tensor, a: Option<&Tensor>, b: Option<&Tensor>, g: Option<&Tensor>, h: Option<&Tensor>, ) -> Result<Tensor> {
888 let problem_key = problem_id.to_string();
890 let state_exists = self.states.contains_key(&problem_key);
891
892 if !state_exists {
893 let consensus_nodes = self.initialize_consensus_nodes(q.len()).unwrap_or_default();
894 let new_state = DeepDistributedQPState {
895 consensus_nodes,
896 policy_network: None,
897 previous_solution: None,
898 problem_matrix_p: Some(p.clone()),
899 problem_vector_q: Some(q.clone()),
900 constraint_matrix_a: a.cloned(),
901 constraint_vector_b: b.cloned(),
902 iteration: 0,
903 convergence_history: Vec::new(),
904 solve_times: Vec::new(),
905 problem_size: q.len(),
906 };
907 self.states.insert(problem_key.clone(), new_state);
908 }
909
910 let state = self.states.get_mut(&problem_key).ok_or_else(|| {
911 TrustformersError::invalid_state("state must exist for problem_key".to_string())
912 })?;
913
914 if let Some(constraint_mat) = g {
916 for node in &mut state.consensus_nodes {
918 node.constraint_residuals = constraint_mat.matmul(&node.local_variables)?;
919 if let Some(h_vec) = h {
920 node.constraint_residuals = node.constraint_residuals.sub(h_vec)?;
921 }
922 }
923 }
924
925 self.solve_distributed_qp(problem_id, q)
927 }
928
929 pub fn set_policy_weights(
931 &mut self,
932 param_id: &str,
933 weights: Vec<Tensor>,
934 biases: Vec<Tensor>,
935 ) -> Result<()> {
936 if let Some(state) = self.states.get_mut(param_id) {
937 if let Some(ref mut network) = state.policy_network {
938 network.weights = weights;
939 network.biases = biases;
940 }
941 }
942 Ok(())
943 }
944
945 pub fn train_policy(
947 &mut self,
948 param_id: &str,
949 experience_data: &[(Tensor, f32)],
950 ) -> Result<()> {
951 if let Some(state) = self.states.get_mut(param_id) {
953 if let Some(ref mut network) = state.policy_network {
954 if !experience_data.is_empty() {
956 let _features: Vec<_> =
957 experience_data.iter().map(|(f, _)| f.clone()).collect();
958 network.output_scale *= 1.01; }
961 }
962 }
963 Ok(())
964 }
965}
966
967#[cfg(test)]
968mod tests {
969 use super::*;
970
971 #[test]
972 fn test_deep_distributed_qp_creation() {
973 let optimizer = DeepDistributedQP::new(1e-3, 4, 100, 1e-6);
974 assert_eq!(optimizer.learning_rate(), 1e-3);
975 assert_eq!(optimizer.config.num_consensus_nodes, 4);
976 assert_eq!(optimizer.config.max_iterations, 100);
977 }
978
979 #[test]
980 fn test_deep_distributed_qp_presets() {
981 let large_scale = DeepDistributedQP::for_large_scale();
982 assert_eq!(large_scale.config.num_consensus_nodes, 8);
983 assert_eq!(large_scale.config.max_iterations, 500);
984
985 let portfolio = DeepDistributedQP::for_portfolio_optimization();
986 assert_eq!(portfolio.config.num_consensus_nodes, 6);
987 assert_eq!(portfolio.config.penalty_parameter, 2.0);
988 }
989
990 #[test]
991 fn test_consensus_nodes_initialization() -> Result<()> {
992 let optimizer = DeepDistributedQP::new(1e-3, 3, 50, 1e-6);
993 let nodes = optimizer.initialize_consensus_nodes(5)?;
994
995 assert_eq!(nodes.len(), 3);
996 for (i, node) in nodes.iter().enumerate() {
997 assert_eq!(node.node_id, i);
998 assert_eq!(node.local_variables.shape(), &[5]);
999 }
1000
1001 Ok(())
1002 }
1003
1004 #[test]
1005 fn test_policy_network_creation() -> Result<()> {
1006 let optimizer = DeepDistributedQP::new(1e-3, 4, 100, 1e-6);
1007 let network = optimizer.create_policy_network(4)?;
1008
1009 assert_eq!(network.weights.len(), 3); assert_eq!(network.biases.len(), 3);
1011 assert_eq!(network.input_mean.shape(), &[4]);
1012
1013 Ok(())
1014 }
1015
1016 #[test]
1017 fn test_soft_threshold() -> Result<()> {
1018 let optimizer = DeepDistributedQP::new(1e-3, 4, 100, 1e-6);
1019 let input = Tensor::from_slice(&[-2.0, -0.5, 0.0, 0.5, 2.0], &[5])?;
1020 let threshold = 1.0;
1021
1022 let result = optimizer.soft_threshold(&input, threshold)?;
1023 let result_vec = result.data()?;
1024
1025 assert!((result_vec[0] - (-1.0)).abs() < 1e-5);
1027 assert!(result_vec[1].abs() < 1e-5);
1028 assert!(result_vec[2].abs() < 1e-5);
1029 assert!(result_vec[3].abs() < 1e-5);
1030 assert!((result_vec[4] - 1.0).abs() < 1e-5);
1031
1032 Ok(())
1033 }
1034
1035 #[test]
1036 fn test_simple_qp_solve() -> Result<()> {
1037 let mut optimizer = DeepDistributedQP::new(0.1, 2, 20, 1e-4);
1038 let mut parameter = Tensor::from_slice(&[1.0, 2.0, 3.0], &[3])?;
1039 let gradient = Tensor::from_slice(&[0.1, 0.2, 0.1], &[3])?;
1040
1041 optimizer.update(&mut parameter, &gradient)?;
1043 optimizer.step();
1044
1045 Ok(())
1049 }
1050
1051 #[test]
1052 fn test_qp_solver_stats() -> Result<()> {
1053 let mut optimizer = DeepDistributedQP::new(1e-3, 2, 10, 1e-4);
1054 let mut param = Tensor::from_slice(&[1.0, 2.0], &[2])?;
1055 let grad = Tensor::from_slice(&[0.1, 0.1], &[2])?;
1056
1057 optimizer.update(&mut param, &grad)?;
1058
1059 let stats = optimizer.qp_solver_stats();
1060 assert_eq!(stats.len(), 1);
1061
1062 let (iterations, solve_time, _consensus_error, _converged) =
1063 stats.values().next().expect("Operation failed in test");
1064 assert!(*iterations <= 10);
1065 assert!(*solve_time >= 0.0);
1066
1067 Ok(())
1068 }
1069
1070 #[test]
1071 fn test_memory_usage() -> Result<()> {
1072 let mut optimizer = DeepDistributedQP::new(1e-3, 3, 10, 1e-4);
1073 let mut param = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], &[4])?;
1074 let grad = Tensor::from_slice(&[0.1, 0.1, 0.1, 0.1], &[4])?;
1075
1076 let memory_before = optimizer.distributed_memory_usage();
1077 optimizer.update(&mut param, &grad)?;
1078 let memory_after = optimizer.distributed_memory_usage();
1079
1080 assert!(memory_after >= memory_before);
1081
1082 Ok(())
1083 }
1084}