#pragma once
#include <stdint.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <memory>
#include <numbers>
#include <utility>
#include <gch/small_vector.hpp>
#include "sleipnir/autodiff/expression_type.hpp"
#include "sleipnir/util/intrusive_shared_ptr.hpp"
#include "sleipnir/util/pool.hpp"
namespace slp::detail {
#ifdef _WIN32
inline constexpr bool USE_POOL_ALLOCATOR = false;
#else
inline constexpr bool USE_POOL_ALLOCATOR = true;
#endif
struct Expression;
inline constexpr void inc_ref_count(Expression* expr);
inline void dec_ref_count(Expression* expr);
using ExpressionPtr = IntrusiveSharedPtr<Expression>;
template <typename T, typename... Args>
static ExpressionPtr make_expression_ptr(Args&&... args) {
if constexpr (USE_POOL_ALLOCATOR) {
return allocate_intrusive_shared<T>(global_pool_allocator<T>(),
std::forward<Args>(args)...);
} else {
return make_intrusive_shared<T>(std::forward<Args>(args)...);
}
}
template <ExpressionType T>
struct BinaryMinusExpression;
template <ExpressionType T>
struct BinaryPlusExpression;
struct ConstExpression;
template <ExpressionType T>
struct DivExpression;
template <ExpressionType T>
struct MultExpression;
template <ExpressionType T>
struct UnaryMinusExpression;
struct Expression {
double val = 0.0;
double adjoint = 0.0;
uint32_t incoming_edges = 0;
int32_t col = -1;
ExpressionPtr adjoint_expr;
uint32_t ref_count = 0;
std::array<ExpressionPtr, 2> args{nullptr, nullptr};
constexpr Expression() = default;
explicit constexpr Expression(double value) : val{value} {}
explicit constexpr Expression(ExpressionPtr lhs)
: args{std::move(lhs), nullptr} {}
constexpr Expression(ExpressionPtr lhs, ExpressionPtr rhs)
: args{std::move(lhs), std::move(rhs)} {}
virtual ~Expression() = default;
constexpr bool is_constant(double constant) const {
return type() == ExpressionType::CONSTANT && val == constant;
}
friend ExpressionPtr operator*(const ExpressionPtr& lhs,
const ExpressionPtr& rhs) {
using enum ExpressionType;
if (lhs->is_constant(0.0)) {
return lhs;
} else if (rhs->is_constant(0.0)) {
return rhs;
} else if (lhs->is_constant(1.0)) {
return rhs;
} else if (rhs->is_constant(1.0)) {
return lhs;
}
if (lhs->type() == CONSTANT && rhs->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(lhs->val * rhs->val);
}
if (lhs->type() == CONSTANT) {
if (rhs->type() == LINEAR) {
return make_expression_ptr<MultExpression<LINEAR>>(lhs, rhs);
} else if (rhs->type() == QUADRATIC) {
return make_expression_ptr<MultExpression<QUADRATIC>>(lhs, rhs);
} else {
return make_expression_ptr<MultExpression<NONLINEAR>>(lhs, rhs);
}
} else if (rhs->type() == CONSTANT) {
if (lhs->type() == LINEAR) {
return make_expression_ptr<MultExpression<LINEAR>>(lhs, rhs);
} else if (lhs->type() == QUADRATIC) {
return make_expression_ptr<MultExpression<QUADRATIC>>(lhs, rhs);
} else {
return make_expression_ptr<MultExpression<NONLINEAR>>(lhs, rhs);
}
} else if (lhs->type() == LINEAR && rhs->type() == LINEAR) {
return make_expression_ptr<MultExpression<QUADRATIC>>(lhs, rhs);
} else {
return make_expression_ptr<MultExpression<NONLINEAR>>(lhs, rhs);
}
}
friend ExpressionPtr operator/(const ExpressionPtr& lhs,
const ExpressionPtr& rhs) {
using enum ExpressionType;
if (lhs->is_constant(0.0)) {
return lhs;
} else if (rhs->is_constant(1.0)) {
return lhs;
}
if (lhs->type() == CONSTANT && rhs->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(lhs->val / rhs->val);
}
if (rhs->type() == CONSTANT) {
if (lhs->type() == LINEAR) {
return make_expression_ptr<DivExpression<LINEAR>>(lhs, rhs);
} else if (lhs->type() == QUADRATIC) {
return make_expression_ptr<DivExpression<QUADRATIC>>(lhs, rhs);
} else {
return make_expression_ptr<DivExpression<NONLINEAR>>(lhs, rhs);
}
} else {
return make_expression_ptr<DivExpression<NONLINEAR>>(lhs, rhs);
}
}
friend ExpressionPtr operator+(const ExpressionPtr& lhs,
const ExpressionPtr& rhs) {
using enum ExpressionType;
if (lhs == nullptr || lhs->is_constant(0.0)) {
return rhs;
} else if (rhs == nullptr || rhs->is_constant(0.0)) {
return lhs;
}
if (lhs->type() == CONSTANT && rhs->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(lhs->val + rhs->val);
}
auto type = std::max(lhs->type(), rhs->type());
if (type == LINEAR) {
return make_expression_ptr<BinaryPlusExpression<LINEAR>>(lhs, rhs);
} else if (type == QUADRATIC) {
return make_expression_ptr<BinaryPlusExpression<QUADRATIC>>(lhs, rhs);
} else {
return make_expression_ptr<BinaryPlusExpression<NONLINEAR>>(lhs, rhs);
}
}
friend ExpressionPtr operator+=(ExpressionPtr& lhs,
const ExpressionPtr& rhs) {
return lhs = lhs + rhs;
}
friend ExpressionPtr operator-(const ExpressionPtr& lhs,
const ExpressionPtr& rhs) {
using enum ExpressionType;
if (lhs->is_constant(0.0)) {
if (rhs->is_constant(0.0)) {
return rhs;
} else {
return -rhs;
}
} else if (rhs->is_constant(0.0)) {
return lhs;
}
if (lhs->type() == CONSTANT && rhs->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(lhs->val - rhs->val);
}
auto type = std::max(lhs->type(), rhs->type());
if (type == LINEAR) {
return make_expression_ptr<BinaryMinusExpression<LINEAR>>(lhs, rhs);
} else if (type == QUADRATIC) {
return make_expression_ptr<BinaryMinusExpression<QUADRATIC>>(lhs, rhs);
} else {
return make_expression_ptr<BinaryMinusExpression<NONLINEAR>>(lhs, rhs);
}
}
friend ExpressionPtr operator-(const ExpressionPtr& lhs) {
using enum ExpressionType;
if (lhs->is_constant(0.0)) {
return lhs;
}
if (lhs->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(-lhs->val);
}
if (lhs->type() == LINEAR) {
return make_expression_ptr<UnaryMinusExpression<LINEAR>>(lhs);
} else if (lhs->type() == QUADRATIC) {
return make_expression_ptr<UnaryMinusExpression<QUADRATIC>>(lhs);
} else {
return make_expression_ptr<UnaryMinusExpression<NONLINEAR>>(lhs);
}
}
friend ExpressionPtr operator+(const ExpressionPtr& lhs) { return lhs; }
virtual double value([[maybe_unused]] double lhs,
[[maybe_unused]] double rhs) const = 0;
virtual ExpressionType type() const = 0;
virtual double grad_l([[maybe_unused]] double lhs,
[[maybe_unused]] double rhs,
[[maybe_unused]] double parent_adjoint) const {
return 0.0;
}
virtual double grad_r([[maybe_unused]] double lhs,
[[maybe_unused]] double rhs,
[[maybe_unused]] double parent_adjoint) const {
return 0.0;
}
virtual ExpressionPtr grad_expr_l(
[[maybe_unused]] const ExpressionPtr& lhs,
[[maybe_unused]] const ExpressionPtr& rhs,
[[maybe_unused]] const ExpressionPtr& parent_adjoint) const {
return make_expression_ptr<ConstExpression>();
}
virtual ExpressionPtr grad_expr_r(
[[maybe_unused]] const ExpressionPtr& lhs,
[[maybe_unused]] const ExpressionPtr& rhs,
[[maybe_unused]] const ExpressionPtr& parent_adjoint) const {
return make_expression_ptr<ConstExpression>();
}
};
inline ExpressionPtr cbrt(const ExpressionPtr& x);
inline ExpressionPtr exp(const ExpressionPtr& x);
inline ExpressionPtr sin(const ExpressionPtr& x);
inline ExpressionPtr sinh(const ExpressionPtr& x);
inline ExpressionPtr sqrt(const ExpressionPtr& x);
template <ExpressionType T>
struct BinaryMinusExpression final : Expression {
constexpr BinaryMinusExpression(ExpressionPtr lhs, ExpressionPtr rhs)
: Expression{std::move(lhs), std::move(rhs)} {}
double value(double lhs, double rhs) const override { return lhs - rhs; }
ExpressionType type() const override { return T; }
double grad_l(double, double, double parent_adjoint) const override {
return parent_adjoint;
}
double grad_r(double, double, double parent_adjoint) const override {
return -parent_adjoint;
}
ExpressionPtr grad_expr_l(
const ExpressionPtr&, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint;
}
ExpressionPtr grad_expr_r(
const ExpressionPtr&, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return -parent_adjoint;
}
};
template <ExpressionType T>
struct BinaryPlusExpression final : Expression {
constexpr BinaryPlusExpression(ExpressionPtr lhs, ExpressionPtr rhs)
: Expression{std::move(lhs), std::move(rhs)} {}
double value(double lhs, double rhs) const override { return lhs + rhs; }
ExpressionType type() const override { return T; }
double grad_l(double, double, double parent_adjoint) const override {
return parent_adjoint;
}
double grad_r(double, double, double parent_adjoint) const override {
return parent_adjoint;
}
ExpressionPtr grad_expr_l(
const ExpressionPtr&, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint;
}
ExpressionPtr grad_expr_r(
const ExpressionPtr&, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint;
}
};
struct CbrtExpression final : Expression {
explicit constexpr CbrtExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::cbrt(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
double c = std::cbrt(x);
return parent_adjoint / (3.0 * c * c);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
auto c = slp::detail::cbrt(x);
return parent_adjoint / (make_expression_ptr<ConstExpression>(3.0) * c * c);
}
};
inline ExpressionPtr cbrt(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->type() == CONSTANT) {
if (x->val == 0.0) {
return x;
} else if (x->val == -1.0 || x->val == 1.0) {
return x;
} else {
return make_expression_ptr<ConstExpression>(std::cbrt(x->val));
}
}
return make_expression_ptr<CbrtExpression>(x);
}
struct ConstExpression final : Expression {
constexpr ConstExpression() = default;
explicit constexpr ConstExpression(double value) : Expression{value} {}
double value(double, double) const override { return val; }
ExpressionType type() const override { return ExpressionType::CONSTANT; }
};
struct DecisionVariableExpression final : Expression {
constexpr DecisionVariableExpression() = default;
explicit constexpr DecisionVariableExpression(double value)
: Expression{value} {}
double value(double, double) const override { return val; }
ExpressionType type() const override { return ExpressionType::LINEAR; }
};
template <ExpressionType T>
struct DivExpression final : Expression {
constexpr DivExpression(ExpressionPtr lhs, ExpressionPtr rhs)
: Expression{std::move(lhs), std::move(rhs)} {}
double value(double lhs, double rhs) const override { return lhs / rhs; }
ExpressionType type() const override { return T; }
double grad_l(double, double rhs, double parent_adjoint) const override {
return parent_adjoint / rhs;
};
double grad_r(double lhs, double rhs, double parent_adjoint) const override {
return parent_adjoint * -lhs / (rhs * rhs);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr&, const ExpressionPtr& rhs,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint / rhs;
}
ExpressionPtr grad_expr_r(
const ExpressionPtr& lhs, const ExpressionPtr& rhs,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint * -lhs / (rhs * rhs);
}
};
template <ExpressionType T>
struct MultExpression final : Expression {
constexpr MultExpression(ExpressionPtr lhs, ExpressionPtr rhs)
: Expression{std::move(lhs), std::move(rhs)} {}
double value(double lhs, double rhs) const override { return lhs * rhs; }
ExpressionType type() const override { return T; }
double grad_l([[maybe_unused]] double lhs, double rhs,
double parent_adjoint) const override {
return parent_adjoint * rhs;
}
double grad_r(double lhs, [[maybe_unused]] double rhs,
double parent_adjoint) const override {
return parent_adjoint * lhs;
}
ExpressionPtr grad_expr_l(
[[maybe_unused]] const ExpressionPtr& lhs, const ExpressionPtr& rhs,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint * rhs;
}
ExpressionPtr grad_expr_r(
const ExpressionPtr& lhs, [[maybe_unused]] const ExpressionPtr& rhs,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint * lhs;
}
};
template <ExpressionType T>
struct UnaryMinusExpression final : Expression {
explicit constexpr UnaryMinusExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double lhs, double) const override { return -lhs; }
ExpressionType type() const override { return T; }
double grad_l(double, double, double parent_adjoint) const override {
return -parent_adjoint;
}
ExpressionPtr grad_expr_l(
const ExpressionPtr&, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return -parent_adjoint;
}
};
inline constexpr void inc_ref_count(Expression* expr) {
++expr->ref_count;
}
inline void dec_ref_count(Expression* expr) {
gch::small_vector<Expression*> stack;
stack.emplace_back(expr);
while (!stack.empty()) {
auto elem = stack.back();
stack.pop_back();
if (--elem->ref_count == 0) {
if (elem->adjoint_expr != nullptr) {
stack.emplace_back(elem->adjoint_expr.get());
}
for (auto& arg : elem->args) {
if (arg != nullptr) {
stack.emplace_back(arg.get());
}
}
if constexpr (USE_POOL_ALLOCATOR) {
auto alloc = global_pool_allocator<Expression>();
std::allocator_traits<decltype(alloc)>::deallocate(alloc, elem,
sizeof(Expression));
}
}
}
}
struct AbsExpression final : Expression {
explicit constexpr AbsExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::abs(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
if (x < 0.0) {
return -parent_adjoint;
} else if (x > 0.0) {
return parent_adjoint;
} else {
return 0.0;
}
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
if (x->val < 0.0) {
return -parent_adjoint;
} else if (x->val > 0.0) {
return parent_adjoint;
} else {
return make_expression_ptr<ConstExpression>();
}
}
};
inline ExpressionPtr abs(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return x;
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::abs(x->val));
}
return make_expression_ptr<AbsExpression>(x);
}
struct AcosExpression final : Expression {
explicit constexpr AcosExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::acos(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return -parent_adjoint / std::sqrt(1.0 - x * x);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return -parent_adjoint /
slp::detail::sqrt(make_expression_ptr<ConstExpression>(1.0) - x * x);
}
};
inline ExpressionPtr acos(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return make_expression_ptr<ConstExpression>(std::numbers::pi / 2.0);
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::acos(x->val));
}
return make_expression_ptr<AcosExpression>(x);
}
struct AsinExpression final : Expression {
explicit constexpr AsinExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::asin(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return parent_adjoint / std::sqrt(1.0 - x * x);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint /
slp::detail::sqrt(make_expression_ptr<ConstExpression>(1.0) - x * x);
}
};
inline ExpressionPtr asin(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return x;
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::asin(x->val));
}
return make_expression_ptr<AsinExpression>(x);
}
struct AtanExpression final : Expression {
explicit constexpr AtanExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::atan(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return parent_adjoint / (1.0 + x * x);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint / (make_expression_ptr<ConstExpression>(1.0) + x * x);
}
};
inline ExpressionPtr atan(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return x;
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::atan(x->val));
}
return make_expression_ptr<AtanExpression>(x);
}
struct Atan2Expression final : Expression {
constexpr Atan2Expression(ExpressionPtr lhs, ExpressionPtr rhs)
: Expression{std::move(lhs), std::move(rhs)} {}
double value(double y, double x) const override { return std::atan2(y, x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double y, double x, double parent_adjoint) const override {
return parent_adjoint * x / (y * y + x * x);
}
double grad_r(double y, double x, double parent_adjoint) const override {
return parent_adjoint * -y / (y * y + x * x);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& y, const ExpressionPtr& x,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint * x / (y * y + x * x);
}
ExpressionPtr grad_expr_r(
const ExpressionPtr& y, const ExpressionPtr& x,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint * -y / (y * y + x * x);
}
};
inline ExpressionPtr atan2(const ExpressionPtr& y, const ExpressionPtr& x) {
using enum ExpressionType;
if (y->is_constant(0.0)) {
return y;
} else if (x->is_constant(0.0)) {
return make_expression_ptr<ConstExpression>(std::numbers::pi / 2.0);
}
if (y->type() == CONSTANT && x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::atan2(y->val, x->val));
}
return make_expression_ptr<Atan2Expression>(y, x);
}
struct CosExpression final : Expression {
explicit constexpr CosExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::cos(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return -parent_adjoint * std::sin(x);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint * -slp::detail::sin(x);
}
};
inline ExpressionPtr cos(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return make_expression_ptr<ConstExpression>(1.0);
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::cos(x->val));
}
return make_expression_ptr<CosExpression>(x);
}
struct CoshExpression final : Expression {
explicit constexpr CoshExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::cosh(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return parent_adjoint * std::sinh(x);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint * slp::detail::sinh(x);
}
};
inline ExpressionPtr cosh(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return make_expression_ptr<ConstExpression>(1.0);
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::cosh(x->val));
}
return make_expression_ptr<CoshExpression>(x);
}
struct ErfExpression final : Expression {
explicit constexpr ErfExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::erf(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return parent_adjoint * 2.0 * std::numbers::inv_sqrtpi * std::exp(-x * x);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint *
make_expression_ptr<ConstExpression>(2.0 *
std::numbers::inv_sqrtpi) *
slp::detail::exp(-x * x);
}
};
inline ExpressionPtr erf(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return x;
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::erf(x->val));
}
return make_expression_ptr<ErfExpression>(x);
}
struct ExpExpression final : Expression {
explicit constexpr ExpExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::exp(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return parent_adjoint * std::exp(x);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint * slp::detail::exp(x);
}
};
inline ExpressionPtr exp(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return make_expression_ptr<ConstExpression>(1.0);
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::exp(x->val));
}
return make_expression_ptr<ExpExpression>(x);
}
inline ExpressionPtr hypot(const ExpressionPtr& x, const ExpressionPtr& y);
struct HypotExpression final : Expression {
constexpr HypotExpression(ExpressionPtr lhs, ExpressionPtr rhs)
: Expression{std::move(lhs), std::move(rhs)} {}
double value(double x, double y) const override { return std::hypot(x, y); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double y, double parent_adjoint) const override {
return parent_adjoint * x / std::hypot(x, y);
}
double grad_r(double x, double y, double parent_adjoint) const override {
return parent_adjoint * y / std::hypot(x, y);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr& y,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint * x / slp::detail::hypot(x, y);
}
ExpressionPtr grad_expr_r(
const ExpressionPtr& x, const ExpressionPtr& y,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint * y / slp::detail::hypot(x, y);
}
};
inline ExpressionPtr hypot(const ExpressionPtr& x, const ExpressionPtr& y) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return y;
} else if (y->is_constant(0.0)) {
return x;
}
if (x->type() == CONSTANT && y->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::hypot(x->val, y->val));
}
return make_expression_ptr<HypotExpression>(x, y);
}
struct LogExpression final : Expression {
explicit constexpr LogExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::log(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return parent_adjoint / x;
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint / x;
}
};
inline ExpressionPtr log(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return x;
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::log(x->val));
}
return make_expression_ptr<LogExpression>(x);
}
struct Log10Expression final : Expression {
explicit constexpr Log10Expression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::log10(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return parent_adjoint / (std::numbers::ln10 * x);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint /
(make_expression_ptr<ConstExpression>(std::numbers::ln10) * x);
}
};
inline ExpressionPtr log10(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return x;
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::log10(x->val));
}
return make_expression_ptr<Log10Expression>(x);
}
inline ExpressionPtr pow(const ExpressionPtr& base, const ExpressionPtr& power);
template <ExpressionType T>
struct PowExpression final : Expression {
constexpr PowExpression(ExpressionPtr lhs, ExpressionPtr rhs)
: Expression{std::move(lhs), std::move(rhs)} {}
double value(double base, double power) const override {
return std::pow(base, power);
}
ExpressionType type() const override { return T; }
double grad_l(double base, double power,
double parent_adjoint) const override {
return parent_adjoint * std::pow(base, power - 1) * power;
}
double grad_r(double base, double power,
double parent_adjoint) const override {
if (base == 0.0) {
return 0.0;
} else {
return parent_adjoint * std::pow(base, power - 1) * base * std::log(base);
}
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& base, const ExpressionPtr& power,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint *
slp::detail::pow(base,
power - make_expression_ptr<ConstExpression>(1.0)) *
power;
}
ExpressionPtr grad_expr_r(
const ExpressionPtr& base, const ExpressionPtr& power,
const ExpressionPtr& parent_adjoint) const override {
if (base->val == 0.0) {
return base;
} else {
return parent_adjoint *
slp::detail::pow(
base, power - make_expression_ptr<ConstExpression>(1.0)) *
base * slp::detail::log(base);
}
}
};
inline ExpressionPtr pow(const ExpressionPtr& base,
const ExpressionPtr& power) {
using enum ExpressionType;
if (base->is_constant(0.0)) {
return base;
} else if (base->is_constant(1.0)) {
return base;
}
if (power->is_constant(0.0)) {
return make_expression_ptr<ConstExpression>(1.0);
} else if (power->is_constant(1.0)) {
return base;
}
if (base->type() == CONSTANT && power->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(
std::pow(base->val, power->val));
}
if (power->is_constant(2.0)) {
if (base->type() == LINEAR) {
return make_expression_ptr<MultExpression<QUADRATIC>>(base, base);
} else {
return make_expression_ptr<MultExpression<NONLINEAR>>(base, base);
}
}
return make_expression_ptr<PowExpression<NONLINEAR>>(base, power);
}
struct SignExpression final : Expression {
explicit constexpr SignExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override {
if (x < 0.0) {
return -1.0;
} else if (x == 0.0) {
return 0.0;
} else {
return 1.0;
}
}
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double, double, double) const override { return 0.0; }
ExpressionPtr grad_expr_l(const ExpressionPtr&, const ExpressionPtr&,
const ExpressionPtr&) const override {
return make_expression_ptr<ConstExpression>();
}
};
inline ExpressionPtr sign(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->type() == CONSTANT) {
if (x->val < 0.0) {
return make_expression_ptr<ConstExpression>(-1.0);
} else if (x->val == 0.0) {
return x;
} else {
return make_expression_ptr<ConstExpression>(1.0);
}
}
return make_expression_ptr<SignExpression>(x);
}
struct SinExpression final : Expression {
explicit constexpr SinExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::sin(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return parent_adjoint * std::cos(x);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint * slp::detail::cos(x);
}
};
inline ExpressionPtr sin(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return x;
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::sin(x->val));
}
return make_expression_ptr<SinExpression>(x);
}
struct SinhExpression final : Expression {
explicit constexpr SinhExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::sinh(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return parent_adjoint * std::cosh(x);
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint * slp::detail::cosh(x);
}
};
inline ExpressionPtr sinh(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return x;
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::sinh(x->val));
}
return make_expression_ptr<SinhExpression>(x);
}
struct SqrtExpression final : Expression {
explicit constexpr SqrtExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::sqrt(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return parent_adjoint / (2.0 * std::sqrt(x));
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint /
(make_expression_ptr<ConstExpression>(2.0) * slp::detail::sqrt(x));
}
};
inline ExpressionPtr sqrt(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->type() == CONSTANT) {
if (x->val == 0.0) {
return x;
} else if (x->val == 1.0) {
return x;
} else {
return make_expression_ptr<ConstExpression>(std::sqrt(x->val));
}
}
return make_expression_ptr<SqrtExpression>(x);
}
struct TanExpression final : Expression {
explicit constexpr TanExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::tan(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return parent_adjoint / (std::cos(x) * std::cos(x));
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint / (slp::detail::cos(x) * slp::detail::cos(x));
}
};
inline ExpressionPtr tan(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return x;
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::tan(x->val));
}
return make_expression_ptr<TanExpression>(x);
}
struct TanhExpression final : Expression {
explicit constexpr TanhExpression(ExpressionPtr lhs)
: Expression{std::move(lhs)} {}
double value(double x, double) const override { return std::tanh(x); }
ExpressionType type() const override { return ExpressionType::NONLINEAR; }
double grad_l(double x, double, double parent_adjoint) const override {
return parent_adjoint / (std::cosh(x) * std::cosh(x));
}
ExpressionPtr grad_expr_l(
const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parent_adjoint) const override {
return parent_adjoint / (slp::detail::cosh(x) * slp::detail::cosh(x));
}
};
inline ExpressionPtr tanh(const ExpressionPtr& x) {
using enum ExpressionType;
if (x->is_constant(0.0)) {
return x;
}
if (x->type() == CONSTANT) {
return make_expression_ptr<ConstExpression>(std::tanh(x->val));
}
return make_expression_ptr<TanhExpression>(x);
}
}