bspline_interpolant.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "bspline_interpolant.hpp"
27 #include "casadi/core/bspline.hpp"
28 
29 namespace casadi {
30 
31  extern "C"
32  int CASADI_INTERPOLANT_BSPLINE_EXPORT
33  casadi_register_interpolant_bspline(Interpolant::Plugin* plugin) {
34  plugin->creator = BSplineInterpolant::creator;
35  plugin->name = "bspline";
36  plugin->doc = BSplineInterpolant::meta_doc.c_str();
37  plugin->version = CASADI_VERSION;
38  plugin->options = &BSplineInterpolant::options_;
39  plugin->deserialize = &BSplineInterpolant::deserialize;
40  plugin->exposed.do_inline = nullptr;
41  return 0;
42  }
43 
44  extern "C"
45  void CASADI_INTERPOLANT_BSPLINE_EXPORT casadi_load_interpolant_bspline() {
47  }
48 
49  const Options BSplineInterpolant::options_
51  {{"degree",
52  {OT_INTVECTOR,
53  "Sets, for each grid dimension, the degree of the spline."}},
54  {"linear_solver",
55  {OT_STRING,
56  "Solver used for constructing the coefficient tensor."}},
57  {"linear_solver_options",
58  {OT_DICT,
59  "Options to be passed to the linear solver."}},
60  {"algorithm",
61  {OT_STRING,
62  "Algorithm used for fitting the data: 'not_a_knot' (default, same as Matlab),"
63  " 'smooth_linear'."}},
64  {"smooth_linear_frac",
65  {OT_DOUBLE,
66  "When 'smooth_linear' algorithm is active, determines sharpness between"
67  " 0 (sharp, as linear interpolation) and 0.5 (smooth)."
68  "Default value is 0.1."}}
69  }
70  };
71 
73  clear_mem();
74  }
75 
77  BSplineInterpolant(const std::string& name,
78  const std::vector<double>& grid,
79  const std::vector<casadi_int>& offset,
80  const std::vector<double>& values,
81  casadi_int m)
82  : Interpolant(name, grid, offset, values, m) {
83 
84  }
85 
86  std::vector<double> BSplineInterpolant::not_a_knot(const std::vector<double>& x, casadi_int k) {
87  std::vector<double> ret;
88  if (k%2) {
89  casadi_int m = (k-1)/2;
90  casadi_assert(x.size()>=2*m+2, "Need more data points");
91  for (casadi_int i=0;i<k+1;++i) ret.push_back(x[0]);
92  for (casadi_int i=0;i<x.size()-2*m-2;++i) ret.push_back(x[m+1+i]);
93  for (casadi_int i=0;i<k+1;++i) ret.push_back(x[x.size()-1]);
94  } else {
95  casadi_error("Not implemented");
96  //for (casadi_int i=0;i<k+1;++i) ret.push_back(x[0]);
97  //for (casadi_int i=0;i<x.size()-2*m-2;++i) ret.push_back(x[m+1+i]);
98  //for (casadi_int i=0;i<k+1;++i) ret.push_back(x[x.size()-1]);
99  }
100  return ret;
101  }
102 
103  void BSplineInterpolant::init(const Dict& opts) {
104 
105  degree_ = std::vector<casadi_int>(offset_.size()-1, 3);
106 
107  linear_solver_ = "lsqr";
109  smooth_linear_frac_ = 0.1;
110 
111  Dict linear_solver_options;
112 
113  // Read options
114  for (auto&& op : opts) {
115  if (op.first=="degree") {
116  degree_ = op.second;
117  } else if (op.first=="linear_solver") {
118  linear_solver_ = op.second.to_string();
119  } else if (op.first=="linear_solver_options") {
120  linear_solver_options = op.second.to_dict();
121  } else if (op.first=="algorithm") {
122  std::string alg = op.second.to_string();
123  if (alg=="not_a_knot") {
125  } else if (alg=="smooth_linear") {
127  } else {
128  casadi_error("Algorithm option invalid: " + get_options().info("algorithm"));
129  }
130  } else if (op.first=="smooth_linear_frac") {
131  smooth_linear_frac_ = op.second;
132  casadi_assert(smooth_linear_frac_>0 && smooth_linear_frac_<0.5,
133  "smooth_linear_frac must be in ]0,0.5[");
134  }
135  }
136 
137  casadi_assert_dev(degree_.size()==offset_.size()-1);
138 
139  // Call the base class initializer
140  Interpolant::init(opts);
141 
142  casadi_assert(!has_parametric_grid(), "Parametric grid not supported");
143 
144  MX x = MX::sym("x", ndim_, batch_x_);
145 
146  if (has_parametric_values()) {
147  MX coeff = MX::sym("coeff", coeff_size());
148 
149  MX e = construct_graph(x, coeff, linear_solver_options, opts);
150 
151  S_ = Function("wrapper", {x, coeff}, {e}, {"x", "c"}, {"f"});
152  } else {
153  MX e = construct_graph(x, DM(values_), linear_solver_options, opts);
154  S_ = Function("wrapper", {x}, {e}, {"x"}, {"f"});
155  }
156 
157  alloc_w(S_.sz_w());
158  alloc_iw(S_.sz_iw());
159  alloc_arg(S_.sz_arg());
160  alloc_res(S_.sz_res());
161  }
162 
163  void BSplineInterpolant::find(std::map<FunctionInternal*, Function>& all_fun,
164  casadi_int max_depth) const {
165  add_embedded(all_fun, S_, max_depth);
166  }
167 
168  std::vector<double> BSplineInterpolant::greville_points(const std::vector<double>& x,
169  casadi_int deg) {
170  casadi_int dim = x.size()-deg-1;
171  std::vector<double> ret(dim);
172  for (casadi_int i = 0; i < dim; ++i) {
173  ret[i] = 0;
174  for (casadi_int j = 0; j < deg; j++) {
175  ret[i] += x[i+1+j];
176  }
177  ret[i] = ret[i] / deg;
178  }
179  return ret;
180  }
181 
182  int BSplineInterpolant::eval(const double** arg, double** res,
183  casadi_int* iw, double* w, void* mem) const {
184  setup(mem, arg, res, iw, w);
186  return S_(arg, res, iw, w, m);
187  }
188 
190  S_->codegen_body(g);
191  }
192 
195  }
196 
198  get_jacobian(const std::string& name,
199  const std::vector<std::string>& inames,
200  const std::vector<std::string>& onames,
201  const Dict& opts) const {
202  return S_->get_jacobian(name, inames, onames, opts);
203  }
204 
206  get_forward(casadi_int nfwd, const std::string& name,
207  const std::vector<std::string>& inames,
208  const std::vector<std::string>& onames,
209  const Dict& opts) const {
210  return S_->get_forward(nfwd, name, inames, onames, opts);
211  }
212 
214  get_reverse(casadi_int nadj, const std::string& name,
215  const std::vector<std::string>& inames,
216  const std::vector<std::string>& onames,
217  const Dict& opts) const {
218  return S_->get_reverse(nadj, name, inames, onames, opts);
219  }
220 
222  s.version("BSplineInterpolant", 1);
223  s.unpack("BSplineInterpolant::s", S_);
224  }
225 
228 
229  s.version("BSplineInterpolant", 1);
230  s.pack("BSplineInterpolant::s", S_);
231  }
232 
233 } // namespace casadi
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
std::string linear_solver_
Only used during init, no need to serialize these.
void codegen_body(CodeGenerator &g) const override
Generate code for the body of the C function.
static std::vector< double > not_a_knot(const std::vector< double > &x, casadi_int k)
std::vector< casadi_int > degree_
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
void init(const Dict &opts) override
Initialize.
MX construct_graph(const MX &x, const M &values, const Dict &linsol_options, const Dict &opts)
static Interpolant * creator(const std::string &name, const std::vector< double > &grid, const std::vector< casadi_int > &offset, const std::vector< double > &values, casadi_int m)
Create a new Interpolant.
Function get_forward(casadi_int nfwd, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Return function that calculates forward derivatives forward(nfwd) returns a cached instance if availa...
Function get_reverse(casadi_int nadj, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Return function that calculates adjoint derivatives reverse(nadj) returns a cached instance if availa...
const Options & get_options() const override
Options.
static std::vector< double > greville_points(const std::vector< double > &x, casadi_int deg)
int eval(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Evaluate numerically.
static const Options options_
Options.
void codegen_declarations(CodeGenerator &g) const override
Generate code for the declarations of the C function.
BSplineInterpolant(const std::string &name, const std::vector< double > &grid, const std::vector< casadi_int > &offset, const std::vector< double > &values, casadi_int m)
static const std::string meta_doc
A documentation string.
void find(std::map< FunctionInternal *, Function > &all_fun, casadi_int max_depth) const override
Function get_jacobian(const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Full Jacobian.
Helper class for C code generation.
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
void alloc_iw(size_t sz_iw, bool persistent=false)
Ensure required length of iw field.
void alloc_res(size_t sz_res, bool persistent=false)
Ensure required length of res field.
void alloc_arg(size_t sz_arg, bool persistent=false)
Ensure required length of arg field.
virtual void codegen_body(CodeGenerator &g) const
Generate code for the function body.
virtual Function get_reverse(casadi_int nadj, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const
Return function that calculates adjoint derivatives.
virtual void codegen_declarations(CodeGenerator &g) const
Generate code for the declarations of the C function.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
virtual Function get_jacobian(const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const
Return Jacobian of all input elements with respect to all output elements.
void setup(void *mem, const double **arg, double **res, casadi_int *iw, double *w) const
Set the (persistent and temporary) work vectors.
void add_embedded(std::map< FunctionInternal *, Function > &all_fun, const Function &dep, casadi_int max_depth) const
virtual Function get_forward(casadi_int nfwd, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const
Return function that calculates forward derivatives.
Function object.
Definition: function.hpp:60
size_t sz_res() const
Get required length of res field.
Definition: function.cpp:1085
size_t sz_iw() const
Get required length of iw field.
Definition: function.cpp:1087
size_t sz_w() const
Get required length of w field.
Definition: function.cpp:1089
size_t sz_arg() const
Get required length of arg field.
Definition: function.cpp:1083
static MX sym(const std::string &name, casadi_int nrow=1, casadi_int ncol=1)
Create an nrow-by-ncol symbolic primitive.
static const Options options_
Options.
casadi_int coeff_size() const
Size of the flattened coefficients vector.
void init(const Dict &opts) override
Initialize.
std::vector< casadi_int > offset_
bool has_parametric_grid() const
Is parametric?
std::vector< double > values_
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
bool has_parametric_values() const
Is parametric?
MX - Matrix expression.
Definition: mx.hpp:92
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
void clear_mem()
Clear all memory (called from destructor)
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
The casadi namespace.
Definition: archiver.cpp:28
int CASADI_INTERPOLANT_BSPLINE_EXPORT casadi_register_interpolant_bspline(Interpolant::Plugin *plugin)
@ OT_INTVECTOR
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
Matrix< double > DM
Definition: dm_fwd.hpp:33
void CASADI_INTERPOLANT_BSPLINE_EXPORT casadi_load_interpolant_bspline()