MUQ  0.4.3
umbridge.h
Go to the documentation of this file.
1 #ifndef UMBRIDGE
2 #define UMBRIDGE
3 
4 // Increase timeout to allow for long-running models.
5 // This should be (to be on the safe side) significantly greater than the maximum time your model may take
6 #define CPPHTTPLIB_READ_TIMEOUT_SECOND 60*60
7 
8 #include <string>
9 #include <vector>
10 
11 
12 #include "json.h"
13 #include "httplib.h"
14 
16 
17 namespace umbridge {
18 
19  class Model {
20  public:
21  Model(std::string name) : name(name) {}
22 
23  virtual std::vector<std::size_t> GetInputSizes(const json& config_json = json()) const = 0;
24  virtual std::vector<std::size_t> GetOutputSizes(const json& config_json = json()) const = 0;
25 
26  virtual std::vector<std::vector<double>> Evaluate(const std::vector<std::vector<double>>& inputs,
27  json config_json = json()) {
28  (void)inputs; (void)config_json; // Avoid unused argument warnings
29  throw std::runtime_error("Evaluate was called, but not implemented by model!");
30  }
31 
32  virtual std::vector<double> Gradient(unsigned int outWrt,
33  unsigned int inWrt,
34  const std::vector<std::vector<double>>& inputs,
35  const std::vector<double>& sens,
36  json config_json = json()) {
37  (void)outWrt; (void)inWrt; (void)inputs; (void)sens; (void)config_json; // Avoid unused argument warnings
38  throw std::runtime_error("Gradient was called, but not implemented by model!");
39  }
40 
41  virtual std::vector<double> ApplyJacobian(unsigned int outWrt,
42  unsigned int inWrt,
43  const std::vector<std::vector<double>>& inputs,
44  const std::vector<double>& vec,
45  json config_json = json()) {
46  (void)outWrt; (void)inWrt; (void)inputs; (void)vec; (void)config_json; // Avoid unused argument warnings
47  throw std::runtime_error("ApplyJacobian was called, but not implemented by model!");
48  }
49 
50  virtual std::vector<double> ApplyHessian(unsigned int outWrt,
51  unsigned int inWrt1,
52  unsigned int inWrt2,
53  const std::vector<std::vector<double>>& inputs,
54  const std::vector<double>& sens,
55  const std::vector<double>& vec,
56  json config_json = json()) {
57  (void)outWrt; (void)inWrt1; (void)inWrt2; (void)inputs; (void)sens; (void)vec; (void)config_json; // Avoid unused argument warnings
58  throw std::runtime_error("ApplyHessian was called, but not implemented by model!");
59  }
60 
61  virtual bool SupportsEvaluate() {return false;}
62  virtual bool SupportsGradient() {return false;}
63  virtual bool SupportsApplyJacobian() {return false;}
64  virtual bool SupportsApplyHessian() {return false;}
65 
66  std::string GetName() const {return name;}
67 
68  protected:
69  std::string name;
70  };
71 
72  // Client-side Model connecting to a server for the actual evaluations etc.
73  class HTTPModel : public Model {
74  public:
75 
76  HTTPModel(std::string host, std::string name, httplib::Headers headers = httplib::Headers())
77  : Model(name), cli(host.c_str()), headers(headers)
78  {
79  if (auto res = cli.Get("/Info", headers)) {
80  json response = json::parse(res->body);
81 
82  if (response.value<double>("protocolVersion",0) != 1.0)
83  throw std::runtime_error("Model protocol version not supported!");
84 
85  // Check if requested model is available on server
86  std::vector<std::string> models = response["models"];
87  if (std::find(models.begin(), models.end(), name) == models.end()) {
88  std::string model_names = "";
89  for (auto& m : models) {
90  model_names += "'" + m + "' ";
91  }
92  throw std::runtime_error("Model " + name + " not found on server! Available models: " + model_names + ".");
93  }
94 
95  } else {
96  throw std::runtime_error("GET Info failed with error type '" + to_string(res.error()) + "'");
97  }
98 
99  json request_body;
100  request_body["name"] = name;
101 
102  if (auto res = cli.Post("/ModelInfo", headers, request_body.dump(), "application/json")) {
103  json response = json::parse(res->body);
104 
105  json supported_features = response.at("support");
106  supportsEvaluate = supported_features.value("Evaluate", false);
107  supportsGradient = supported_features.value("Gradient", false);
108  supportsApplyJacobian = supported_features.value("ApplyJacobian", false);
109  supportsApplyHessian = supported_features.value("ApplyHessian", false);
110  } else {
111  throw std::runtime_error("POST ModelInfo failed with error type '" + to_string(res.error()) + "'");
112  }
113  }
114 
115  std::vector<std::size_t> GetInputSizes(const json& config_json = json()) const override {
116 
117  json request_body;
118  request_body["name"] = name;
119  if (!config_json.empty())
120  request_body["config"] = config_json;
121 
122  if (auto res = cli.Post("/InputSizes", headers, request_body.dump(), "application/json")) {
123  json response_body = json::parse(res->body);
124  std::vector<std::size_t> outputvec = response_body["inputSizes"].get<std::vector<std::size_t>>();
125  return outputvec;
126  } else {
127  throw std::runtime_error("POST InputSizes failed with error type '" + to_string(res.error()) + "'");
128  return std::vector<std::size_t>(0);
129  }
130  }
131 
132  std::vector<std::size_t> GetOutputSizes(const json& config_json = json()) const override {
133 
134  json request_body;
135  request_body["name"] = name;
136  if (!config_json.empty())
137  request_body["config"] = config_json;
138 
139  if (auto res = cli.Post("/OutputSizes", headers, request_body.dump(), "application/json")) {
140  json response_body = json::parse(res->body);
141  std::vector<std::size_t> outputvec = response_body["outputSizes"].get<std::vector<std::size_t>>();
142  return outputvec;
143  } else {
144  throw std::runtime_error("POST OutputSizes failed with error type '" + to_string(res.error()) + "'");
145  return std::vector<std::size_t>(0);
146  }
147  }
148 
149  std::vector<std::vector<double>> Evaluate(const std::vector<std::vector<double>>& inputs, json config_json = json()) override {
150 
151  json request_body;
152  request_body["name"] = name;
153 
154  for (std::size_t i = 0; i < inputs.size(); i++) {
155  request_body["input"][i] = inputs[i];
156  }
157  if (!config_json.empty())
158  request_body["config"] = config_json;
159 
160  if (auto res = cli.Post("/Evaluate", headers, request_body.dump(), "application/json")) {
161  json response_body = json::parse(res->body);
162  throw_if_error_in_response(response_body);
163 
164  std::vector<std::vector<double>> outputs(response_body["output"].size());
165  for (std::size_t i = 0; i < response_body["output"].size(); i++) {
166  outputs[i] = response_body["output"][i].get<std::vector<double>>();
167  }
168  return outputs;
169  } else {
170  throw std::runtime_error("POST Evaluate failed with error type '" + to_string(res.error()) + "'");
171  }
172  }
173 
174  std::vector<double> Gradient(unsigned int outWrt,
175  unsigned int inWrt,
176  const std::vector<std::vector<double>>& inputs,
177  const std::vector<double>& sens,
178  json config_json = json()) override
179  {
180 
181  json request_body;
182  request_body["name"] = name;
183  request_body["outWrt"] = outWrt;
184  request_body["inWrt"] = inWrt;
185  for (std::size_t i = 0; i < inputs.size(); i++) {
186  request_body["input"][i] = inputs[i];
187  }
188  request_body["sens"] = sens;
189  if (!config_json.empty())
190  request_body["config"] = config_json;
191 
192  if (auto res = cli.Post("/Gradient", headers, request_body.dump(), "application/json")) {
193  json response_body = json::parse(res->body);
194  throw_if_error_in_response(response_body);
195 
196  return response_body["output"].get<std::vector<double>>();
197  } else {
198  throw std::runtime_error("POST Gradient failed with error type '" + to_string(res.error()) + "'");
199  }
200  }
201 
202  std::vector<double> ApplyJacobian(unsigned int outWrt,
203  unsigned int inWrt,
204  const std::vector<std::vector<double>>& inputs,
205  const std::vector<double>& vec,
206  json config_json = json()) override {
207 
208  json request_body;
209  request_body["name"] = name;
210  request_body["outWrt"] = outWrt;
211  request_body["inWrt"] = inWrt;
212  for (std::size_t i = 0; i < inputs.size(); i++) {
213  request_body["input"][i] = inputs[i];
214  }
215  request_body["vec"] = vec;
216  if (!config_json.empty())
217  request_body["config"] = config_json;
218 
219  if (auto res = cli.Post("/ApplyJacobian", headers, request_body.dump(), "application/json")) {
220  json response_body = json::parse(res->body);
221  throw_if_error_in_response(response_body);
222 
223  return response_body["output"].get<std::vector<double>>();
224  } else {
225  throw std::runtime_error("POST ApplyJacobian failed with error type '" + to_string(res.error()) + "'");
226  }
227  }
228 
229  std::vector<double> ApplyHessian(unsigned int outWrt,
230  unsigned int inWrt1,
231  unsigned int inWrt2,
232  const std::vector<std::vector<double>>& inputs,
233  const std::vector<double>& sens,
234  const std::vector<double>& vec,
235  json config_json = json()) override {
236 
237  json request_body;
238  request_body["name"] = name;
239  request_body["outWrt"] = outWrt;
240  request_body["inWrt1"] = inWrt1;
241  request_body["inWrt2"] = inWrt2;
242  for (std::size_t i = 0; i < inputs.size(); i++) {
243  request_body["input"][i] = inputs[i];
244  }
245  request_body["sens"] = sens;
246  request_body["vec"] = vec;
247  if (!config_json.empty())
248  request_body["config"] = config_json;
249 
250  if (auto res = cli.Post("/ApplyHessian", headers, request_body.dump(), "application/json")) {
251  json response_body = json::parse(res->body);
252  throw_if_error_in_response(response_body);
253 
254  return response_body["output"].get<std::vector<double>>();
255  } else {
256  throw std::runtime_error("POST ApplyHessian failed with error type '" + to_string(res.error()) + "'");
257  }
258  }
259 
260  bool SupportsEvaluate() override {
261  return supportsEvaluate;
262  }
263  bool SupportsGradient() override {
264  return supportsGradient;
265  }
266  bool SupportsApplyJacobian() override {
267  return supportsApplyJacobian;
268  }
269  bool SupportsApplyHessian() override {
270  return supportsApplyHessian;
271  }
272 
273  private:
274 
275  mutable httplib::Client cli;
276  httplib::Headers headers;
277 
278  bool supportsEvaluate = false;
279  bool supportsGradient = false;
280  bool supportsApplyJacobian = false;
281  bool supportsApplyHessian = false;
282 
283  // Throw error if response contains error message
284  void throw_if_error_in_response(const json& response_body) {
285  if (response_body.find("error") != response_body.end()) {
286  throw std::runtime_error("Model server returned error of type " + response_body["error"]["type"].get<std::string>() + ", message: " + response_body["error"]["message"].get<std::string>());
287  }
288  }
289 
290  };
291 
292  // Check if inputs dimensions match model's expected input size and return error in httplib response
293  bool check_input_sizes(const std::vector<std::vector<double>>& inputs, const json& config_json, const Model& model, httplib::Response& res) {
294  if (inputs.size() != model.GetOutputSizes(config_json).size()) {
295  json response_body;
296  response_body["error"]["type"] = "InvalidInput";
297  response_body["error"]["message"] = "Number of inputs does not match number of model inputs. Expected " + std::to_string(model.GetInputSizes(config_json).size()) + " but got " + std::to_string(inputs.size());
298  res.set_content(response_body.dump(), "application/json");
299  res.status = 400;
300  return false;
301  }
302  for (std::size_t i = 0; i < inputs.size(); i++) {
303  if (inputs[i].size() != model.GetInputSizes(config_json)[i]) {
304  json response_body;
305  response_body["error"]["type"] = "InvalidInput";
306  response_body["error"]["message"] = "Input size mismatch! In input " + std::to_string(i) + " model expected size " + std::to_string(model.GetInputSizes(config_json)[i]) + " but got " + std::to_string(inputs[i].size());
307  res.set_content(response_body.dump(), "application/json");
308  res.status = 400;
309  return false;
310  }
311  }
312  return true;
313  }
314 
315  // Check if sensitivity vector's dimension matches correct model output size and return error in httplib response
316  bool check_sensitivity_size(const std::vector<double>& sens, int outWrt, const json& config_json, const Model& model, httplib::Response& res) {
317  if (sens.size() != model.GetOutputSizes(config_json)[outWrt]) {
318  json response_body;
319  response_body["error"]["type"] = "InvalidInput";
320  response_body["error"]["message"] = "Sensitivity vector size mismatch! Expected " + std::to_string(model.GetOutputSizes(config_json)[outWrt]) + " but got " + std::to_string(sens.size());
321  res.set_content(response_body.dump(), "application/json");
322  res.status = 400;
323  return false;
324  }
325  return true;
326  }
327 
328  // Check if vector's dimension matches correct model output size and return error in httplib response
329  bool check_vector_size(const std::vector<double>& vec, int inWrt, const json& config_json, const Model& model, httplib::Response& res) {
330  if (vec.size() != model.GetInputSizes(config_json)[inWrt]) {
331  json response_body;
332  response_body["error"]["type"] = "InvalidInput";
333  response_body["error"]["message"] = "Vector size mismatch! Expected " + std::to_string(model.GetInputSizes(config_json)[inWrt]) + " but got " + std::to_string(vec.size());
334  res.set_content(response_body.dump(), "application/json");
335  res.status = 400;
336  return false;
337  }
338  return true;
339  }
340 
341  // Check if outputs dimensions match model's expected output size and return error in httplib response
342  bool check_output_sizes(const std::vector<std::vector<double>>& outputs, const json& config_json, const Model& model, httplib::Response& res) {
343  if (outputs.size() != model.GetOutputSizes(config_json).size()) {
344  json response_body;
345  response_body["error"]["type"] = "InvalidOutput";
346  response_body["error"]["message"] = "Number of outputs declared by model does not match number of outputs returned by model. Model declared " + std::to_string(model.GetOutputSizes(config_json).size()) + " but returned " + std::to_string(outputs.size());
347  res.set_content(response_body.dump(), "application/json");
348  res.status = 500;
349  return false;
350  }
351  for (std::size_t i = 0; i < outputs.size(); i++) {
352  if (outputs[i].size() != model.GetOutputSizes(config_json)[i]) {
353  json response_body;
354  response_body["error"]["type"] = "InvalidOutput";
355  response_body["error"]["message"] = "Output size mismatch! In output " + std::to_string(i) + " model declared size " + std::to_string(model.GetOutputSizes(config_json)[i]) + " but returned " + std::to_string(outputs[i].size());
356  res.set_content(response_body.dump(), "application/json");
357  res.status = 500;
358  return false;
359  }
360  }
361  return true;
362  }
363 
364  // Check if inWrt is between zero and model's input size inWrt and return error in httplib response
365  bool check_input_wrt(int inWrt, const json& config_json, const Model& model, httplib::Response& res) {
366  if (inWrt < 0 || inWrt >= (int)model.GetInputSizes(config_json).size()) {
367  json response_body;
368  response_body["error"]["type"] = "InvalidInput";
369  response_body["error"]["message"] = "Input inWrt out of range! Expected between 0 and " + std::to_string(model.GetInputSizes(config_json).size() - 1) + " but got " + std::to_string(inWrt);
370  res.set_content(response_body.dump(), "application/json");
371  res.status = 400;
372  return false;
373  }
374  return true;
375  }
376 
377  // Check if outWrt is between zero and model's output size outWrt and return error in httplib response
378  bool check_output_wrt(int outWrt, const json& config_json, const Model& model, httplib::Response& res) {
379  if (outWrt < 0 || outWrt >= (int)model.GetOutputSizes(config_json).size()) {
380  json response_body;
381  response_body["error"]["type"] = "InvalidInput";
382  response_body["error"]["message"] = "Input outWrt out of range! Expected between 0 and " + std::to_string(model.GetOutputSizes(config_json).size() - 1) + " but got " + std::to_string(outWrt);
383  res.set_content(response_body.dump(), "application/json");
384  res.status = 400;
385  return false;
386  }
387  return true;
388  }
389 
390  // Construct response for unsupported feature
391  void write_unsupported_feature_response(httplib::Response& res, std::string feature) {
392  json response_body;
393  response_body["error"]["type"] = "UnsupportedFeature";
394  response_body["error"]["message"] = "Feature '" + feature + "' is not supported by this model";
395  res.set_content(response_body.dump(), "application/json");
396  res.status = 400;
397  }
398 
399  // Get model from name
400  Model& get_model_from_name(std::vector<Model*>& models, std::string name) {
401  for (auto& model : models) {
402  if (model->GetName() == name) {
403  return *model;
404  }
405  }
406  throw std::runtime_error("Model not found");
407  }
408 
409  // Check if model exists and return error in httplib response
410  bool check_model_exists(std::vector<Model*>& models, std::string name, httplib::Response& res) {
411  try {
412  get_model_from_name(models, name);
413  } catch (std::runtime_error& e) {
414  json response_body;
415  response_body["error"]["type"] = "ModelNotFound";
416  response_body["error"]["message"] = "Model '" + name + "' not supported by this server!";
417  res.set_content(response_body.dump(), "application/json");
418  res.status = 400;
419  return false;
420  }
421  return true;
422  }
423 
424  // Provides access to a model via network
425  void serveModels(std::vector<Model*> models, std::string host, int port) {
426 
427  httplib::Server svr;
428  std::mutex model_mutex; // Ensure the underlying model is only called sequentially
429 
430  svr.Post("/Evaluate", [&](const httplib::Request &req, httplib::Response &res) {
431  json request_body = json::parse(req.body);
432  if (!check_model_exists(models, request_body["name"], res))
433  return;
434  Model& model = get_model_from_name(models, request_body["name"]);
435 
436  if (!model.SupportsEvaluate()) {
437  write_unsupported_feature_response(res, "Evaluate");
438  return;
439  }
440 
441  std::vector<std::vector<double>> inputs(request_body["input"].size());
442  for (std::size_t i = 0; i < inputs.size(); i++) {
443  inputs[i] = request_body["input"][i].get<std::vector<double>>();
444  }
445 
446  json empty_default_config;
447  json config_json = request_body.value("config", empty_default_config);
448 
449  if (!check_input_sizes(inputs, config_json, model, res))
450  return;
451 
452  const std::lock_guard<std::mutex> model_lock(model_mutex);
453  std::vector<std::vector<double>> outputs = model.Evaluate(inputs, config_json);
454 
455  if (!check_output_sizes(outputs, config_json, model, res))
456  return;
457 
458  json response_body;
459  for (std::size_t i = 0; i < outputs.size(); i++) {
460  response_body["output"][i] = outputs[i];
461  }
462 
463  res.set_content(response_body.dump(), "application/json");
464  });
465 
466  svr.Post("/Gradient", [&](const httplib::Request &req, httplib::Response &res) {
467  json request_body = json::parse(req.body);
468  if (!check_model_exists(models, request_body["name"], res))
469  return;
470  Model& model = get_model_from_name(models, request_body["name"]);
471 
472  if (!model.SupportsGradient()) {
473  write_unsupported_feature_response(res, "Gradient");
474  return;
475  }
476 
477  unsigned int inWrt = request_body.at("inWrt");
478  unsigned int outWrt = request_body.at("outWrt");
479 
480  std::vector<std::vector<double>> inputs(request_body["input"].size());
481  for (std::size_t i = 0; i < inputs.size(); i++) {
482  inputs[i] = request_body["input"][i].get<std::vector<double>>();
483  }
484 
485  std::vector<double> sens = request_body.at("sens");
486 
487  json empty_default_config;
488  json config_json = request_body.value("config", empty_default_config);
489 
490  if (!check_input_wrt(inWrt, config_json, model, res))
491  return;
492  if (!check_output_wrt(outWrt, config_json, model, res))
493  return;
494  if (!check_input_sizes(inputs, config_json, model, res))
495  return;
496  if (!check_sensitivity_size(sens, outWrt, config_json, model, res))
497  return;
498 
499  const std::lock_guard<std::mutex> model_lock(model_mutex);
500  std::vector<double> gradient = model.Gradient(outWrt, inWrt, inputs, sens, config_json);
501 
502  json response_body;
503  response_body["output"] = gradient;
504 
505  res.set_content(response_body.dump(), "application/json");
506  });
507 
508  svr.Post("/ApplyJacobian", [&](const httplib::Request &req, httplib::Response &res) {
509  json request_body = json::parse(req.body);
510  if (!check_model_exists(models, request_body["name"], res))
511  return;
512  Model& model = get_model_from_name(models, request_body["name"]);
513 
514  if (!model.SupportsApplyJacobian()) {
515  write_unsupported_feature_response(res, "ApplyJacobian");
516  return;
517  }
518 
519  unsigned int inWrt = request_body.at("inWrt");
520  unsigned int outWrt = request_body.at("outWrt");
521 
522  std::vector<std::vector<double>> inputs(request_body["input"].size());
523  for (std::size_t i = 0; i < inputs.size(); i++) {
524  inputs[i] = request_body["input"][i].get<std::vector<double>>();
525  }
526 
527  std::vector<double> vec = request_body.at("vec");
528 
529  json empty_default_config;
530  json config_json = request_body.value("config", empty_default_config);
531 
532  if (!check_input_wrt(inWrt, config_json, model, res))
533  return;
534  if (!check_output_wrt(outWrt, config_json, model, res))
535  return;
536  if (!check_input_sizes(inputs, config_json, model, res))
537  return;
538  if (!check_vector_size(vec, inWrt, config_json, model, res))
539  return;
540 
541  const std::lock_guard<std::mutex> model_lock(model_mutex);
542  std::vector<double> jacobian_action = model.ApplyJacobian(outWrt, inWrt, inputs, vec, config_json);
543 
544  json response_body;
545  response_body["output"] = jacobian_action;
546 
547  res.set_content(response_body.dump(), "application/json");
548  });
549 
550  svr.Post("/ApplyHessian", [&](const httplib::Request &req, httplib::Response &res) {
551  json request_body = json::parse(req.body);
552  if (!check_model_exists(models, request_body["name"], res))
553  return;
554  Model& model = get_model_from_name(models, request_body["name"]);
555 
556  if (!model.SupportsApplyHessian()) {
557  write_unsupported_feature_response(res, "ApplyHessian");
558  return;
559  }
560 
561  unsigned int outWrt = request_body.at("outWrt");
562  unsigned int inWrt1 = request_body.at("inWrt1");
563  unsigned int inWrt2 = request_body.at("inWrt2");
564 
565  std::vector<std::vector<double>> inputs(request_body["input"].size());
566  for (std::size_t i = 0; i < inputs.size(); i++) {
567  inputs[i] = request_body["input"][i].get<std::vector<double>>();
568  }
569 
570  std::vector<double> sens = request_body.at("sens");
571  std::vector<double> vec = request_body.at("vec");
572 
573  json empty_default_config;
574  json config_json = request_body.value("config", empty_default_config);
575 
576  if (!check_input_wrt(inWrt1, config_json, model, res))
577  return;
578  if (!check_input_wrt(inWrt2, config_json, model, res))
579  return;
580  if (!check_output_wrt(outWrt, config_json, model, res))
581  return;
582  if (!check_input_sizes(inputs, config_json, model, res))
583  return;
584  if (!check_sensitivity_size(sens, outWrt, config_json, model, res))
585  return;
586 
587  const std::lock_guard<std::mutex> model_lock(model_mutex);
588  std::vector<double> hessian_action = model.ApplyHessian(outWrt, inWrt1, inWrt2, inputs, sens, vec, config_json);
589 
590  json response_body;
591  response_body["output"] = hessian_action;
592 
593  res.set_content(response_body.dump(), "application/json");
594  });
595 
596  svr.Get("/Info", [&](const httplib::Request &, httplib::Response &res) {
597  json response_body;
598  response_body["protocolVersion"] = 1.0;
599  std::vector<std::string> model_names;
600  for (auto& model : models) {
601  model_names.push_back(model->GetName());
602  }
603  response_body["models"] = model_names;
604 
605  res.set_content(response_body.dump(), "application/json");
606  });
607 
608  svr.Post("/ModelInfo", [&](const httplib::Request &req, httplib::Response &res) {
609  json request_body = json::parse(req.body);
610  if (!check_model_exists(models, request_body["name"], res))
611  return;
612  Model& model = get_model_from_name(models, request_body["name"]);
613 
614  json response_body;
615  response_body["support"] = {};
616  response_body["support"]["Evaluate"] = model.SupportsEvaluate();
617  response_body["support"]["Gradient"] = model.SupportsGradient();
618  response_body["support"]["ApplyJacobian"] = model.SupportsApplyJacobian();
619  response_body["support"]["ApplyHessian"] = model.SupportsApplyHessian();
620 
621  res.set_content(response_body.dump(), "application/json");
622  });
623 
624  svr.Post("/InputSizes", [&](const httplib::Request &req, httplib::Response &res) {
625  json request_body = json::parse(req.body);
626  if (!check_model_exists(models, request_body["name"], res))
627  return;
628  Model& model = get_model_from_name(models, request_body["name"]);
629 
630  json empty_default_config;
631  json config_json = request_body.value("config", empty_default_config);
632 
633  json response_body;
634  response_body["inputSizes"] = model.GetInputSizes(config_json);
635 
636  res.set_content(response_body.dump(), "application/json");
637  });
638 
639  svr.Post("/OutputSizes", [&](const httplib::Request &req, httplib::Response &res) {
640  json request_body = json::parse(req.body);
641  if (!check_model_exists(models, request_body["name"], res))
642  return;
643  Model& model = get_model_from_name(models, request_body["name"]);
644 
645  json empty_default_config;
646  json config_json = request_body.value("config", empty_default_config);
647 
648  json response_body;
649  response_body["outputSizes"] = model.GetOutputSizes(config_json);
650 
651  res.set_content(response_body.dump(), "application/json");
652  });
653 
654  std::cout << "Listening on port " << port << "..." << std::endl;
655  svr.listen(host.c_str(), port);
656  std::cout << "Quit" << std::endl;
657  }
658 
659 }
660 
661 #endif
a class to store JSON values
Definition: json.h:16658
basic_json get() const
get special-case overload
Definition: json.h:19336
ValueType value(const typename object_t::key_type &key, const ValueType &default_value) const
access specified object element with default value
Definition: json.h:20279
static JSON_HEDLEY_WARN_UNUSED_RESULT basic_json parse(InputType &&i, const parser_callback_t cb=nullptr, const bool allow_exceptions=true, const bool ignore_comments=false)
deserialize from a compatible input
Definition: json.h:23136
size_type size() const noexcept
returns the number of elements
Definition: json.h:21494
string_t dump(const int indent=-1, const char indent_char=' ', const bool ensure_ascii=false, const error_handler_t error_handler=error_handler_t::strict) const
serialization
Definition: json.h:18759
reference at(size_type idx)
access specified array element with bounds checking
Definition: json.h:19793
iterator end() noexcept
returns an iterator to one past the last element
Definition: json.h:21037
void push_back(basic_json &&val)
add an object to an array
Definition: json.h:21713
iterator find(KeyT &&key)
find an element in a JSON object
Definition: json.h:20812
bool supportsApplyHessian
Definition: umbridge.h:281
bool SupportsApplyHessian() override
Definition: umbridge.h:269
httplib::Client cli
Definition: umbridge.h:275
bool SupportsEvaluate() override
Definition: umbridge.h:260
bool SupportsGradient() override
Definition: umbridge.h:263
bool SupportsApplyJacobian() override
Definition: umbridge.h:266
std::vector< std::vector< double > > Evaluate(const std::vector< std::vector< double >> &inputs, json config_json=json()) override
Definition: umbridge.h:149
std::vector< std::size_t > GetOutputSizes(const json &config_json=json()) const override
Definition: umbridge.h:132
std::vector< double > Gradient(unsigned int outWrt, unsigned int inWrt, const std::vector< std::vector< double >> &inputs, const std::vector< double > &sens, json config_json=json()) override
Definition: umbridge.h:174
void throw_if_error_in_response(const json &response_body)
Definition: umbridge.h:284
httplib::Headers headers
Definition: umbridge.h:276
std::vector< double > ApplyHessian(unsigned int outWrt, unsigned int inWrt1, unsigned int inWrt2, const std::vector< std::vector< double >> &inputs, const std::vector< double > &sens, const std::vector< double > &vec, json config_json=json()) override
Definition: umbridge.h:229
bool supportsApplyJacobian
Definition: umbridge.h:280
std::vector< double > ApplyJacobian(unsigned int outWrt, unsigned int inWrt, const std::vector< std::vector< double >> &inputs, const std::vector< double > &vec, json config_json=json()) override
Definition: umbridge.h:202
HTTPModel(std::string host, std::string name, httplib::Headers headers=httplib::Headers())
Definition: umbridge.h:76
std::vector< std::size_t > GetInputSizes(const json &config_json=json()) const override
Definition: umbridge.h:115
virtual bool SupportsGradient()
Definition: umbridge.h:62
virtual std::vector< double > ApplyHessian(unsigned int outWrt, unsigned int inWrt1, unsigned int inWrt2, const std::vector< std::vector< double >> &inputs, const std::vector< double > &sens, const std::vector< double > &vec, json config_json=json())
Definition: umbridge.h:50
std::string name
Definition: umbridge.h:69
virtual bool SupportsApplyHessian()
Definition: umbridge.h:64
virtual std::vector< std::size_t > GetInputSizes(const json &config_json=json()) const =0
Model(std::string name)
Definition: umbridge.h:21
virtual std::vector< double > Gradient(unsigned int outWrt, unsigned int inWrt, const std::vector< std::vector< double >> &inputs, const std::vector< double > &sens, json config_json=json())
Definition: umbridge.h:32
virtual std::vector< std::vector< double > > Evaluate(const std::vector< std::vector< double >> &inputs, json config_json=json())
Definition: umbridge.h:26
virtual std::vector< double > ApplyJacobian(unsigned int outWrt, unsigned int inWrt, const std::vector< std::vector< double >> &inputs, const std::vector< double > &vec, json config_json=json())
Definition: umbridge.h:41
virtual std::vector< std::size_t > GetOutputSizes(const json &config_json=json()) const =0
std::string GetName() const
Definition: umbridge.h:66
virtual bool SupportsApplyJacobian()
Definition: umbridge.h:63
virtual bool SupportsEvaluate()
Definition: umbridge.h:61
basic_json<> json
default JSON class
Definition: json.h:2933
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
Definition: json.h:25172
void write_unsupported_feature_response(httplib::Response &res, std::string feature)
Definition: umbridge.h:391
bool check_output_sizes(const std::vector< std::vector< double >> &outputs, const json &config_json, const Model &model, httplib::Response &res)
Definition: umbridge.h:342
bool check_sensitivity_size(const std::vector< double > &sens, int outWrt, const json &config_json, const Model &model, httplib::Response &res)
Definition: umbridge.h:316
bool check_input_sizes(const std::vector< std::vector< double >> &inputs, const json &config_json, const Model &model, httplib::Response &res)
Definition: umbridge.h:293
bool check_output_wrt(int outWrt, const json &config_json, const Model &model, httplib::Response &res)
Definition: umbridge.h:378
void serveModels(std::vector< Model * > models, std::string host, int port)
Definition: umbridge.h:425
bool check_vector_size(const std::vector< double > &vec, int inWrt, const json &config_json, const Model &model, httplib::Response &res)
Definition: umbridge.h:329
bool check_input_wrt(int inWrt, const json &config_json, const Model &model, httplib::Response &res)
Definition: umbridge.h:365
Model & get_model_from_name(std::vector< Model * > &models, std::string name)
Definition: umbridge.h:400
bool check_model_exists(std::vector< Model * > &models, std::string name, httplib::Response &res)
Definition: umbridge.h:410