fmatvec  0.0.0
symbolic.h
1#ifndef _fmatvec_symbolic_h_
2#define _fmatvec_symbolic_h_
3
4#include "ast.h"
5#include <set>
6#include "function.h"
7#include <boost/hana/type.hpp>
8
9namespace fmatvec {
10
11// definitions of matrix/vector operations/functions only usefull for symbolic calculations
12// like building partial derivatives, evaluation of symbolic expressions to double values, ...
13
14template<class Dep, class ATIndep>
15Dep parDer(const Dep &dep, const ATIndep &indep) {
16 Dep ret;
17 if constexpr (std::is_same_v<Dep, SymbolicExpression>) {
18 }
19 else if constexpr (Dep::isVector)
20 ret.resize(dep.size());
21 else
22 ret.resize(dep.rows(), dep.cols());
23 auto d=dep.begin();
24 auto r=ret.begin();
25 for(; d!=dep.end(); ++d, ++r)
26 *r=parDer(*d, indep);
27 return ret;
28}
29
30template<class ATDep, class ATIndep>
31Vector<Fixed<3>, ATDep> parDer(const Matrix<Rotation, Fixed<3>, Fixed<3>, ATDep> &R, const ATIndep &x) {
32 Matrix<General, Fixed<3>, Fixed<3>, ATDep> Rs;
33 for(int r=0; r<3; ++r)
34 for(int c=0; c<3; ++c)
35 Rs(r,c)=parDer(R(r,c), x);
36 auto retTilde=Rs*trans(R);
37 Vector<Fixed<3>, ATDep> ret;
38 ret(0)=retTilde(2,1);
39 ret(1)=retTilde(0,2);
40 ret(2)=retTilde(1,0);
41 return ret;
42}
43
44template<class DepShape, class IndepShape, class ATDep, class ATIndep>
45Matrix<General, DepShape, IndepShape, ATDep> parDer(const Vector<DepShape, ATDep> &dep, const Vector<IndepShape, ATIndep> &indep) {
46 Matrix<General, DepShape, IndepShape, ATDep> ret(dep.size(), indep.size());
47 for(int r=0; r<dep.size(); ++r)
48 for(int c=0; c<indep.size(); ++c)
49 ret(r,c)=parDer(dep(r), indep(c));
50 return ret;
51}
52
53template<class IndepShape, class ATDep, class ATIndep>
54RowVector<IndepShape, ATDep> parDer(const ATDep &dep, const Vector<IndepShape, ATIndep> &indep) {
55 RowVector<IndepShape, ATDep> ret(indep.size());
56 for(int r=0; r<indep.size(); ++r)
57 ret(r)=parDer(dep, indep(r));
58 return ret;
59}
60
61template<class ATDep, class Shape, class ATIndep>
62Matrix<General, Fixed<3>, Shape, ATDep> parDer(const Matrix<Rotation, Fixed<3>, Fixed<3>, ATDep> &R, const Vector<Shape, ATIndep> &x) {
63 Matrix<General, Fixed<3>, Shape, ATDep> ret(3,x.size());
64 for(int i=0; i<x.size(); ++i)
65 ret.set(i, parDer(R, x(i)));
66 return ret;
67}
68
69template<class Dep, class ATDep, class ATIndep>
70Dep dirDer(const Dep &dep, const ATDep &indepdir, const ATIndep &indep) {
71 return parDer(dep, indep)*indepdir;
72}
73
74template<class ATDep, class ATIndep>
75Vector<Fixed<3>, ATDep> dirDer(const Matrix<Rotation, Fixed<3>, Fixed<3>, ATDep> &R, const ATDep &indepdir, const ATIndep &indep) {
76 return parDer(R, indep)*indepdir;
77}
78
79template<class Dep, class ShapeIndep, class ATDep, class ATIndep>
80Dep dirDer(const Dep &dep, const Vector<ShapeIndep, ATDep> &indepdir, const Vector<ShapeIndep, ATIndep> &indep) {
81 Dep ret;
82 if constexpr (std::is_same_v<Dep, SymbolicExpression>) {
83 ret=0.0;
84 }
85 else if constexpr (Dep::isVector)
86 ret<<=Dep(dep.size(), INIT, 0.0);
87 else
88 ret<<=Dep(dep.rows(), dep.cols(), INIT, 0.0);
89 for(int i=0; i<indep.size(); ++i)
90 ret+=parDer(dep, indep(i))*indepdir(i);
91 return ret;
92}
93
94template<class ATDep, class ShapeIndep, class ATIndep>
95Vector<Fixed<3>, ATDep> dirDer(const Matrix<Rotation, Fixed<3>, Fixed<3>, ATDep> &dep, const Vector<ShapeIndep, ATDep> &indepdir, const Vector<ShapeIndep, ATIndep> &indep) {
96 return parDer(dep, indep)*indepdir;
97}
98
99// substitute scalar in scalar expression -> defined in ast.h
100template<class Ret, class A, class B>
101Ret subst(const Ret &src, const A &a, const B &b) {
102 if constexpr (!std::is_same_v<A, IndependentVariable>) {
103 if constexpr (A::isVector) {
104 if(a.size()!=b.size())
105 throw std::runtime_error("The size of the independent and the dependent substitution variable does not match.");
106 }
107 else {
108 if(a.rows()!=b.rows() || a.cols()!=b.cols())
109 throw std::runtime_error("The size of the independent and the dependent substitution variable does not match.");
110 }
111 }
112
113 if constexpr (std::is_same_v<Ret, SymbolicExpression> && std::is_same_v<A, IndependentVariable>) {
114 return AST::substScalar(src, a, b);
115 }
116 if constexpr (std::is_same_v<Ret, SymbolicExpression> && !std::is_same_v<A, IndependentVariable>) {
117 auto ret=src;
118 auto aIt=a.begin();
119 auto bIt=b.begin();
120 for(; aIt<a.end(); ++aIt, ++bIt)
121 ret=subst(ret, *aIt, *bIt);
122 return ret;
123 }
124 if constexpr (!std::is_same_v<Ret, SymbolicExpression> && std::is_same_v<A, IndependentVariable>) {
125 auto ret=src;
126 for(auto retIt=ret.begin(); retIt<ret.end(); ++retIt)
127 *retIt=subst(*retIt, a, b);
128 return ret;
129 }
130 if constexpr (!std::is_same_v<Ret, SymbolicExpression> && !std::is_same_v<A, IndependentVariable>) {
131 auto ret=src;
132 for(auto retIt=ret.begin(); retIt<ret.end(); ++retIt) {
133 auto aIt=a.begin();
134 auto bIt=b.begin();
135 for(; aIt<a.end(); ++aIt, ++bIt)
136 *retIt=subst(*retIt, *aIt, *bIt);
137 }
138 return ret;
139 }
140}
141
142
143
144template<class Dst, class Src>
145Dst& operator^=(Dst &dst, const Src &src) {
146 auto dstIt=dst.begin();
147 auto srcIt=src.begin();
148 for(; dstIt<dst.end(); ++dstIt, ++srcIt)
149 *dstIt ^= *srcIt;
150 return dst;
151}
152
153
154
155/* Class for evaluating a symbolic expression
156 * This class is not copy/move-able to since it uses ByteCode which itself uses internal raw pointers (for
157 * performance reasons) which cannot be copied/moved.
158*/
159template<class... Arg>
160class Eval {
161 private:
162 // Type for all symbolic arguments as a tuple
163 using SymTuple = std::tuple<Arg...>;
164 // Type for all numeric return values as a tuple
165 using NumTuple = std::tuple<typename ReplaceAT<Arg, double>::Type...>;
166 // The return type: equals NumTuple if more than 1 arg is given in the ctor; for only 1 arg the tuple is skipped
167 using NumRetType = std::conditional_t<std::tuple_size_v<NumTuple> == 1, std::tuple_element_t<0,NumTuple>, NumTuple>;
168 public:
169 ~Eval();
170 Eval(const Eval &src) = delete;
171 Eval(Eval &&src) = delete;
172 Eval<Arg...>& operator=(const Eval &src) = delete;
173 Eval<Arg...>& operator=(Eval &&src) = delete;
174
175 // construct an evaluation object for all symbolic args
176 // An arg can be a symbolic scalar, vector or matrix
177 Eval(const Arg&... arg);
178 // evaluate all symbolic args given by the ctor and return a tuple of corrsponding evaluated numeric values.
179 // Note that the return values can be get easily using "structured binding".
180 inline const NumRetType& operator()() const;
181 private:
182 // the numeric values as a tuple
183 NumTuple numTuple;
184 // hold a reference to Symbols used by the evaluation to avoid deleting these symbols (since the code has pointers to these)
185 std::set<std::shared_ptr<const AST::Symbol>> symbolStore;
186 // helper function to walk over all SymbolicExpression (AT=atomic type) and corresponding double value in parallel.
187 // For each pair the function is called.
188 template<int I=0>
189 void walkAT(const SymTuple &symTuple, NumTuple &numTuple,
190 const std::function<void(const SymbolicExpression&, double&)> &func);
191
192 // members for bytecode evaluation
193
194 std::vector<AST::ByteCode> byteCode;
195
196 // the constructor and operator() for runtime evaluation
197 void ctorByteCode(const Arg&... arg);
198 inline void callByteCode() const;
199};
200
201template<class... Arg>
202Eval<Arg...>::~Eval() = default;
203
204template<class... Arg>
205Eval<Arg...>::Eval(const Arg&... arg) {
206 ctorByteCode(arg...);
207}
208
209template<class... Arg>
210auto Eval<Arg...>::operator()() const -> const NumRetType& {
211 callByteCode();
212 // return the numeric values: the above call has written to its addresses
213 if constexpr (std::tuple_size_v<NumTuple> == 1)
214 return std::get<0>(numTuple);
215 else
216 return numTuple;
217}
218
219template<class... Arg>
220template<int I>
221void Eval<Arg...>::walkAT(const SymTuple &symTuple, NumTuple &numTuple,
222 const std::function<void(const SymbolicExpression&, double&)> &func) {
223 // get the I-th symbolic and numeric variable form the tuples (this function handles the I-th tuple entry)
224 // (this function is called with I=0 from the external caller)
225 auto &sym = std::get<I>(symTuple);
226 auto &num = std::get<I>(numTuple);
227 // get the type of the I-th tuple entry
228 using Sym = std::tuple_element_t<I,SymTuple>;
229
230 if constexpr (std::is_same_v<Sym, SymbolicExpression>)
231 func(sym, num);
232 else {
233 // fill bytecode for vector or matrix value
234 // resize the vector or matrix
235 if constexpr (Sym::isVector)
236 num.resize(sym.size());
237 else
238 num.resize(sym.rows(), sym.cols());
239 // get begin iterator of the vector/matrix and iterator both iterator simultaniously
240 auto symIt=sym.begin();
241 auto numIt=num.begin();
242 for(; symIt!=sym.end(); ++symIt, ++numIt)
243 func(*symIt, *numIt);
244 }
245
246 // call this function recursively for the next (I+1)-th entry in the tuple (only if there is a next entry)
247 if constexpr (I+1<std::tuple_size_v<SymTuple>)
248 walkAT<I+1>(symTuple, numTuple, func);
249}
250
251template<class... Arg>
252void Eval<Arg...>::ctorByteCode(const Arg&... arg) {
253 // first walk through all vertices in all args and count the number of operations needed
254 size_t byteCodeCount=0;
255 std::set<const AST::Vertex*> existingVertex1;
256 walkAT(SymTuple(arg...), numTuple, [&byteCodeCount, &existingVertex1](auto &sym, auto &num) {
257 sym->walkVertex([&byteCodeCount, &existingVertex1](const std::shared_ptr<const AST::Vertex>& v) {
258 if(!existingVertex1.insert(v.get()).second) return;
259
260 if(auto s=std::dynamic_pointer_cast<const AST::Symbol>(v); s)
261 byteCodeCount++;
262 byteCodeCount++;
263 });
264 byteCodeCount++;
265 });
266 // pre-allocate byteCode (ByteCode has not move-ctor)
267 byteCode.reserve(byteCodeCount);
268 std::map<const AST::Vertex*, std::vector<AST::ByteCode>::iterator> existingVertex;
269 // than save all symbols in the symbolStore (the code uses a pointer to Symbol::x)
270 // and add code to copy from &Symbol::x to byteCode. This copy is a "slow" operation since &Symbol::x may be far away.
271 walkAT(SymTuple(arg...), numTuple, [this, &existingVertex](auto &sym, auto &num) {
272 sym->walkVertex([this, &existingVertex](const std::shared_ptr<const AST::Vertex>& v) {
273 if(auto s=std::dynamic_pointer_cast<const AST::Symbol>(v); s) {
274 symbolStore.insert(s);
275 s->dumpByteCode(byteCode, existingVertex);
276 }
277 });
278 });
279 // now add the code of the symbolic expression: this is a "fast" operation since only addresses inside of byteCode are used
280 // Also store a interator to each AT (atomic type) result of the symbolic expression
281 std::vector< std::vector<AST::ByteCode>::iterator > exprRet;
282 walkAT(SymTuple(arg...), numTuple, [this, &existingVertex, &exprRet](auto &sym, auto &num) {
283 exprRet.emplace_back(sym->dumpByteCode(byteCode, existingVertex));
284 });
285 // lastly add code to copy the result of each AT (the iterators from above) to the address of the return value.
286 // This is again a "slow" operation since the address of the return value may be far away fromo byteCode.
287 auto exprRetIt = exprRet.begin();
288 walkAT(SymTuple(arg...), numTuple, [this, &exprRet, &exprRetIt](auto &sym, auto &num) {
289 byteCode.emplace_back(1);
290 auto it = --byteCode.end();
291 it->func = [](double* r, const AST::ByteCode::Arg& a) { *r = *a[0]; };
292 it->argsPtr = { (*(exprRetIt++))->retPtr };
293 it->retPtr = &num;
294 });
295}
296
297template<class... Arg>
298void Eval<Arg...>::callByteCode() const {
299#if defined(FMATVEC_DEBUG) && !defined(SWIG)
300 SymbolicExpression::evalOperationsCount = byteCode.size();
301#endif
302 std::for_each(byteCode.begin(), byteCode.end(), [](const AST::ByteCode &bc){
303 bc.func(bc.retPtr, bc.argsPtr);
304 });
305}
306
307template<class RetN, class ArgN>
308class FunctionWrap1VecRetToScalar : public Function<double(ArgN)> {
309 public:
310 FunctionWrap1VecRetToScalar(const std::shared_ptr<Function<RetN(ArgN)>> &func_, int idx_) :
311 func(func_), idx(idx_) {}
312 double operator()(const ArgN &arg) override {
313 return (*func)(arg)(idx);
314 }
315 double dirDer(const ArgN &argDir, const ArgN &arg) override {
316 return func->dirDer(argDir, arg)(idx);
317 }
318 double dirDerDirDer(const ArgN &argDir_1, const ArgN &argDir_2, const ArgN &arg) override {
319 return func->dirDerDirDer(argDir_1, argDir_2, arg)(idx);
320 }
321 private:
322 std::shared_ptr<Function<RetN(ArgN)>> func;
323 int idx;
324};
325
326template<class RetN, class Arg1N, class Arg2N>
327class FunctionWrap2VecRetToScalar : public Function<double(Arg1N,Arg2N)> {
328 public:
329 FunctionWrap2VecRetToScalar(const std::shared_ptr<Function<RetN(Arg1N,Arg2N)>> &func_, int idx_) :
330 func(func_), idx(idx_) {}
331 double operator()(const Arg1N &arg1, const Arg2N &arg2) override {
332 return (*func)(arg1,arg2)(idx);
333 }
334 double dirDer1(const Arg1N &dir1, const Arg1N &arg1, const Arg2N &arg2) override {
335 return func->dirDer1(dir1, arg1, arg2)(idx);
336 }
337 double dirDer2(const Arg2N &dir2, const Arg1N &arg1, const Arg2N &arg2) override {
338 return func->dirDer2(dir2, arg1, arg2)(idx);
339 }
340 double dirDer1DirDer1(const Arg1N &dir1_1, const Arg1N &dir1_2, const Arg1N &arg1, const Arg2N &arg2) override {
341 return func->dirDer1DirDer1(dir1_1, dir1_2, arg1, arg2)(idx);
342 }
343 double dirDer2DirDer1(const Arg2N &dir2_1, const Arg1N &dir1_2, const Arg1N &arg1, const Arg2N &arg2) override {
344 return func->dirDer2DirDer1(dir2_1, dir1_2, arg1, arg2)(idx);
345 }
346 double dirDer2DirDer2(const Arg2N &dir2_1, const Arg2N &dir2_2, const Arg1N &arg1, const Arg2N &arg2) override {
347 return func->dirDer2DirDer2(dir2_1, dir2_2, arg1, arg2)(idx);
348 }
349 private:
350 std::shared_ptr<Function<RetN(Arg1N,Arg2N)>> func;
351 int idx;
352};
353
354template<class RetN, class ArgN>
355class FunctionWrap1MatRetToScalar : public Function<double(ArgN)> {
356 public:
357 FunctionWrap1MatRetToScalar(const std::shared_ptr<Function<RetN(ArgN)>> &func_, int row_, int col_) :
358 func(func_), row(row_), col(col_) {}
359 double operator()(const ArgN &arg) override {
360 return (*func)(arg)(row,col);
361 }
362 double dirDer(const ArgN &argDir, const ArgN &arg) override {
363 return func->dirDer(argDir, arg)(row,col);
364 }
365 double dirDerDirDer(const ArgN &argDir_1, const ArgN &argDir_2, const ArgN &arg) override {
366 return func->dirDerDirDer(argDir_1, argDir_2, arg)(row,col);
367 }
368 private:
369 std::shared_ptr<Function<RetN(ArgN)>> func;
370 int row;
371 int col;
372};
373
374template<class RetN, class Arg1N, class Arg2N>
375class FunctionWrap2MatRetToScalar : public Function<double(Arg1N,Arg2N)> {
376 public:
377 FunctionWrap2MatRetToScalar(const std::shared_ptr<Function<RetN(Arg1N,Arg2N)>> &func_, int row_, int col_) :
378 func(func_), row(row_), col(col_) {}
379 double operator()(const Arg1N &arg1, const Arg2N &arg2) override {
380 return (*func)(arg1,arg2)(row,col);
381 }
382 double dirDer1(const Arg1N &dir1, const Arg1N &arg1, const Arg2N &arg2) override {
383 return func->dirDer1(dir1, arg1, arg2)(row,col);
384 }
385 double dirDer2(const Arg2N &dir2, const Arg1N &arg1, const Arg2N &arg2) override {
386 return func->dirDer2(dir2, arg1, arg2)(row,col);
387 }
388 double dirDer1DirDer1(const Arg1N &dir1_1, const Arg1N &dir1_2, const Arg1N &arg1, const Arg2N &arg2) override {
389 return func->dirDer1DirDer1(dir1_1, dir1_2, arg1, arg2)(row,col);
390 }
391 double dirDer2DirDer1(const Arg2N &dir2_1, const Arg1N &dir1_2, const Arg1N &arg1, const Arg2N &arg2) override {
392 return func->dirDer2DirDer1(dir2_1, dir1_2, arg1, arg2)(row,col);
393 }
394 double dirDer2DirDer2(const Arg2N &dir2_1, const Arg2N &dir2_2, const Arg1N &arg1, const Arg2N &arg2) override {
395 return func->dirDer2DirDer2(dir2_1, dir2_2, arg1, arg2)(row,col);
396 }
397 private:
398 std::shared_ptr<Function<RetN(Arg1N,Arg2N)>> func;
399 int row;
400 int col;
401};
402
403template<class Func, class ArgS>
404typename ReplaceAT<typename std::function<Func>::result_type,SymbolicExpression>::Type symbolicFuncWrapVecAndMatRet(
405 const std::shared_ptr<Function<Func>> &func, const ArgS &arg, int size1=0, int size2=0) {
406 using RetN = typename std::function<Func>::result_type;
407 using RetS = typename ReplaceAT<RetN,SymbolicExpression>::Type;
408 using ArgN = typename ReplaceAT<ArgS,double>::Type;
409
410 auto hasSize = boost::hana::is_valid([](auto&& vec) -> decltype(vec.size()) {});
411
412 RetS ret;
413 if constexpr (hasSize(ret)) {
414 // vector
415 int size=func->getRetSize().first;
416 if(size==0 && size1!=0)
417 size=size1;
418 else if(size==0)
419 size=(*func)(ArgN()).size();
420 ret.resize(size);
421 for(int i=0; i<size; ++i)
422 ret(i) = AST::SymbolicFuncWrapArg1<double(ArgN), ArgS>::
423 call(std::make_shared<FunctionWrap1VecRetToScalar<RetN,ArgN>>(func, i), arg);
424 return ret;
425 }
426 else {
427 // matrix
428 auto size=func->getRetSize();
429 if(size.first==0 && size1!=0)
430 size.first=size1;
431 else if(size.first==0)
432 size.first=(*func)(ArgN()).rows();
433 if(size.second==0 && size2!=0)
434 size.second=size2;
435 else if(size.second==0)
436 size.second=(*func)(ArgN()).cols();
437 ret.resize(size.first, size.second);
438 for(int r=0; r<size.first; ++r)
439 for(int c=0; c<size.second; ++c)
440 ret(r,c) = AST::SymbolicFuncWrapArg1<double(ArgN), ArgS>::call(std::make_shared<FunctionWrap1MatRetToScalar<RetN,ArgN>>(
441 func, r, c), arg);
442 return ret;
443 }
444}
445
446template<class Func, class Arg1S, class Arg2S>
447typename ReplaceAT<typename std::function<Func>::result_type,SymbolicExpression>::Type symbolicFuncWrapVecAndMatRet(
448 const std::shared_ptr<Function<Func>> &func, const Arg1S &arg1, const Arg2S &arg2, int size1=0, int size2=0) {
449 using RetN = typename std::function<Func>::result_type;
450 using RetS = typename ReplaceAT<RetN,SymbolicExpression>::Type;
451 using Arg1N = typename ReplaceAT<Arg1S,double>::Type;
452 using Arg2N = typename ReplaceAT<Arg2S,double>::Type;
453
454 auto hasSize = boost::hana::is_valid([](auto&& vec) -> decltype(vec.size()) {});
455
456 RetS ret;
457 if constexpr (hasSize(ret)) {
458 // vector
459 int size=func->getRetSize().first;
460 if(size==0 && size1!=0)
461 size=size1;
462 else if(size==0)
463 size=(*func)(Arg1N(), Arg2N()).size();
464 ret.resize(size);
465 for(int i=0; i<size; ++i)
466 ret(i) = AST::SymbolicFuncWrapArg2<double(Arg1N,Arg2N), Arg1S, Arg2S>::
467 call(std::make_shared<FunctionWrap2VecRetToScalar<RetN,Arg1N,Arg2N>>(func, i), arg1, arg2);
468 return ret;
469 }
470 else {
471 // matrix
472 auto size=func->getRetSize();
473 if(size.first==0 && size1!=0)
474 size.first=size1;
475 else if(size.first==0)
476 size.first=(*func)(Arg1N(),Arg2N()).rows();
477 if(size.second==0 && size2!=0)
478 size.second=size2;
479 else if(size.second==0)
480 size.second=(*func)(Arg1N(),Arg2N()).cols();
481 ret.resize(size.first, size.second);
482 for(int r=0; r<size.first; ++r)
483 for(int c=0; c<size.second; ++c)
484 ret(r,c) = AST::SymbolicFuncWrapArg2<double(Arg1N,Arg2N), Arg1S, Arg2S>::
485 call(std::make_shared<FunctionWrap2MatRetToScalar<RetN,Arg1N,Arg2N>>(func, r, c), arg1, arg2);
486 return ret;
487 }
488}
489
495template<class Func, class ArgS>
497 const std::shared_ptr<Function<Func>> &func, const ArgS &arg, int size1=0, int size2=0) {
498 using RetN = typename std::function<Func>::result_type;
499 using ArgN = typename ReplaceAT<ArgS,double>::Type;
500 if constexpr (std::is_same_v<RetN, double>)
501 return AST::SymbolicFuncWrapArg1<double(ArgN), ArgS>::call(func, arg);
502 else
503 return symbolicFuncWrapVecAndMatRet<Func, ArgS>(func, arg, size1, size2);
504}
505
511template<class Func, class Arg1S, class Arg2S>
513 const std::shared_ptr<Function<Func>> &func, const Arg1S &arg1, const Arg2S &arg2, int size1=0, int size2=0) {
514 using RetN = typename std::function<Func>::result_type;
515 using Arg1N = typename ReplaceAT<Arg1S,double>::Type;
516 using Arg2N = typename ReplaceAT<Arg2S,double>::Type;
517 if constexpr (std::is_same_v<RetN, double>)
518 return AST::SymbolicFuncWrapArg2<double(Arg1N, Arg2N), Arg1S, Arg2S>::call(func, arg1, arg2);
519 else
520 return symbolicFuncWrapVecAndMatRet<Func, Arg1S, Arg2S>(func, arg1, arg2, size1, size2);
521}
522
523}
524
525#endif
Definition: symbolic.h:160
Definition: symbolic.h:355
Definition: symbolic.h:308
Definition: symbolic.h:375
Definition: symbolic.h:327
Definition: function.h:202
Definition: ast.h:38
Definition: types.h:85
Namespace fmatvec.
Definition: _memory.cc:28
RowVector< Row, AT > trans(const Vector< Row, AT > &x)
Transpose of a vector.
Definition: linear_algebra.h:1723
SymbolicExpression parDer(const SymbolicExpression &dep, const IndependentVariable &indep)
Definition: ast.cc:299
ReplaceAT< typenamestd::function< Func >::result_type, SymbolicExpression >::Type symbolicFunc(const std::shared_ptr< Function< Func > > &func, const ArgS &arg, int size1=0, int size2=0)
Definition: symbolic.h:496
Definition: types.h:306