17#include <gch/small_vector.hpp>
19#include "sleipnir/autodiff/expression_type.hpp"
20#include "sleipnir/util/force_inline.hpp"
21#include "sleipnir/util/function_ref.hpp"
22#include "sleipnir/util/intrusive_shared_ptr.hpp"
23#include "sleipnir/util/pool.hpp"
25namespace slp::detail {
28inline constexpr bool USE_POOL_ALLOCATOR =
false;
30inline constexpr bool USE_POOL_ALLOCATOR =
true;
33template <
typename Scalar>
37inline constexpr size_t MAX_ARGS = 4;
43using ArgArray = std::array<T, MAX_ARGS>;
48template <
typename Scalar>
49using ExpressionPtr = IntrusiveSharedPtr<Expression<Scalar>>;
56template <
typename T,
typename... Args>
57static ExpressionPtr<typename T::Scalar> make_expression_ptr(Args&&... args) {
58 if constexpr (USE_POOL_ALLOCATOR) {
59 return allocate_intrusive_shared<T>(global_pool_allocator<T>(),
60 std::forward<Args>(args)...);
62 return make_intrusive_shared<T>(std::forward<Args>(args)...);
66template <
typename Scalar, ExpressionType T>
67struct BinaryMinusExpression;
69template <
typename Scalar, ExpressionType T>
70struct BinaryPlusExpression;
72template <
typename Scalar, ExpressionType T>
75template <
typename Scalar, ExpressionType T>
78template <
typename Scalar, ExpressionType T>
79struct UnaryMinusExpression;
89template <
template <
typename, ExpressionType>
typename T,
typename Scalar,
91SLEIPNIR_FORCE_INLINE ExpressionPtr<Scalar> make_typed_expression_ptr(
92 ExpressionType type,
const Args&... args) {
93 using enum ExpressionType;
96 return make_expression_ptr<T<Scalar, LINEAR>>(args...);
97 }
else if (type == QUADRATIC) {
98 return make_expression_ptr<T<Scalar, QUADRATIC>>(args...);
100 return make_expression_ptr<T<Scalar, NONLINEAR>>(args...);
108template <
typename Scalar>
109ExpressionPtr<Scalar> constant_ptr(Scalar value);
120template <
typename Value,
typename Scalar>
121Value lit(Scalar value) {
122 if constexpr (std::same_as<Value, Scalar>) {
125 return constant_ptr(value);
132template <
typename Scalar_>
183 return type() == ExpressionType::CONSTANT &&
val == constant;
192 using enum ExpressionType;
195 if (lhs->is_constant(
Scalar(0))) {
198 }
else if (rhs->is_constant(
Scalar(0))) {
201 }
else if (lhs->is_constant(
Scalar(1))) {
204 }
else if (rhs->is_constant(
Scalar(1))) {
210 if (lhs->type() == CONSTANT && rhs->type() == CONSTANT) {
211 return constant_ptr(lhs->val * rhs->val);
216 auto type = NONLINEAR;
217 if (lhs->type() == CONSTANT || rhs->type() == CONSTANT) {
218 type = std::max(lhs->type(), rhs->type());
219 }
else if (lhs->type() == LINEAR && rhs->type() == LINEAR) {
222 return make_typed_expression_ptr<MultExpression, Scalar>(
type, lhs, rhs);
231 using enum ExpressionType;
234 if (lhs->is_constant(
Scalar(0))) {
237 }
else if (rhs->is_constant(
Scalar(1))) {
243 if (lhs->type() == CONSTANT && rhs->type() == CONSTANT) {
244 return constant_ptr(lhs->val / rhs->val);
249 auto type = rhs->type() == CONSTANT ? lhs->type() : NONLINEAR;
250 return make_typed_expression_ptr<DivExpression, Scalar>(
type, lhs, rhs);
259 using enum ExpressionType;
263 if (lhs ==
nullptr || lhs->is_constant(
Scalar(0))) {
266 }
else if (rhs ==
nullptr || rhs->is_constant(
Scalar(0))) {
272 if (lhs->type() == CONSTANT && rhs->type() == CONSTANT) {
273 return constant_ptr(lhs->val + rhs->val);
276 auto type = std::max(lhs->type(), rhs->type());
277 return make_typed_expression_ptr<BinaryPlusExpression, Scalar>(
type, lhs,
287 return lhs = lhs + rhs;
296 using enum ExpressionType;
299 if (lhs->is_constant(
Scalar(0))) {
300 if (rhs->is_constant(
Scalar(0))) {
307 }
else if (rhs->is_constant(
Scalar(0))) {
313 if (lhs->type() == CONSTANT && rhs->type() == CONSTANT) {
314 return constant_ptr(lhs->val - rhs->val);
317 auto type = std::max(lhs->type(), rhs->type());
318 return make_typed_expression_ptr<BinaryMinusExpression, Scalar>(
type, lhs,
326 using enum ExpressionType;
329 if (lhs->is_constant(
Scalar(0))) {
335 if (lhs->type() == CONSTANT) {
336 return constant_ptr(-lhs->val);
339 return make_typed_expression_ptr<UnaryMinusExpression, Scalar>(lhs->type(),
368 virtual ExpressionType
type()
const = 0;
373 virtual std::string_view
name()
const = 0;
403template <
typename Derived,
typename Scalar,
size_t NumArgs,
404 ExpressionType Type = ExpressionType::NONLINEAR>
410 static_assert(NumArgs <= MAX_ARGS,
"Increase MAX_ARGS");
416 std::array<ExpressionPtr<Scalar>, NumArgs>
args;
421 template <
typename... Args>
422 requires(
sizeof...(Args) == NumArgs &&
426 :
args{std::forward<Args>(
args)...} {}
433 [&]<
size_t... Is>(std::index_sequence<Is...>) {
434 (func(
args[Is].get()), ...);
435 }(std::make_index_sequence<NumArgs>{});
442 return [&]<
size_t... Is>(std::index_sequence<Is...>) {
443 return static_cast<const Derived&
>(*this).eval(
args[Is]->
val...);
444 }(std::make_index_sequence<NumArgs>{});
450 ExpressionType
type() const final {
return Type; }
455 std::string_view
name() const final {
return Derived::NAME; }
459 [&]<
size_t... Is>(std::index_sequence<Is...>) {
461 ArgArray<Scalar> g{};
462 grad(g, this->
val, args[Is]->
val...);
465 ((
args[Is]->adj += this->
adj * g[Is]), ...);
466 }(std::make_index_sequence<NumArgs>{});
471 [&]<
size_t... Is>(std::index_sequence<Is...>) {
474 if constexpr (Derived::CONSTANT_PARTIALS) {
476 ArgArray<Scalar> g{};
477 grad(g, this->
val, args[Is]->
val...);
486 }
else if (g[Is] ==
Scalar(-1)) {
488 }
else if (g[Is] !=
Scalar(0)) {
489 args[Is]->adj_expr += this->adj_expr * constant_ptr(g[Is]);
495 ArgArray<ExpressionPtr<Scalar>> g;
503 if (g[Is] !=
nullptr) {
504 args[Is]->adj_expr += this->adj_expr * g[Is];
509 }(std::make_index_sequence<NumArgs>{});
525 template <
typename Value,
typename... Args>
526 void grad(ArgArray<Value>& g,
const Value&
val,
const Args&... x)
const {
527 if constexpr (
requires(
const Derived& expr) {
528 expr.eval_grad(g,
val, x...);
530 static_cast<const Derived&
>(*this).eval_grad(g,
val, x...);
544template <
typename T,
typename... Args>
545ExpressionPtr<typename T::Scalar> fold_or_make(
const Args&... args) {
546 using Scalar =
typename T::Scalar;
548 if (((args->type() == ExpressionType::CONSTANT) && ...)) {
549 Scalar val = T::eval(args->val...);
550 if constexpr (
sizeof...(Args) == 1) {
551 const auto& x = std::get<0>(std::forward_as_tuple(args...));
556 return constant_ptr(val);
559 return make_expression_ptr<T>(args...);
566template <
typename Scalar, ExpressionType T>
569 using BinaryMinusExpression::NonLeafExpression::NonLeafExpression;
572 static constexpr std::string_view
NAME =
"binary minus";
579 template <
typename Value>
580 static void eval_grad(ArgArray<Value>& g, [[maybe_unused]]
const Value&
val,
581 [[maybe_unused]]
const Value& lhs,
582 [[maybe_unused]]
const Value& rhs) {
583 g[0] = lit<Value>(
Scalar(1));
584 g[1] = lit<Value>(
Scalar(-1));
592template <
typename Scalar, ExpressionType T>
595 using BinaryPlusExpression::NonLeafExpression::NonLeafExpression;
598 static constexpr std::string_view
NAME =
"binary plus";
605 template <
typename Value>
606 static void eval_grad(ArgArray<Value>& g, [[maybe_unused]]
const Value&
val,
607 [[maybe_unused]]
const Value& lhs,
608 [[maybe_unused]]
const Value& rhs) {
609 g[0] = lit<Value>(
Scalar(1));
610 g[1] = lit<Value>(
Scalar(1));
618template <
typename Scalar, ExpressionType T>
621 using DivExpression::NonLeafExpression::NonLeafExpression;
624 static constexpr std::string_view
NAME =
"division";
628 template <
typename Value>
629 static void eval_grad(ArgArray<Value>& g, [[maybe_unused]]
const Value&
val,
630 const Value& lhs,
const Value& rhs) {
631 g[0] = lit<Value>(
Scalar(1)) / rhs;
632 g[1] = -lhs / (rhs * rhs);
640template <
typename Scalar, ExpressionType T>
643 using MultExpression::NonLeafExpression::NonLeafExpression;
646 static constexpr std::string_view
NAME =
"multiplication";
650 template <
typename Value>
651 static void eval_grad(ArgArray<Value>& g, [[maybe_unused]]
const Value&
val,
652 const Value& lhs,
const Value& rhs) {
662template <
typename Scalar, ExpressionType T>
665 using UnaryMinusExpression::NonLeafExpression::NonLeafExpression;
668 static constexpr std::string_view
NAME =
"unary minus";
675 template <
typename Value>
676 static void eval_grad(ArgArray<Value>& g, [[maybe_unused]]
const Value&
val,
677 [[maybe_unused]]
const Value& lhs) {
678 g[0] = lit<Value>(
Scalar(-1));
686template <
typename Scalar>
687constexpr void inc_ref_count(Expression<Scalar>* expr) {
696template <
typename Scalar>
697void deallocate_expression(Expression<Scalar>* expr) {
702 gch::small_vector<Expression<Scalar>*> stack;
703 stack.emplace_back(expr);
707 auto release = [&stack](Expression<Scalar>* child) {
708 if (--child->ref_count == 0) {
709 stack.emplace_back(child);
713 while (!stack.empty()) {
714 auto elem = stack.back();
717 if (elem->adj_expr !=
nullptr) {
718 release(elem->adj_expr.get());
720 elem->visit_args(release);
724 if constexpr (USE_POOL_ALLOCATOR) {
725 auto alloc = global_pool_allocator<Expression<Scalar>>();
726 std::allocator_traits<
decltype(alloc)>::deallocate(alloc, elem, 1);
728 operator delete(elem);
737template <
typename Scalar>
738constexpr void dec_ref_count(Expression<Scalar>* expr) {
739 if (--expr->ref_count == 0) {
740 deallocate_expression(expr);
747template <
typename Scalar>
757 ExpressionType
type()
const override {
return ExpressionType::CONSTANT; }
759 std::string_view
name()
const override {
return "constant"; }
762template <
typename Scalar>
763ExpressionPtr<Scalar> constant_ptr(Scalar value) {
764 return make_expression_ptr<ConstantExpression<Scalar>>(value);
770template <
typename Scalar>
783 ExpressionType
type()
const override {
return ExpressionType::LINEAR; }
785 std::string_view
name()
const override {
return "decision variable"; }
Definition intrusive_shared_ptr.hpp:27
Definition function_ref.hpp:13
Definition expression.hpp:568
static constexpr bool CONSTANT_PARTIALS
Whether the partial derivatives are the same at every argument value.
Definition expression.hpp:575
static constexpr std::string_view NAME
The expression's name.
Definition expression.hpp:572
Definition expression.hpp:594
static constexpr std::string_view NAME
The expression's name.
Definition expression.hpp:598
static constexpr bool CONSTANT_PARTIALS
Whether the partial derivatives are the same at every argument value.
Definition expression.hpp:601
Definition expression.hpp:748
ExpressionType type() const override
Definition expression.hpp:757
Scalar value() const override
Definition expression.hpp:755
std::string_view name() const override
Definition expression.hpp:759
constexpr ConstantExpression(Scalar value)
Definition expression.hpp:752
Definition expression.hpp:771
Scalar value() const override
Definition expression.hpp:781
std::string_view name() const override
Definition expression.hpp:785
constexpr DecisionVariableExpression(Scalar value)
Definition expression.hpp:778
ExpressionType type() const override
Definition expression.hpp:783
constexpr DecisionVariableExpression()
Constructs a decision variable expression with a value of zero.
Definition expression.hpp:773
Definition expression.hpp:620
static constexpr std::string_view NAME
The expression's name.
Definition expression.hpp:624
Definition expression.hpp:133
Scalar val
The value of the expression node.
Definition expression.hpp:138
virtual Scalar value() const =0
ExpressionPtr< Scalar > adj_expr
Definition expression.hpp:145
friend ExpressionPtr< Scalar > operator-(const ExpressionPtr< Scalar > &lhs)
Definition expression.hpp:325
virtual void accumulate_adjoints() const
Accumulates the child adjoints as Scalars.
Definition expression.hpp:376
uint32_t is_leaf
Definition expression.hpp:166
uint32_t ref_count
Reference count for intrusive shared pointer.
Definition expression.hpp:159
friend ExpressionPtr< Scalar > operator+(const ExpressionPtr< Scalar > &lhs, const ExpressionPtr< Scalar > &rhs)
Definition expression.hpp:257
int32_t scratch
Definition expression.hpp:156
Scalar adj
The adjoint of the expression node, used during autodiff.
Definition expression.hpp:141
constexpr Expression()=default
Constructs a constant expression with a value of zero.
Scalar_ Scalar
Scalar type alias.
Definition expression.hpp:135
virtual ExpressionType type() const =0
friend ExpressionPtr< Scalar > operator/(const ExpressionPtr< Scalar > &lhs, const ExpressionPtr< Scalar > &rhs)
Definition expression.hpp:229
friend ExpressionPtr< Scalar > operator-(const ExpressionPtr< Scalar > &lhs, const ExpressionPtr< Scalar > &rhs)
Definition expression.hpp:294
friend ExpressionPtr< Scalar > operator*(const ExpressionPtr< Scalar > &lhs, const ExpressionPtr< Scalar > &rhs)
Definition expression.hpp:190
virtual std::string_view name() const =0
virtual void accumulate_adjoints_expr() const
Accumulates the child adjoints as Expressions.
Definition expression.hpp:379
virtual void visit_args(function_ref< void(Expression< Scalar > *arg)> func) const
Definition expression.hpp:353
SLEIPNIR_FORCE_INLINE constexpr bool is_constant(Scalar constant) const
Definition expression.hpp:182
friend ExpressionPtr< Scalar > operator+=(ExpressionPtr< Scalar > &lhs, const ExpressionPtr< Scalar > &rhs)
Definition expression.hpp:285
constexpr Expression(Scalar value)
Definition expression.hpp:174
friend ExpressionPtr< Scalar > operator+(const ExpressionPtr< Scalar > &lhs)
Definition expression.hpp:346
Definition expression.hpp:642
static constexpr std::string_view NAME
The expression's name.
Definition expression.hpp:646
Definition expression.hpp:405
static constexpr bool CONSTANT_PARTIALS
Whether the partial derivatives are the same at every argument value.
Definition expression.hpp:413
constexpr NonLeafExpression(Args &&... args)
Definition expression.hpp:425
Scalar value() const final
Definition expression.hpp:441
void accumulate_adjoints_expr() const final
Accumulates the child adjoints as Expressions.
Definition expression.hpp:470
static constexpr size_t NUM_ARGS
The number of arguments.
Definition expression.hpp:407
std::array< ExpressionPtr< Scalar >, NumArgs > args
The arguments in visit_args() order.
Definition expression.hpp:416
ExpressionType type() const final
Definition expression.hpp:450
void accumulate_adjoints() const final
Accumulates the child adjoints as Scalars.
Definition expression.hpp:458
void visit_args(function_ref< void(Expression< Scalar > *arg)> func) const final
Definition expression.hpp:431
std::string_view name() const final
Definition expression.hpp:455
Definition expression.hpp:664
static constexpr std::string_view NAME
The expression's name.
Definition expression.hpp:668
static constexpr bool CONSTANT_PARTIALS
Whether the partial derivatives are the same at every argument value.
Definition expression.hpp:671