1#![allow(dead_code)]
8
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use std::fmt;
12
13pub struct VisualProblemBuilder {
15 problem: VisualProblem,
17 config: BuilderConfig,
19 validator: ProblemValidator,
21 generator: CodeGenerator,
23 history: ProblemHistory,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct BuilderConfig {
29 pub real_time_validation: bool,
31 pub auto_save_interval: Option<std::time::Duration>,
33 pub max_problem_size: usize,
35 pub default_variable_type: VariableType,
37 pub supported_formats: Vec<ExportFormat>,
39 pub theme: Theme,
41}
42
43impl Default for BuilderConfig {
44 fn default() -> Self {
45 Self {
46 real_time_validation: true,
47 auto_save_interval: Some(std::time::Duration::from_secs(30)),
48 max_problem_size: 10000,
49 default_variable_type: VariableType::Binary,
50 supported_formats: vec![
51 ExportFormat::Python,
52 ExportFormat::Rust,
53 ExportFormat::JSON,
54 ExportFormat::QUBO,
55 ],
56 theme: Theme::default(),
57 }
58 }
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct Theme {
63 pub primary_color: String,
65 pub secondary_color: String,
67 pub background_color: String,
69 pub text_color: String,
71 pub grid: GridSettings,
73}
74
75impl Default for Theme {
76 fn default() -> Self {
77 Self {
78 primary_color: "#007acc".to_string(),
79 secondary_color: "#ffa500".to_string(),
80 background_color: "#ffffff".to_string(),
81 text_color: "#000000".to_string(),
82 grid: GridSettings::default(),
83 }
84 }
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct GridSettings {
89 pub enabled: bool,
91 pub size: usize,
93 pub color: String,
95 pub snap: bool,
97}
98
99impl Default for GridSettings {
100 fn default() -> Self {
101 Self {
102 enabled: true,
103 size: 20,
104 color: "#e0e0e0".to_string(),
105 snap: true,
106 }
107 }
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct VisualProblem {
113 pub metadata: ProblemMetadata,
115 pub variables: Vec<VisualVariable>,
117 pub constraints: Vec<VisualConstraint>,
119 pub objective: Option<VisualObjective>,
121 pub layout: ProblemLayout,
123 pub state: ProblemState,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct ProblemMetadata {
129 pub name: String,
131 pub description: String,
133 pub author: String,
135 pub created: std::time::SystemTime,
137 pub modified: std::time::SystemTime,
139 pub category: ProblemCategory,
141 pub tags: Vec<String>,
143 pub version: String,
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
148pub enum ProblemCategory {
149 Optimization,
151 Decision,
153 ConstraintSatisfaction,
155 Scheduling,
157 Routing,
159 Finance,
161 MachineLearning,
163 Custom(String),
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct VisualVariable {
169 pub id: String,
171 pub name: String,
173 pub var_type: VariableType,
175 pub domain: VariableDomain,
177 pub position: Position,
179 pub visual_properties: VariableVisualProperties,
181 pub description: String,
183 pub groups: Vec<String>,
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
188pub enum VariableType {
189 Binary,
191 Integer { min: i64, max: i64 },
193 Real { min: f64, max: f64 },
195 Categorical { options: Vec<String> },
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub enum VariableDomain {
201 Binary,
203 IntegerRange { min: i64, max: i64 },
205 RealRange { min: f64, max: f64 },
207 Discrete { values: Vec<String> },
209 Custom { specification: String },
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct Position {
215 pub x: f64,
217 pub y: f64,
219 pub z: Option<f64>,
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize)]
224pub struct VariableVisualProperties {
225 pub color: String,
227 pub size: f64,
229 pub shape: VariableShape,
231 pub visible: bool,
233 pub label: LabelSettings,
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
238pub enum VariableShape {
239 Circle,
240 Square,
241 Triangle,
242 Diamond,
243 Hexagon,
244 Custom(String),
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct LabelSettings {
249 pub show: bool,
251 pub text: Option<String>,
253 pub font_size: f64,
255 pub position: LabelPosition,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
260pub enum LabelPosition {
261 Top,
262 Bottom,
263 Left,
264 Right,
265 Center,
266 Custom(Position),
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
271pub struct VisualConstraint {
272 pub id: String,
274 pub name: String,
276 pub constraint_type: ConstraintType,
278 pub variables: Vec<String>,
280 pub parameters: HashMap<String, ConstraintParameter>,
282 pub visual_properties: ConstraintVisualProperties,
284 pub validation_status: ValidationStatus,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize)]
289pub enum ConstraintType {
290 Linear {
292 coefficients: Vec<f64>,
293 operator: ComparisonOperator,
294 rhs: f64,
295 },
296 Quadratic {
298 matrix: Vec<Vec<f64>>,
299 operator: ComparisonOperator,
300 rhs: f64,
301 },
302 Logical { expression: LogicalExpression },
304 Cardinality {
306 min: Option<usize>,
307 max: Option<usize>,
308 },
309 AllDifferent,
311 Custom { name: String, expression: String },
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize)]
316pub enum ComparisonOperator {
317 LessEqual,
318 GreaterEqual,
319 Equal,
320 LessThan,
321 GreaterThan,
322}
323
324#[derive(Debug, Clone, Serialize, Deserialize)]
325pub enum LogicalExpression {
326 And(Vec<String>),
327 Or(Vec<String>),
328 Not(String),
329 Implies(String, String),
330 Equivalent(String, String),
331}
332
333#[derive(Debug, Clone, Serialize, Deserialize)]
334pub enum ConstraintParameter {
335 Integer(i64),
336 Real(f64),
337 String(String),
338 Boolean(bool),
339 Vector(Vec<f64>),
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct ConstraintVisualProperties {
344 pub color: String,
346 pub line_style: LineStyle,
348 pub thickness: f64,
350 pub connections: Vec<Connection>,
352 pub show_equation: bool,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
357pub enum LineStyle {
358 Solid,
359 Dashed,
360 Dotted,
361 DashDot,
362}
363
364#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct Connection {
366 pub from: String,
368 pub to: String,
370 pub connection_type: ConnectionType,
372 pub path: Vec<Position>,
374}
375
376#[derive(Debug, Clone, Serialize, Deserialize)]
377pub enum ConnectionType {
378 Direct,
379 Curved,
380 Orthogonal,
381 Custom,
382}
383
384#[derive(Debug, Clone, Serialize, Deserialize)]
386pub struct VisualObjective {
387 pub id: String,
389 pub name: String,
391 pub objective_type: ObjectiveType,
393 pub expression: ObjectiveExpression,
395 pub direction: OptimizationDirection,
397 pub visual_properties: ObjectiveVisualProperties,
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize)]
402pub enum ObjectiveType {
403 Linear,
404 Quadratic,
405 Polynomial,
406 Custom,
407}
408
409#[derive(Debug, Clone, Serialize, Deserialize)]
410pub enum ObjectiveExpression {
411 Linear {
412 coefficients: HashMap<String, f64>,
413 constant: f64,
414 },
415 Quadratic {
416 linear_terms: HashMap<String, f64>,
417 quadratic_terms: HashMap<(String, String), f64>,
418 constant: f64,
419 },
420 Custom {
421 expression: String,
422 },
423}
424
425#[derive(Debug, Clone, Serialize, Deserialize)]
426pub enum OptimizationDirection {
427 Minimize,
428 Maximize,
429}
430
431#[derive(Debug, Clone, Serialize, Deserialize)]
432pub struct ObjectiveVisualProperties {
433 pub color_scheme: ColorScheme,
435 pub show_heatmap: bool,
437 pub show_contours: bool,
439}
440
441#[derive(Debug, Clone, Serialize, Deserialize)]
442pub enum ColorScheme {
443 Viridis,
444 Plasma,
445 Inferno,
446 Magma,
447 Blues,
448 Reds,
449 Greens,
450 Custom(Vec<String>),
451}
452
453#[derive(Debug, Clone, Serialize, Deserialize)]
455pub struct ProblemLayout {
456 pub layout_type: LayoutType,
458 pub dimensions: Dimensions,
460 pub auto_layout: AutoLayoutSettings,
462 pub zoom: f64,
464 pub center: Position,
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
469pub enum LayoutType {
470 FreeForm,
472 Grid { rows: usize, cols: usize },
474 ForceDirected,
476 Hierarchical,
478 Circular,
480 Custom(String),
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize)]
485pub struct Dimensions {
486 pub width: f64,
488 pub height: f64,
490 pub depth: Option<f64>,
492}
493
494#[derive(Debug, Clone, Serialize, Deserialize)]
495pub struct AutoLayoutSettings {
496 pub enabled: bool,
498 pub algorithm: LayoutAlgorithm,
500 pub parameters: HashMap<String, f64>,
502}
503
504#[derive(Debug, Clone, Serialize, Deserialize)]
505pub enum LayoutAlgorithm {
506 SpringEmbedder,
507 ForceAtlas2,
508 Fruchterman,
509 Kamada,
510 Custom(String),
511}
512
513#[derive(Debug, Clone, Serialize, Deserialize)]
515pub enum ProblemState {
516 Editing,
518 Validating,
520 Valid,
522 Invalid { errors: Vec<ValidationError> },
524 Solving,
526 Solved { solution: ProblemSolution },
528}
529
530#[derive(Debug, Clone, Serialize, Deserialize)]
531pub struct ProblemSolution {
532 pub assignments: HashMap<String, f64>,
534 pub objective_value: f64,
536 pub quality: f64,
538 pub solve_time: std::time::Duration,
540}
541
542pub struct ProblemValidator {
544 rules: Vec<ValidationRule>,
546 errors: Vec<ValidationError>,
548}
549
550#[derive(Debug, Clone, Serialize, Deserialize)]
551pub struct ValidationRule {
552 pub name: String,
554 pub rule_type: ValidationRuleType,
556 pub severity: ValidationSeverity,
558 pub message_template: String,
560}
561
562#[derive(Debug, Clone, Serialize, Deserialize)]
563pub enum ValidationRuleType {
564 VariableCount { min: usize, max: usize },
566 ConstraintConsistency,
568 ObjectiveFunction,
570 VariableDependencies,
572 DomainValidity,
574 Custom(String),
576}
577
578#[derive(Debug, Clone, Serialize, Deserialize)]
579pub enum ValidationSeverity {
580 Info,
581 Warning,
582 Error,
583 Critical,
584}
585
586#[derive(Debug, Clone, Serialize, Deserialize)]
587pub struct ValidationError {
588 pub id: String,
590 pub error_type: ValidationRuleType,
592 pub severity: ValidationSeverity,
594 pub message: String,
596 pub location: Option<ErrorLocation>,
598 pub suggestions: Vec<String>,
600}
601
602#[derive(Debug, Clone, Serialize, Deserialize)]
603pub enum ErrorLocation {
604 Variable(String),
605 Constraint(String),
606 Objective,
607 Global,
608}
609
610#[derive(Debug, Clone, Serialize, Deserialize)]
611pub enum ValidationStatus {
612 Valid,
613 Warning,
614 Error,
615 Unknown,
616}
617
618pub struct CodeGenerator {
620 templates: HashMap<ExportFormat, CodeTemplate>,
622 settings: GenerationSettings,
624}
625
626#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
627pub enum ExportFormat {
628 Python,
630 Rust,
632 JSON,
634 QUBO,
636 MPS,
638 LP,
640 Custom(String),
642}
643
644#[derive(Debug, Clone, Serialize, Deserialize)]
645pub struct CodeTemplate {
646 pub template: String,
648 pub placeholders: Vec<String>,
650 pub metadata: TemplateMetadata,
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize)]
655pub struct TemplateMetadata {
656 pub name: String,
658 pub description: String,
660 pub version: String,
662 pub author: String,
664}
665
666#[derive(Debug, Clone, Serialize, Deserialize)]
667pub struct GenerationSettings {
668 pub include_comments: bool,
670 pub code_style: CodeStyle,
672 pub optimization_level: OptimizationLevel,
674}
675
676#[derive(Debug, Clone, Serialize, Deserialize)]
677pub enum CodeStyle {
678 Compact,
679 Readable,
680 Verbose,
681 Custom(HashMap<String, String>),
682}
683
684#[derive(Debug, Clone, Serialize, Deserialize)]
685pub enum OptimizationLevel {
686 None,
687 Basic,
688 Advanced,
689 Aggressive,
690}
691
692pub struct ProblemHistory {
694 history: Vec<HistoryEntry>,
696 current: usize,
698 max_size: usize,
700}
701
702#[derive(Debug, Clone)]
703pub struct HistoryEntry {
704 pub action_type: ActionType,
706 pub timestamp: std::time::Instant,
708 pub before_state: VisualProblem,
710 pub after_state: VisualProblem,
712 pub description: String,
714}
715
716#[derive(Debug, Clone)]
717pub enum ActionType {
718 AddVariable(String),
720 RemoveVariable(String),
722 ModifyVariable(String),
724 AddConstraint(String),
726 RemoveConstraint(String),
728 ModifyConstraint(String),
730 SetObjective,
732 ModifyObjective,
734 LayoutChange,
736 BulkOperation(Vec<Self>),
738}
739
740impl VisualProblemBuilder {
741 pub fn new(config: BuilderConfig) -> Self {
743 Self {
744 problem: VisualProblem::new(),
745 config,
746 validator: ProblemValidator::new(),
747 generator: CodeGenerator::new(),
748 history: ProblemHistory::new(100),
749 }
750 }
751
752 pub fn new_problem(&mut self, name: &str) -> Result<(), String> {
754 let problem = VisualProblem {
755 metadata: ProblemMetadata {
756 name: name.to_string(),
757 description: String::new(),
758 author: String::new(),
759 created: std::time::SystemTime::now(),
760 modified: std::time::SystemTime::now(),
761 category: ProblemCategory::Optimization,
762 tags: Vec::new(),
763 version: "1.0.0".to_string(),
764 },
765 variables: Vec::new(),
766 constraints: Vec::new(),
767 objective: None,
768 layout: ProblemLayout {
769 layout_type: LayoutType::FreeForm,
770 dimensions: Dimensions {
771 width: 1000.0,
772 height: 800.0,
773 depth: None,
774 },
775 auto_layout: AutoLayoutSettings {
776 enabled: false,
777 algorithm: LayoutAlgorithm::SpringEmbedder,
778 parameters: HashMap::new(),
779 },
780 zoom: 1.0,
781 center: Position {
782 x: 500.0,
783 y: 400.0,
784 z: None,
785 },
786 },
787 state: ProblemState::Editing,
788 };
789
790 let old_problem = self.problem.clone();
791 self.problem = problem;
792 let current_problem = self.problem.clone();
793 self.record_action(
794 ActionType::BulkOperation(vec![]),
795 &old_problem,
796 ¤t_problem,
797 "New problem created",
798 );
799
800 Ok(())
801 }
802
803 pub fn add_variable(
805 &mut self,
806 name: &str,
807 var_type: VariableType,
808 position: Position,
809 ) -> Result<String, String> {
810 if self.problem.variables.iter().any(|v| v.name == name) {
812 return Err(format!("Variable '{name}' already exists"));
813 }
814
815 let id = format!("var_{}", self.problem.variables.len());
816
817 let variable = VisualVariable {
818 id: id.clone(),
819 name: name.to_string(),
820 var_type: var_type.clone(),
821 domain: match var_type {
822 VariableType::Binary => VariableDomain::Binary,
823 VariableType::Integer { min, max } => VariableDomain::IntegerRange { min, max },
824 VariableType::Real { min, max } => VariableDomain::RealRange { min, max },
825 VariableType::Categorical { options } => {
826 VariableDomain::Discrete { values: options }
827 }
828 },
829 position,
830 visual_properties: VariableVisualProperties {
831 color: self.config.theme.primary_color.clone(),
832 size: 20.0,
833 shape: VariableShape::Circle,
834 visible: true,
835 label: LabelSettings {
836 show: true,
837 text: Some(name.to_string()),
838 font_size: 12.0,
839 position: LabelPosition::Bottom,
840 },
841 },
842 description: String::new(),
843 groups: Vec::new(),
844 };
845
846 let before_state = self.problem.clone();
847 self.problem.variables.push(variable);
848 self.problem.metadata.modified = std::time::SystemTime::now();
849
850 let current_problem = self.problem.clone();
851 self.record_action(
852 ActionType::AddVariable(id.clone()),
853 &before_state,
854 ¤t_problem,
855 &format!("Added variable '{name}'"),
856 );
857
858 if self.config.real_time_validation {
859 self.validate_problem()?;
860 }
861
862 Ok(id)
863 }
864
865 pub fn remove_variable(&mut self, variable_id: &str) -> Result<(), String> {
867 let pos = self
868 .problem
869 .variables
870 .iter()
871 .position(|v| v.id == variable_id)
872 .ok_or_else(|| format!("Variable '{variable_id}' not found"))?;
873
874 let before_state = self.problem.clone();
875 let variable = self.problem.variables.remove(pos);
876
877 for constraint in &mut self.problem.constraints {
879 constraint.variables.retain(|v| v != &variable.id);
880 }
881
882 self.problem.constraints.retain(|c| !c.variables.is_empty());
884
885 self.problem.metadata.modified = std::time::SystemTime::now();
886
887 let current_problem = self.problem.clone();
888 self.record_action(
889 ActionType::RemoveVariable(variable_id.to_string()),
890 &before_state,
891 ¤t_problem,
892 &format!("Removed variable '{}'", variable.name),
893 );
894
895 if self.config.real_time_validation {
896 self.validate_problem()?;
897 }
898
899 Ok(())
900 }
901
902 pub fn add_constraint(
904 &mut self,
905 name: &str,
906 constraint_type: ConstraintType,
907 variables: Vec<String>,
908 ) -> Result<String, String> {
909 for var_id in &variables {
911 if !self.problem.variables.iter().any(|v| &v.id == var_id) {
912 return Err(format!("Variable '{var_id}' not found"));
913 }
914 }
915
916 let id = format!("constraint_{}", self.problem.constraints.len());
917
918 let constraint = VisualConstraint {
919 id: id.clone(),
920 name: name.to_string(),
921 constraint_type,
922 variables,
923 parameters: HashMap::new(),
924 visual_properties: ConstraintVisualProperties {
925 color: self.config.theme.secondary_color.clone(),
926 line_style: LineStyle::Solid,
927 thickness: 2.0,
928 connections: Vec::new(),
929 show_equation: true,
930 },
931 validation_status: ValidationStatus::Unknown,
932 };
933
934 let before_state = self.problem.clone();
935 self.problem.constraints.push(constraint);
936 self.problem.metadata.modified = std::time::SystemTime::now();
937
938 let current_problem = self.problem.clone();
939 self.record_action(
940 ActionType::AddConstraint(id.clone()),
941 &before_state,
942 ¤t_problem,
943 &format!("Added constraint '{name}'"),
944 );
945
946 if self.config.real_time_validation {
947 self.validate_problem()?;
948 }
949
950 Ok(id)
951 }
952
953 pub fn set_objective(
955 &mut self,
956 name: &str,
957 expression: ObjectiveExpression,
958 direction: OptimizationDirection,
959 ) -> Result<(), String> {
960 let objective = VisualObjective {
961 id: "objective_0".to_string(),
962 name: name.to_string(),
963 objective_type: match &expression {
964 ObjectiveExpression::Linear { .. } => ObjectiveType::Linear,
965 ObjectiveExpression::Quadratic { .. } => ObjectiveType::Quadratic,
966 ObjectiveExpression::Custom { .. } => ObjectiveType::Custom,
967 },
968 expression,
969 direction,
970 visual_properties: ObjectiveVisualProperties {
971 color_scheme: ColorScheme::Viridis,
972 show_heatmap: false,
973 show_contours: false,
974 },
975 };
976
977 let before_state = self.problem.clone();
978 self.problem.objective = Some(objective);
979 self.problem.metadata.modified = std::time::SystemTime::now();
980
981 let current_problem = self.problem.clone();
982 self.record_action(
983 ActionType::SetObjective,
984 &before_state,
985 ¤t_problem,
986 &format!("Set objective function '{name}'"),
987 );
988
989 if self.config.real_time_validation {
990 self.validate_problem()?;
991 }
992
993 Ok(())
994 }
995
996 pub fn auto_layout(&mut self, algorithm: LayoutAlgorithm) -> Result<(), String> {
998 let before_state = self.problem.clone();
999
1000 match algorithm {
1001 LayoutAlgorithm::SpringEmbedder => {
1002 self.apply_spring_layout()?;
1003 }
1004 LayoutAlgorithm::ForceAtlas2 => {
1005 self.apply_force_atlas_layout()?;
1006 }
1007 LayoutAlgorithm::Fruchterman => {
1008 self.apply_fruchterman_layout()?;
1009 }
1010 _ => return Err("Layout algorithm not implemented".to_string()),
1011 }
1012
1013 self.problem.metadata.modified = std::time::SystemTime::now();
1014
1015 let current_problem = self.problem.clone();
1016 self.record_action(
1017 ActionType::LayoutChange,
1018 &before_state,
1019 ¤t_problem,
1020 &format!("Applied {algorithm:?} layout"),
1021 );
1022
1023 Ok(())
1024 }
1025
1026 pub fn validate_problem(&mut self) -> Result<(), String> {
1028 self.problem.state = ProblemState::Validating;
1029
1030 let errors = self.validator.validate(&self.problem)?;
1031
1032 if errors.is_empty() {
1033 self.problem.state = ProblemState::Valid;
1034 } else {
1035 self.problem.state = ProblemState::Invalid { errors };
1036 }
1037
1038 Ok(())
1039 }
1040
1041 pub fn generate_code(&self, format: ExportFormat) -> Result<String, String> {
1043 self.generator.generate(&self.problem, format)
1044 }
1045
1046 pub fn undo(&mut self) -> Result<(), String> {
1058 if let Some(entry) = self.history.undo() {
1059 self.problem = entry.before_state.clone();
1060 Ok(())
1061 } else {
1062 Err("Nothing to undo".to_string())
1063 }
1064 }
1065
1066 pub fn redo(&mut self) -> Result<(), String> {
1068 if let Some(entry) = self.history.redo() {
1069 self.problem = entry.after_state.clone();
1070 Ok(())
1071 } else {
1072 Err("Nothing to redo".to_string())
1073 }
1074 }
1075
1076 pub const fn get_problem(&self) -> &VisualProblem {
1078 &self.problem
1079 }
1080
1081 pub fn load_problem(&mut self, json: &str) -> Result<(), String> {
1083 let problem: VisualProblem =
1084 serde_json::from_str(json).map_err(|e| format!("Failed to parse JSON: {e}"))?;
1085
1086 let before_state = self.problem.clone();
1087 let _old_problem = self.problem.clone();
1088 self.problem = problem;
1089
1090 let current_problem = self.problem.clone();
1091 self.record_action(
1092 ActionType::BulkOperation(vec![]),
1093 &before_state,
1094 ¤t_problem,
1095 "Loaded problem from JSON",
1096 );
1097
1098 Ok(())
1099 }
1100
1101 pub fn save_problem(&self) -> Result<String, String> {
1103 serde_json::to_string_pretty(&self.problem)
1104 .map_err(|e| format!("Failed to serialize to JSON: {e}"))
1105 }
1106
1107 fn record_action(
1167 &mut self,
1168 action_type: ActionType,
1169 before: &VisualProblem,
1170 after: &VisualProblem,
1171 description: &str,
1172 ) {
1173 let entry = HistoryEntry {
1174 action_type,
1175 timestamp: std::time::Instant::now(),
1176 before_state: before.clone(),
1177 after_state: after.clone(),
1178 description: description.to_string(),
1179 };
1180
1181 self.history.push(entry);
1182 }
1183
1184 fn apply_spring_layout(&mut self) -> Result<(), String> {
1186 let n = self.problem.variables.len();
1187 if n == 0 {
1188 return Ok(());
1189 }
1190
1191 let mut positions: Vec<(f64, f64)> = self
1193 .problem
1194 .variables
1195 .iter()
1196 .map(|v| (v.position.x, v.position.y))
1197 .collect();
1198
1199 for _ in 0..100 {
1200 let mut forces = vec![(0.0, 0.0); n];
1201
1202 for i in 0..n {
1204 for j in i + 1..n {
1205 let dx = positions[i].0 - positions[j].0;
1206 let dy = positions[i].1 - positions[j].1;
1207 let dist = dx.hypot(dy).max(1.0);
1208
1209 let force = 1000.0 / (dist * dist);
1210 let fx = force * dx / dist;
1211 let fy = force * dy / dist;
1212
1213 forces[i].0 += fx;
1214 forces[i].1 += fy;
1215 forces[j].0 -= fx;
1216 forces[j].1 -= fy;
1217 }
1218 }
1219
1220 for constraint in &self.problem.constraints {
1222 for i in 0..constraint.variables.len() {
1223 for j in i + 1..constraint.variables.len() {
1224 if let (Some(idx1), Some(idx2)) = (
1225 self.problem
1226 .variables
1227 .iter()
1228 .position(|v| v.id == constraint.variables[i]),
1229 self.problem
1230 .variables
1231 .iter()
1232 .position(|v| v.id == constraint.variables[j]),
1233 ) {
1234 let dx = positions[idx1].0 - positions[idx2].0;
1235 let dy = positions[idx1].1 - positions[idx2].1;
1236 let dist = dx.hypot(dy).max(1.0);
1237
1238 let force = 0.1 * dist;
1239 let fx = force * dx / dist;
1240 let fy = force * dy / dist;
1241
1242 forces[idx1].0 -= fx;
1243 forces[idx1].1 -= fy;
1244 forces[idx2].0 += fx;
1245 forces[idx2].1 += fy;
1246 }
1247 }
1248 }
1249 }
1250
1251 for i in 0..n {
1253 positions[i].0 += forces[i].0 * 0.01;
1254 positions[i].1 += forces[i].1 * 0.01;
1255 }
1256 }
1257
1258 for (i, var) in self.problem.variables.iter_mut().enumerate() {
1260 var.position.x = positions[i].0;
1261 var.position.y = positions[i].1;
1262 }
1263
1264 Ok(())
1265 }
1266
1267 fn apply_force_atlas_layout(&mut self) -> Result<(), String> {
1269 self.apply_spring_layout()
1271 }
1272
1273 fn apply_fruchterman_layout(&mut self) -> Result<(), String> {
1275 self.apply_spring_layout()
1277 }
1278}
1279
1280impl Default for VisualProblem {
1281 fn default() -> Self {
1282 Self::new()
1283 }
1284}
1285
1286impl VisualProblem {
1287 pub fn new() -> Self {
1289 Self {
1290 metadata: ProblemMetadata {
1291 name: "Untitled".to_string(),
1292 description: String::new(),
1293 author: String::new(),
1294 created: std::time::SystemTime::now(),
1295 modified: std::time::SystemTime::now(),
1296 category: ProblemCategory::Optimization,
1297 tags: Vec::new(),
1298 version: "1.0.0".to_string(),
1299 },
1300 variables: Vec::new(),
1301 constraints: Vec::new(),
1302 objective: None,
1303 layout: ProblemLayout {
1304 layout_type: LayoutType::FreeForm,
1305 dimensions: Dimensions {
1306 width: 1000.0,
1307 height: 800.0,
1308 depth: None,
1309 },
1310 auto_layout: AutoLayoutSettings {
1311 enabled: false,
1312 algorithm: LayoutAlgorithm::SpringEmbedder,
1313 parameters: HashMap::new(),
1314 },
1315 zoom: 1.0,
1316 center: Position {
1317 x: 500.0,
1318 y: 400.0,
1319 z: None,
1320 },
1321 },
1322 state: ProblemState::Editing,
1323 }
1324 }
1325}
1326
1327impl Default for ProblemValidator {
1328 fn default() -> Self {
1329 Self::new()
1330 }
1331}
1332
1333impl ProblemValidator {
1334 pub fn new() -> Self {
1336 Self {
1337 rules: Self::default_rules(),
1338 errors: Vec::new(),
1339 }
1340 }
1341
1342 fn default_rules() -> Vec<ValidationRule> {
1344 vec![
1345 ValidationRule {
1346 name: "Variable count".to_string(),
1347 rule_type: ValidationRuleType::VariableCount { min: 1, max: 10000 },
1348 severity: ValidationSeverity::Error,
1349 message_template: "Problem must have between {min} and {max} variables".to_string(),
1350 },
1351 ValidationRule {
1352 name: "Objective function".to_string(),
1353 rule_type: ValidationRuleType::ObjectiveFunction,
1354 severity: ValidationSeverity::Warning,
1355 message_template: "Problem should have an objective function".to_string(),
1356 },
1357 ]
1358 }
1359
1360 pub fn validate(&mut self, problem: &VisualProblem) -> Result<Vec<ValidationError>, String> {
1362 self.errors.clear();
1363
1364 for rule in &self.rules {
1365 match &rule.rule_type {
1366 ValidationRuleType::VariableCount { min, max } => {
1367 let count = problem.variables.len();
1368 if count < *min || count > *max {
1369 self.errors.push(ValidationError {
1370 id: format!("var_count_{count}"),
1371 error_type: rule.rule_type.clone(),
1372 severity: rule.severity.clone(),
1373 message: rule
1374 .message_template
1375 .replace("{min}", &min.to_string())
1376 .replace("{max}", &max.to_string()),
1377 location: Some(ErrorLocation::Global),
1378 suggestions: vec![
1379 "Add more variables".to_string(),
1380 "Remove unnecessary variables".to_string(),
1381 ],
1382 });
1383 }
1384 }
1385 ValidationRuleType::ObjectiveFunction => {
1386 if problem.objective.is_none() {
1387 self.errors.push(ValidationError {
1388 id: "missing_objective".to_string(),
1389 error_type: rule.rule_type.clone(),
1390 severity: rule.severity.clone(),
1391 message: rule.message_template.clone(),
1392 location: Some(ErrorLocation::Objective),
1393 suggestions: vec!["Add an objective function".to_string()],
1394 });
1395 }
1396 }
1397 _ => {}
1398 }
1399 }
1400
1401 Ok(self.errors.clone())
1402 }
1403}
1404
1405impl Default for CodeGenerator {
1406 fn default() -> Self {
1407 Self::new()
1408 }
1409}
1410
1411impl CodeGenerator {
1412 pub fn new() -> Self {
1414 Self {
1415 templates: Self::default_templates(),
1416 settings: GenerationSettings {
1417 include_comments: true,
1418 code_style: CodeStyle::Readable,
1419 optimization_level: OptimizationLevel::Basic,
1420 },
1421 }
1422 }
1423
1424 fn default_templates() -> HashMap<ExportFormat, CodeTemplate> {
1426 let mut templates = HashMap::new();
1427
1428 templates.insert(
1429 ExportFormat::Python,
1430 CodeTemplate {
1431 template: r"
1432# Generated quantum optimization problem
1433import numpy as np
1434from quantrs2_tytan import *
1435
1436# Variables
1437{variables}
1438
1439# Objective function
1440{objective}
1441
1442# Constraints
1443{constraints}
1444
1445# Build and solve
1446{solve_code}
1447"
1448 .to_string(),
1449 placeholders: vec![
1450 "variables".to_string(),
1451 "objective".to_string(),
1452 "constraints".to_string(),
1453 "solve_code".to_string(),
1454 ],
1455 metadata: TemplateMetadata {
1456 name: "Python Template".to_string(),
1457 description: "Generate Python code using quantrs2-tytan".to_string(),
1458 version: "1.0.0".to_string(),
1459 author: "QuantRS2".to_string(),
1460 },
1461 },
1462 );
1463
1464 templates.insert(
1465 ExportFormat::Rust,
1466 CodeTemplate {
1467 template: r"
1468// Generated quantum optimization problem
1469use quantrs2_tytan::*;
1470
1471fn main() -> Result<(), Box<dyn std::error::Error>> {{
1472 // Variables
1473 {variables}
1474
1475 // Objective function
1476 {objective}
1477
1478 // Constraints
1479 {constraints}
1480
1481 // Build and solve
1482 {solve_code}
1483
1484 Ok(())
1485}}
1486"
1487 .to_string(),
1488 placeholders: vec![
1489 "variables".to_string(),
1490 "objective".to_string(),
1491 "constraints".to_string(),
1492 "solve_code".to_string(),
1493 ],
1494 metadata: TemplateMetadata {
1495 name: "Rust Template".to_string(),
1496 description: "Generate Rust code using quantrs2-tytan".to_string(),
1497 version: "1.0.0".to_string(),
1498 author: "QuantRS2".to_string(),
1499 },
1500 },
1501 );
1502
1503 templates
1504 }
1505
1506 pub fn generate(
1508 &self,
1509 problem: &VisualProblem,
1510 format: ExportFormat,
1511 ) -> Result<String, String> {
1512 match format {
1513 ExportFormat::JSON => serde_json::to_string_pretty(problem)
1514 .map_err(|e| format!("JSON generation error: {e}")),
1515 ExportFormat::Python => self.generate_python_code(problem),
1516 ExportFormat::Rust => self.generate_rust_code(problem),
1517 _ => Err("Format not supported yet".to_string()),
1518 }
1519 }
1520
1521 fn generate_python_code(&self, problem: &VisualProblem) -> Result<String, String> {
1523 let template = self
1524 .templates
1525 .get(&ExportFormat::Python)
1526 .ok_or("Python template not found")?;
1527
1528 let mut code = template.template.clone();
1529
1530 let variables_code = problem
1532 .variables
1533 .iter()
1534 .map(|v| format!("{} = symbols(\"{}\")", v.name, v.name))
1535 .collect::<Vec<_>>()
1536 .join("\n");
1537
1538 let objective_code = if let Some(obj) = &problem.objective {
1540 match &obj.expression {
1541 ObjectiveExpression::Linear {
1542 coefficients,
1543 constant,
1544 } => {
1545 let terms: Vec<String> = coefficients
1546 .iter()
1547 .map(|(var, coef)| {
1548 if *coef == 1.0 {
1549 var.clone()
1550 } else {
1551 format!("{coef} * {var}")
1552 }
1553 })
1554 .collect();
1555
1556 if *constant == 0.0 {
1557 format!("h = {}", terms.join(" + "))
1558 } else {
1559 format!("h = {} + {}", terms.join(" + "), constant)
1560 }
1561 }
1562 _ => "h = 0 # Complex objective not implemented".to_string(),
1563 }
1564 } else {
1565 "h = 0 # No objective function".to_string()
1566 };
1567
1568 let constraints_code = problem
1570 .constraints
1571 .iter()
1572 .map(|c| format!("# Constraint: {}", c.name))
1573 .collect::<Vec<_>>()
1574 .join("\n");
1575
1576 let solve_code = r"
1578# Compile to QUBO
1579qubo, offset = Compile(h).get_qubo()
1580
1581# Choose solver
1582solver = SASampler()
1583
1584# Solve
1585result = solver.run_qubo(qubo, 100)
1586
1587# Display results
1588for r in result:
1589 print(r)
1590"
1591 .to_string();
1592
1593 code = code.replace("{variables}", &variables_code);
1594 code = code.replace("{objective}", &objective_code);
1595 code = code.replace("{constraints}", &constraints_code);
1596 code = code.replace("{solve_code}", &solve_code);
1597
1598 Ok(code)
1599 }
1600
1601 fn generate_rust_code(&self, problem: &VisualProblem) -> Result<String, String> {
1603 let template = self
1604 .templates
1605 .get(&ExportFormat::Rust)
1606 .ok_or("Rust template not found")?;
1607
1608 let mut code = template.template.clone();
1609
1610 let variables_code = problem
1612 .variables
1613 .iter()
1614 .map(|v| format!(" let {} = symbols(\"{}\");", v.name, v.name))
1615 .collect::<Vec<_>>()
1616 .join("\n");
1617
1618 let objective_code = " let h = x; // Simplified objective".to_string();
1620
1621 let constraints_code = " // Constraints not implemented in template".to_string();
1623
1624 let solve_code = r#"
1626 // Compile to QUBO
1627 let (qubo, offset) = Compile::new(&h).get_qubo()?;
1628
1629 // Choose solver
1630 let solver = SASampler::new(None);
1631
1632 // Solve
1633 let mut result = solver.run_qubo(&qubo, 100)?;
1634
1635 // Display results
1636 for r in &result {
1637 println!("{:?}", r);
1638 }"#
1639 .to_string();
1640
1641 code = code.replace("{variables}", &variables_code);
1642 code = code.replace("{objective}", &objective_code);
1643 code = code.replace("{constraints}", &constraints_code);
1644 code = code.replace("{solve_code}", &solve_code);
1645
1646 Ok(code)
1647 }
1648}
1649
1650impl ProblemHistory {
1651 pub const fn new(max_size: usize) -> Self {
1653 Self {
1654 history: Vec::new(),
1655 current: 0,
1656 max_size,
1657 }
1658 }
1659
1660 pub fn push(&mut self, entry: HistoryEntry) {
1662 self.history.truncate(self.current);
1664
1665 self.history.push(entry);
1667 self.current = self.history.len();
1668
1669 if self.history.len() > self.max_size {
1671 self.history.remove(0);
1672 self.current -= 1;
1673 }
1674 }
1675
1676 pub fn undo(&mut self) -> Option<&HistoryEntry> {
1678 if self.current > 0 {
1679 self.current -= 1;
1680 self.history.get(self.current)
1681 } else {
1682 None
1683 }
1684 }
1685
1686 pub fn redo(&mut self) -> Option<&HistoryEntry> {
1688 if self.current < self.history.len() {
1689 let entry = self.history.get(self.current);
1690 self.current += 1;
1691 entry
1692 } else {
1693 None
1694 }
1695 }
1696}
1697
1698impl fmt::Display for VisualProblem {
1699 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1700 write!(
1701 f,
1702 "Problem '{}': {} variables, {} constraints",
1703 self.metadata.name,
1704 self.variables.len(),
1705 self.constraints.len()
1706 )
1707 }
1708}
1709
1710#[cfg(test)]
1711mod tests {
1712 use super::*;
1713
1714 #[test]
1715 fn test_visual_problem_builder() -> Result<(), String> {
1716 let config = BuilderConfig::default();
1717 let mut builder = VisualProblemBuilder::new(config);
1718
1719 builder.new_problem("Test Problem")?;
1721
1722 let var1_id = builder.add_variable(
1724 "x1",
1725 VariableType::Binary,
1726 Position {
1727 x: 100.0,
1728 y: 100.0,
1729 z: None,
1730 },
1731 )?;
1732
1733 let var2_id = builder.add_variable(
1734 "x2",
1735 VariableType::Binary,
1736 Position {
1737 x: 200.0,
1738 y: 100.0,
1739 z: None,
1740 },
1741 )?;
1742
1743 assert_eq!(builder.problem.variables.len(), 2);
1744
1745 let _constraint_id = builder.add_constraint(
1747 "Sum constraint",
1748 ConstraintType::Linear {
1749 coefficients: vec![1.0, 1.0],
1750 operator: ComparisonOperator::LessEqual,
1751 rhs: 1.0,
1752 },
1753 vec![var1_id.clone(), var2_id.clone()],
1754 )?;
1755
1756 assert_eq!(builder.problem.constraints.len(), 1);
1757
1758 let mut coefficients = HashMap::new();
1760 coefficients.insert(var1_id, 1.0);
1761 coefficients.insert(var2_id, 2.0);
1762
1763 builder.set_objective(
1764 "Linear objective",
1765 ObjectiveExpression::Linear {
1766 coefficients,
1767 constant: 0.0,
1768 },
1769 OptimizationDirection::Maximize,
1770 )?;
1771
1772 assert!(builder.problem.objective.is_some());
1773
1774 builder.undo()?;
1776 assert!(builder.problem.objective.is_none());
1777
1778 builder.redo()?;
1779 assert!(builder.problem.objective.is_some());
1780
1781 let python_code = builder.generate_code(ExportFormat::Python)?;
1783 assert!(python_code.contains("symbols"));
1784 assert!(python_code.contains("SASampler"));
1785
1786 let json = builder.save_problem()?;
1788 assert!(json.contains("Test Problem"));
1789
1790 Ok(())
1791 }
1792
1793 #[test]
1794 fn test_validation() -> Result<(), String> {
1795 let mut validator = ProblemValidator::new();
1796 let mut problem = VisualProblem::new();
1797
1798 let errors = validator.validate(&problem)?;
1800 assert!(!errors.is_empty());
1801
1802 problem.variables.push(VisualVariable {
1804 id: "var1".to_string(),
1805 name: "x1".to_string(),
1806 var_type: VariableType::Binary,
1807 domain: VariableDomain::Binary,
1808 position: Position {
1809 x: 0.0,
1810 y: 0.0,
1811 z: None,
1812 },
1813 visual_properties: VariableVisualProperties {
1814 color: "#000000".to_string(),
1815 size: 10.0,
1816 shape: VariableShape::Circle,
1817 visible: true,
1818 label: LabelSettings {
1819 show: true,
1820 text: None,
1821 font_size: 12.0,
1822 position: LabelPosition::Bottom,
1823 },
1824 },
1825 description: String::new(),
1826 groups: Vec::new(),
1827 });
1828
1829 let errors = validator.validate(&problem)?;
1831 assert!(errors
1832 .iter()
1833 .any(|e| matches!(e.severity, ValidationSeverity::Warning)));
1834
1835 Ok(())
1836 }
1837}