Janus 2.0.0
High-performance C++20 dual-mode numerical framework
Loading...
Searching...
No Matches
Arithmetic.hpp
Go to the documentation of this file.
1#pragma once
7
9#include <Eigen/Dense>
10#include <cmath>
11
12namespace janus {
13
14// --- Absolute Value ---
21template <JanusScalar T> T abs(const T &x) {
22 if constexpr (std::is_floating_point_v<T>) {
23 return std::abs(x);
24 } else {
25 return fabs(x);
26 }
27}
28
35template <typename Derived> auto abs(const Eigen::MatrixBase<Derived> &x) {
36 return x.array().abs().matrix();
37}
38
39// --- Square Root ---
46template <JanusScalar T> T sqrt(const T &x) {
47 if constexpr (std::is_floating_point_v<T>) {
48 return std::sqrt(x);
49 } else {
50 return sqrt(x);
51 }
52}
53
60template <typename Derived> auto sqrt(const Eigen::MatrixBase<Derived> &x) {
61 return x.array().sqrt().matrix();
62}
63
64// --- Power ---
72template <JanusScalar T> T pow(const T &base, const T &exponent) {
73 if constexpr (std::is_floating_point_v<T>) {
74 return std::pow(base, exponent);
75 } else {
76 return pow(base, exponent);
77 }
78}
79
87template <JanusScalar T>
88 requires(!std::is_same_v<T, double>)
89T pow(const T &base, double exponent) {
90 if constexpr (std::is_floating_point_v<T>) {
91 return std::pow(base, static_cast<T>(exponent));
92 } else {
93 return janus::pow(base, static_cast<T>(exponent));
94 }
95}
96
104template <JanusScalar T>
105 requires(!std::is_same_v<T, double>)
106T pow(double base, const T &exponent) {
107 // Cast base to T to match homogeneous overload
108 return janus::pow(static_cast<T>(base), exponent);
109}
110
119template <typename Derived, typename Scalar>
120auto pow(const Eigen::MatrixBase<Derived> &base, const Scalar &exponent) {
121 return base.array().pow(exponent).matrix();
122}
123
124// --- Exp ---
131template <JanusScalar T> T exp(const T &x) {
132 if constexpr (std::is_floating_point_v<T>) {
133 return std::exp(x);
134 } else {
135 return exp(x);
136 }
137}
138
145template <typename Derived> auto exp(const Eigen::MatrixBase<Derived> &x) {
146 return x.array().exp().matrix();
147}
148
149// --- Log ---
156template <JanusScalar T> T log(const T &x) {
157 if constexpr (std::is_floating_point_v<T>) {
158 return std::log(x);
159 } else {
160 return log(x);
161 }
162}
163
170template <typename Derived> auto log(const Eigen::MatrixBase<Derived> &x) {
171 return x.array().log().matrix();
172}
173
180template <JanusScalar T> T log10(const T &x) {
181 if constexpr (std::is_floating_point_v<T>) {
182 return std::log10(x);
183 } else {
184 return log10(x);
185 }
186}
187
194template <typename Derived> auto log10(const Eigen::MatrixBase<Derived> &x) {
195 return x.array().log10().matrix();
196}
197
198// --- Hyperbolic Functions ---
205template <JanusScalar T> T sinh(const T &x) {
206 if constexpr (std::is_floating_point_v<T>) {
207 return std::sinh(x);
208 } else {
209 return sinh(x);
210 }
211}
212
219template <typename Derived> auto sinh(const Eigen::MatrixBase<Derived> &x) {
220 return x.array().sinh().matrix();
221}
222
229template <JanusScalar T> T cosh(const T &x) {
230 if constexpr (std::is_floating_point_v<T>) {
231 return std::cosh(x);
232 } else {
233 return cosh(x);
234 }
235}
236
243template <typename Derived> auto cosh(const Eigen::MatrixBase<Derived> &x) {
244 return x.array().cosh().matrix();
245}
246
253template <JanusScalar T> T tanh(const T &x) {
254 if constexpr (std::is_floating_point_v<T>) {
255 return std::tanh(x);
256 } else {
257 return tanh(x);
258 }
259}
260
267template <typename Derived> auto tanh(const Eigen::MatrixBase<Derived> &x) {
268 return x.array().tanh().matrix();
269}
270
271// --- Rounding and Sign ---
278template <JanusScalar T> T floor(const T &x) {
279 if constexpr (std::is_floating_point_v<T>) {
280 return std::floor(x);
281 } else {
282 return floor(x);
283 }
284}
285
292template <typename Derived> auto floor(const Eigen::MatrixBase<Derived> &x) {
293 return x.array().floor().matrix();
294}
295
302template <JanusScalar T> T ceil(const T &x) {
303 if constexpr (std::is_floating_point_v<T>) {
304 return std::ceil(x);
305 } else {
306 return ceil(x);
307 }
308}
309
316template <typename Derived> auto ceil(const Eigen::MatrixBase<Derived> &x) {
317 return x.array().ceil().matrix();
318}
319
326template <JanusScalar T> T sign(const T &x) {
327 if constexpr (std::is_floating_point_v<T>) {
328 // Return 1.0, -1.0, or 0.0
329 return (x > 0) ? T(1.0) : ((x < 0) ? T(-1.0) : T(0.0));
330 } else {
331 return sign(x);
332 }
333}
334
341template <typename Derived> auto sign(const Eigen::MatrixBase<Derived> &x) {
342 return x.array().sign().matrix();
343}
344
345// --- Modulo ---
353template <JanusScalar T> T fmod(const T &x, const T &y) {
354 if constexpr (std::is_floating_point_v<T>) {
355 return std::fmod(x, y);
356 } else {
357 return fmod(x, y);
358 }
359}
360
361// Note: Ensure strictly positive modulus if needed, std::fmod matches C++ behavior.
362// There is no direct .fmod() in Eigen Array, need to map
371template <typename Derived, typename Scalar>
372auto fmod(const Eigen::MatrixBase<Derived> &x, const Scalar &y) {
373 if constexpr (std::is_same_v<typename Derived::Scalar, casadi::MX> ||
374 std::is_same_v<Scalar, casadi::MX>) {
375 // Element-wise fmod when either operand is symbolic
376 using ResultScalar = casadi::MX;
377 Eigen::Matrix<ResultScalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime> result(
378 x.rows(), x.cols());
379 const ResultScalar y_mx = static_cast<ResultScalar>(y);
380 for (Eigen::Index i = 0; i < x.rows(); ++i) {
381 for (Eigen::Index j = 0; j < x.cols(); ++j) {
382 result(i, j) = janus::fmod(static_cast<ResultScalar>(x(i, j)), y_mx);
383 }
384 }
385 return result;
386 } else {
387 return x.binaryExpr(Eigen::MatrixBase<Derived>::Constant(x.rows(), x.cols(), y),
388 [](double a, double b) { return std::fmod(a, b); });
389 }
390}
391
392// --- Log2 ---
399template <JanusScalar T> T log2(const T &x) {
400 if constexpr (std::is_floating_point_v<T>) {
401 return std::log2(x);
402 } else {
403 // CasADi: log2(x) = log(x) / log(2)
404 return log(x) / log(T(2.0));
405 }
406}
407
414template <typename Derived> auto log2(const Eigen::MatrixBase<Derived> &x) {
415 using Scalar = typename Derived::Scalar;
416 if constexpr (std::is_same_v<Scalar, casadi::MX>) {
417 return x.unaryExpr([](const Scalar &v) { return janus::log2(v); });
418 } else {
419 return (x.array().log() / std::log(2.0)).matrix();
420 }
421}
422
423// --- Exp2 ---
430template <JanusScalar T> T exp2(const T &x) {
431 if constexpr (std::is_floating_point_v<T>) {
432 return std::exp2(x);
433 } else {
434 // CasADi: 2^x = exp(x * log(2))
435 return exp(x * log(T(2.0)));
436 }
437}
438
445template <typename Derived> auto exp2(const Eigen::MatrixBase<Derived> &x) {
446 using Scalar = typename Derived::Scalar;
447 if constexpr (std::is_same_v<Scalar, casadi::MX>) {
448 return x.unaryExpr([](const Scalar &v) { return janus::exp2(v); });
449 } else {
450 return (x.array() * std::log(2.0)).exp().matrix();
451 }
452}
453
454// --- Cbrt ---
461template <JanusScalar T> T cbrt(const T &x) {
462 if constexpr (std::is_floating_point_v<T>) {
463 return std::cbrt(x);
464 } else {
465 // CasADi: cbrt(x) = sign(x) * pow(abs(x), 1/3)
466 // This handles negative values correctly
467 return sign(x) * pow(fabs(x), T(1.0 / 3.0));
468 }
469}
470
477template <typename Derived> auto cbrt(const Eigen::MatrixBase<Derived> &x) {
478 using Scalar = typename Derived::Scalar;
479 if constexpr (std::is_same_v<Scalar, casadi::MX>) {
480 return x.unaryExpr([](const Scalar &v) { return janus::cbrt(v); });
481 } else {
482 return x.unaryExpr([](double v) { return std::cbrt(v); });
483 }
484}
485
486// --- Round ---
493template <JanusScalar T> T round(const T &x) {
494 if constexpr (std::is_floating_point_v<T>) {
495 return std::round(x);
496 } else {
497 // CasADi: implement round as floor(x + 0.5) for positive,
498 // ceil(x - 0.5) for negative (round half away from zero)
499 return floor(x + T(0.5));
500 }
501}
502
509template <typename Derived> auto round(const Eigen::MatrixBase<Derived> &x) {
510 using Scalar = typename Derived::Scalar;
511 if constexpr (std::is_same_v<Scalar, casadi::MX>) {
512 return x.unaryExpr([](const Scalar &v) { return janus::round(v); });
513 } else {
514 return x.array().round().matrix();
515 }
516}
517
518// --- Trunc ---
525template <JanusScalar T> T trunc(const T &x) {
526 if constexpr (std::is_floating_point_v<T>) {
527 return std::trunc(x);
528 } else {
529 // CasADi: trunc(x) = sign(x) * floor(abs(x))
530 return sign(x) * floor(fabs(x));
531 }
532}
533
540template <typename Derived> auto trunc(const Eigen::MatrixBase<Derived> &x) {
541 using Scalar = typename Derived::Scalar;
542 if constexpr (std::is_same_v<Scalar, casadi::MX>) {
543 return x.unaryExpr([](const Scalar &v) { return janus::trunc(v); });
544 } else {
545 return x.unaryExpr([](double v) { return std::trunc(v); });
546 }
547}
548
549// --- Hypot ---
557template <JanusScalar T> T hypot(const T &x, const T &y) {
558 if constexpr (std::is_floating_point_v<T>) {
559 return std::hypot(x, y);
560 } else {
561 // CasADi: use sqrt(x^2 + y^2) - CasADi handles this symbolically
562 return sqrt(x * x + y * y);
563 }
564}
565
573template <JanusScalar T>
574 requires(!std::is_same_v<T, double>)
575T hypot(const T &x, double y) {
576 return janus::hypot(x, T(y));
577}
578
586template <JanusScalar T>
587 requires(!std::is_same_v<T, double>)
588T hypot(double x, const T &y) {
589 return janus::hypot(T(x), y);
590}
591
599template <typename Derived>
600auto hypot(const Eigen::MatrixBase<Derived> &x, const Eigen::MatrixBase<Derived> &y) {
601 using Scalar = typename Derived::Scalar;
602 if constexpr (std::is_same_v<Scalar, casadi::MX>) {
603 return (x.array().square() + y.array().square()).sqrt().matrix();
604 } else {
605 return x.binaryExpr(y, [](double a, double b) { return std::hypot(a, b); });
606 }
607}
608
609// --- Expm1 ---
616template <JanusScalar T> T expm1(const T &x) {
617 if constexpr (std::is_floating_point_v<T>) {
618 return std::expm1(x);
619 } else {
620 // CasADi: fall back to exp(x) - 1
621 return exp(x) - T(1.0);
622 }
623}
624
631template <typename Derived> auto expm1(const Eigen::MatrixBase<Derived> &x) {
632 using Scalar = typename Derived::Scalar;
633 if constexpr (std::is_same_v<Scalar, casadi::MX>) {
634 return x.unaryExpr([](const Scalar &v) { return janus::expm1(v); });
635 } else {
636 return x.unaryExpr([](double v) { return std::expm1(v); });
637 }
638}
639
640// --- Log1p ---
647template <JanusScalar T> T log1p(const T &x) {
648 if constexpr (std::is_floating_point_v<T>) {
649 return std::log1p(x);
650 } else {
651 // CasADi: fall back to log(1 + x)
652 return log(T(1.0) + x);
653 }
654}
655
662template <typename Derived> auto log1p(const Eigen::MatrixBase<Derived> &x) {
663 using Scalar = typename Derived::Scalar;
664 if constexpr (std::is_same_v<Scalar, casadi::MX>) {
665 return x.unaryExpr([](const Scalar &v) { return janus::log1p(v); });
666 } else {
667 return x.unaryExpr([](double v) { return std::log1p(v); });
668 }
669}
670
671// --- Copysign ---
679template <JanusScalar T> T copysign(const T &x, const T &y) {
680 if constexpr (std::is_floating_point_v<T>) {
681 return std::copysign(x, y);
682 } else {
683 // CasADi: copysign(x, y) = sign(y) * abs(x)
684 return sign(y) * fabs(x);
685 }
686}
687
695template <JanusScalar T>
696 requires(!std::is_same_v<T, double>)
697T copysign(const T &x, double y) {
698 return janus::copysign(x, T(y));
699}
700
708template <JanusScalar T>
709 requires(!std::is_same_v<T, double>)
710T copysign(double x, const T &y) {
711 return janus::copysign(T(x), y);
712}
713
721template <typename Derived>
722auto copysign(const Eigen::MatrixBase<Derived> &x, const Eigen::MatrixBase<Derived> &y) {
723 using Scalar = typename Derived::Scalar;
724 if constexpr (std::is_same_v<Scalar, casadi::MX>) {
725 return x.binaryExpr(y,
726 [](const Scalar &a, const Scalar &b) { return janus::copysign(a, b); });
727 } else {
728 return x.binaryExpr(y, [](double a, double b) { return std::copysign(a, b); });
729 }
730}
731
732// --- Square ---
739template <JanusScalar T> T square(const T &x) { return x * x; }
740
747template <typename Derived> auto square(const Eigen::MatrixBase<Derived> &x) {
748 return x.array().square().matrix();
749}
750
751} // namespace janus
C++20 concepts constraining valid Janus scalar types.
Definition Diagnostics.hpp:19
T hypot(const T &x, const T &y)
Computes sqrt(x^2 + y^2) without undue overflow/underflow.
Definition Arithmetic.hpp:557
T cbrt(const T &x)
Computes cube root of x.
Definition Arithmetic.hpp:461
T trunc(const T &x)
Truncates x toward zero.
Definition Arithmetic.hpp:525
T tanh(const T &x)
Computes hyperbolic tangent of x.
Definition Arithmetic.hpp:253
T sinh(const T &x)
Computes hyperbolic sine.
Definition Arithmetic.hpp:205
T square(const T &x)
Computes x^2 (more efficient than pow(x, 2)).
Definition Arithmetic.hpp:739
T fmod(const T &x, const T &y)
Computes floating-point remainder of x/y.
Definition Arithmetic.hpp:353
T abs(const T &x)
Computes the absolute value of a scalar.
Definition Arithmetic.hpp:21
T sqrt(const T &x)
Computes the square root of a scalar.
Definition Arithmetic.hpp:46
T copysign(const T &x, const T &y)
Returns magnitude of x with sign of y.
Definition Arithmetic.hpp:679
T log(const T &x)
Computes the natural logarithm of x.
Definition Arithmetic.hpp:156
T log2(const T &x)
Computes base-2 logarithm of x.
Definition Arithmetic.hpp:399
T round(const T &x)
Rounds x to the nearest integer.
Definition Arithmetic.hpp:493
T cosh(const T &x)
Computes hyperbolic cosine of x.
Definition Arithmetic.hpp:229
T sign(const T &x)
Computes sign of x.
Definition Arithmetic.hpp:326
T floor(const T &x)
Computes floor of x.
Definition Arithmetic.hpp:278
T pow(const T &base, const T &exponent)
Computes the power function: base^exponent.
Definition Arithmetic.hpp:72
T exp2(const T &x)
Computes 2^x.
Definition Arithmetic.hpp:430
T ceil(const T &x)
Computes ceiling of x.
Definition Arithmetic.hpp:302
T log10(const T &x)
Computes the base-10 logarithm of x.
Definition Arithmetic.hpp:180
T log1p(const T &x)
Computes log(1 + x), accurate for small x.
Definition Arithmetic.hpp:647
T expm1(const T &x)
Computes exp(x) - 1, accurate for small x.
Definition Arithmetic.hpp:616
T exp(const T &x)
Computes the exponential function e^x.
Definition Arithmetic.hpp:131