server_runner/
server_runner.rs1use async_trait::async_trait;
2use ndarray::ArrayD;
3use std::collections::HashMap;
4use std::net::SocketAddr;
5use tonic::transport::Server;
6
7use philote_mdo::{
8 philote_info::{
9 discipline_service_server::DisciplineServiceServer,
10 explicit_service_server::ExplicitServiceServer, VariableMetaData, VariableType,
11 },
12 server::ExplicitServer,
13 traits::{Discipline, ExplicitDiscipline},
14 ArrayMap, PartialMap, PhiloteError, Result,
15};
16
17pub struct ParaboloidDiscipline {
19 variables: Vec<VariableMetaData>,
20 partials: Vec<(String, String)>,
21 options: HashMap<String, String>,
22}
23
24impl ParaboloidDiscipline {
25 pub fn new() -> Self {
26 Self {
27 variables: Vec::new(),
28 partials: Vec::new(),
29 options: HashMap::new(),
30 }
31 }
32}
33
34impl Discipline for ParaboloidDiscipline {
35 fn name(&self) -> &str {
36 "ParaboloidServer"
37 }
38
39 fn version(&self) -> &str {
40 "1.0.0"
41 }
42
43 fn is_continuous(&self) -> bool {
44 true
45 }
46
47 fn is_differentiable(&self) -> bool {
48 true
49 }
50
51 fn provides_gradients(&self) -> bool {
52 true
53 }
54
55 fn initialize(&mut self) -> Result<()> {
56 self.add_option("a", "double")?;
57 self.add_option("b", "double")?;
58 Ok(())
59 }
60
61 fn add_input(&mut self, name: &str, shape: &[usize], units: &str) -> Result<()> {
62 let var_meta = VariableMetaData {
63 r#type: VariableType::KInput as i32,
64 name: name.to_string(),
65 shape: shape.iter().map(|&s| s as i64).collect(),
66 units: units.to_string(),
67 dynamic_shape: false,
68 };
69 self.variables.push(var_meta);
70 Ok(())
71 }
72
73 fn add_output(&mut self, name: &str, shape: &[usize], units: &str) -> Result<()> {
74 let var_meta = VariableMetaData {
75 r#type: VariableType::KOutput as i32,
76 name: name.to_string(),
77 shape: shape.iter().map(|&s| s as i64).collect(),
78 units: units.to_string(),
79 dynamic_shape: false,
80 };
81 self.variables.push(var_meta);
82 Ok(())
83 }
84
85 fn add_option(&mut self, name: &str, option_type: &str) -> Result<()> {
86 self.options
87 .insert(name.to_string(), option_type.to_string());
88 Ok(())
89 }
90
91 fn set_options(&mut self, _options: &HashMap<String, serde_json::Value>) -> Result<()> {
92 Ok(())
93 }
94
95 fn setup(&mut self) -> Result<()> {
96 self.variables.clear();
97 self.add_input("x", &[1], "")?;
98 self.add_input("y", &[1], "")?;
99 self.add_output("f", &[1], "")?;
100 Ok(())
101 }
102
103 fn declare_partials(&mut self, func: &str, var: &str) -> Result<()> {
104 self.partials.push((func.to_string(), var.to_string()));
105 Ok(())
106 }
107
108 fn setup_partials(&mut self) -> Result<()> {
109 self.declare_partials("f", "x")?;
110 self.declare_partials("f", "y")?;
111 Ok(())
112 }
113
114 fn get_variable_definitions(&self) -> Result<Vec<VariableMetaData>> {
115 Ok(self.variables.clone())
116 }
117
118 fn get_partials_definitions(&self) -> Result<Vec<(String, String)>> {
119 Ok(self.partials.clone())
120 }
121
122 fn get_available_options(&self) -> Result<HashMap<String, String>> {
123 Ok(self.options.clone())
124 }
125}
126
127#[async_trait]
128impl ExplicitDiscipline for ParaboloidDiscipline {
129 async fn compute(&self, inputs: &ArrayMap) -> Result<ArrayMap> {
130 let x = inputs
131 .get("x")
132 .ok_or_else(|| PhiloteError::VariableNotFound("x".to_string()))?;
133
134 let y = inputs
135 .get("y")
136 .ok_or_else(|| PhiloteError::VariableNotFound("y".to_string()))?;
137
138 if x.len() != 1 || y.len() != 1 {
139 return Err(PhiloteError::array_error("Expected scalar inputs"));
140 }
141
142 let x_val = x[[0]];
143 let y_val = y[[0]];
144
145 let f_val = (x_val - 3.0).powi(2) + x_val * y_val + (y_val + 4.0).powi(2) - 3.0;
147
148 println!("🧮 Computing: x={}, y={} => f={}", x_val, y_val, f_val);
149
150 let mut outputs = HashMap::new();
151 let f_array = ArrayD::from_elem(vec![1], f_val);
152 outputs.insert("f".to_string(), f_array);
153
154 Ok(outputs)
155 }
156
157 async fn compute_partials(&self, inputs: &ArrayMap) -> Result<PartialMap> {
158 let x = inputs
159 .get("x")
160 .ok_or_else(|| PhiloteError::VariableNotFound("x".to_string()))?;
161
162 let y = inputs
163 .get("y")
164 .ok_or_else(|| PhiloteError::VariableNotFound("y".to_string()))?;
165
166 if x.len() != 1 || y.len() != 1 {
167 return Err(PhiloteError::array_error("Expected scalar inputs"));
168 }
169
170 let x_val = x[[0]];
171 let y_val = y[[0]];
172
173 let df_dx = 2.0 * (x_val - 3.0) + y_val;
175
176 let df_dy = x_val + 2.0 * (y_val + 4.0);
178
179 println!("📊 Computing gradients: df/dx={}, df/dy={}", df_dx, df_dy);
180
181 let mut partials = HashMap::new();
182 partials.insert(
183 ("f".to_string(), "x".to_string()),
184 ArrayD::from_elem(vec![1], df_dx),
185 );
186 partials.insert(
187 ("f".to_string(), "y".to_string()),
188 ArrayD::from_elem(vec![1], df_dy),
189 );
190
191 Ok(partials)
192 }
193}
194
195#[tokio::main]
196async fn main() -> Result<()> {
197 println!("🚀 Starting Philote Rust Server Example");
198
199 let mut discipline = ParaboloidDiscipline::new();
201 discipline.initialize()?;
202 discipline.setup()?;
203 discipline.setup_partials()?;
204
205 let server = ExplicitServer::new(discipline).with_verbose(true);
207
208 let addr: SocketAddr = "127.0.0.1:50051"
210 .parse()
211 .map_err(|e| PhiloteError::config_error(format!("Invalid address: {}", e)))?;
212
213 println!("🌐 Server listening on: {}", addr);
214 println!("📡 Ready to accept client connections!");
215 println!("💡 You can now run the client_example to test the connection.");
216
217 let server_arc = std::sync::Arc::new(server);
220
221 Server::builder()
222 .add_service(ExplicitServiceServer::from_arc(server_arc.clone()))
223 .add_service(DisciplineServiceServer::from_arc(server_arc))
224 .serve(addr)
225 .await
226 .map_err(|e| PhiloteError::config_error(format!("Server failed: {}", e)))?;
227
228 Ok(())
229}