diff --git a/install_diff/Makefile b/install_diff/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c0e24c01c1880b927e65026c769a9116a47536b0 --- /dev/null +++ b/install_diff/Makefile @@ -0,0 +1,62 @@ +CXX = g++ +BIN = bin +LIB = lib +LIB_NAME = autodiff +OBJS_GRADIENT_DESCENT = root/obj/node.o root/obj/graph.o examples/obj/gradient_descent.o +OBJS_ANN = root/obj/node.o root/obj/graph.o examples/obj/ann.o +OBJS_GRADIENT = root/obj/node.o root/obj/graph.o examples/obj/gradient.o +OBJS_SPEED = root/obj/node.o root/obj/graph.o examples/obj/speed.o +OBJS_SIMPLE = root/obj/node.o root/obj/graph.o examples/obj/simple.o + +all : gradient_descent ann gradient speed simple + +gradient_descent : $(BIN) root/include/vectmath.h root/include/mor.h root/include/dor.h root/include/por.h + $(MAKE) -C examples obj obj/gradient_descent.o + $(MAKE) -C root obj obj/node.o obj/graph.o + $(CXX) -o $(BIN)/gradient_descent $(OBJS_GRADIENT_DESCENT) $(LIBS) + +ann : $(BIN) root/include/vectmath.h root/include/mor.h root/include/dor.h root/include/por.h + $(MAKE) -C examples obj obj/ann.o + $(MAKE) -C root obj obj/node.o obj/graph.o + $(CXX) -o $(BIN)/ann $(OBJS_ANN) $(LIBS) + +gradient : $(BIN) root/include/vectmath.h root/include/mor.h root/include/dor.h root/include/por.h + $(MAKE) -C examples obj obj/gradient.o + $(MAKE) -C root obj obj/node.o obj/graph.o + $(CXX) -o $(BIN)/gradient $(OBJS_GRADIENT) $(LIBS) + +speed : $(BIN) root/include/vectmath.h root/include/mor.h root/include/dor.h root/include/por.h + $(MAKE) -C examples obj obj/speed.o + $(MAKE) -C root obj obj/node.o obj/graph.o + $(CXX) -o $(BIN)/speed $(OBJS_SPEED) $(LIBS) + +simple : $(BIN) root/include/mor.h root/include/dor.h root/include/por.h + $(MAKE) -C examples obj obj/simple.o + $(MAKE) -C root obj obj/node.o obj/graph.o + $(CXX) -o $(BIN)/simple $(OBJS_SIMPLE) $(LIBS) + +$(BIN) : + if [ ! -d $(BIN) ]; then mkdir $(BIN); fi + +$(LIB) : + if [ ! -d $(LIB) ]; then mkdir $(LIB); fi + +clean : + $(MAKE) -C root clean + $(MAKE) -C examples clean + if [ -d $(BIN) ]; then rm $(BIN) -r; fi + +install : $(LIB) + ar rcs $(LIB)/lib$(LIB_NAME).a root/obj/graph.o root/obj/node.o + if [ ! -d /usr/local/include/$(LIB_NAME) ]; then sudo mkdir /usr/local/include/$(LIB_NAME); fi + sudo cp $(LIB)/lib$(LIB_NAME).a /usr/local/lib + sudo cp root/include/*.h /usr/local/include/$(LIB_NAME) + +.PHONY : all +.PHONY : gradient_descent +.PHONY : ann +.PHONY : gradient +.PHONY : speed +.PHONY : simple +.PHONY : clean +.PHONY : install diff --git a/install_diff/examples/Makefile b/install_diff/examples/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..5633ce1a4210616aa9b872a624d60464ef59e37a --- /dev/null +++ b/install_diff/examples/Makefile @@ -0,0 +1,31 @@ +CXX = g++ +ODIR = obj +CXXFLAGS = -std=c++11 -O3 +OBJS = $(ODIR)/gradient_descent.o $(ODIR)/ann.o $(ODIR)/gradient.o $(ODIR)/speed.o $(ODIR)/simple.o + +all : $(ODIR) $(OBJS) + +$(ODIR)/gradient_descent.o : src/gradient_descent.cpp ../root/include/vectmath.h ../root/include/node.h + $(CXX) -c $< -o $@ $(CXXFLAGS) + +$(ODIR)/ann.o : src/ann.cpp ../root/include/vectmath.h ../root/include/node.h + $(CXX) -c $< -o $@ $(CXXFLAGS) + +$(ODIR)/gradient.o : src/gradient.cpp ../root/include/vectmath.h ../root/include/node.h + $(CXX) -c $< -o $@ $(CXXFLAGS) + +$(ODIR)/speed.o : src/speed.cpp ../root/include/vectmath.h ../root/include/node.h + $(CXX) -c $< -o $@ $(CXXFLAGS) + +$(ODIR)/simple.o : src/simple.cpp ../root/include/node.h + $(CXX) -c $< -o $@ $(CXXFLAGS) + +$(ODIR) : + if [ ! -d $(ODIR) ]; then mkdir $(ODIR); fi + +clean : + if [ -d $(ODIR) ]; then rm $(ODIR) -r; fi + +.PHONY : all +.PHONY : clean + diff --git a/install_diff/examples/src/ann.cpp b/install_diff/examples/src/ann.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73da71d384cdffe307af63317e0f36b4c2e41f9d --- /dev/null +++ b/install_diff/examples/src/ann.cpp @@ -0,0 +1,126 @@ +#include +#include + +#include "../../root/include/vectmath.h" +#include "../../root/include/node.h" + +typedef std::vector Vector; +typedef std::vector Matrix; + +Node random_number(){ + return rand()/(double)RAND_MAX; +} + +Node tan_h(Node& x){ + return (1-exp(-2*x))/(1+exp(-2*x)); +} + +Node mean_square_error(Vector& y_true, Vector& y_pred){ + Node loss; + for(size_t i=0 ; iactivation = activation; + this->input_shape = input; + this->output_shape = output; + weights.resize(input, Vector(output)); + bias.resize(1, Vector(output)); + random_number >> weights; + random_number >> bias; + } + + Matrix forward(Matrix& previous){ + Matrix output = dot(previous, weights) + bias; + output = activation >> output; + return output; + } + + void backward(Node& loss, const float& learning_rate){ + weights -= learning_rate*loss.gradient(weights); + bias -= learning_rate*loss.gradient(bias); + } +}; + +struct Network { + std::vector layers; + int input_shape; + Graph* graph; + + Network(){ + graph = Graph::getInstance(); + } + + void input_layer(int input_shape){ + this->input_shape = input_shape; + } + + void add(int output_shape, Node (*activation)(Node&)){ + int input = layers.empty()?input_shape:layers.back().output_shape; + layers.push_back(Layer(input, output_shape, activation)); + } + + Matrix run(Matrix& input){ + Matrix output(input.size()); + for(size_t j=0 ; jnew_recording(); + } + } + std::cout << std::endl; + } +}; + +int main(int argc, char const *argv[]) { + srand(time(NULL)); + + Matrix input = {{0,0},{0,1},{1,0},{1,1}}; + Matrix output = {{0},{1},{1},{0}}; + + Network network; + network.input_layer(2); + network.add(3, tan_h); + network.add(1, tan_h); + network.fit(input, output, mean_square_error, 500, 0.1); + + Matrix pred = network.run(input); + std::cout << pred << std::endl; + return 0; +} diff --git a/install_diff/examples/src/gradient.cpp b/install_diff/examples/src/gradient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4b131de6ce60db38d19c97c26af8b19a97d10594 --- /dev/null +++ b/install_diff/examples/src/gradient.cpp @@ -0,0 +1,15 @@ +#include + +#include "../../root/include/vectmath.h" +#include "../../root/include/node.h" + +Node function(std::vector x){ + return pow(x[0]-x[1], 2) + x[0]*x[1]*x[2]; // (x-y)^2 + x*y*z +} + +int main(int argc, char const *argv[]) { + std::vector x = {5,6,7}; + Node f = function(x); + std::cout << "grad(f) = " << f.gradient(x) << std::endl; + return 0; +} diff --git a/install_diff/examples/src/gradient_descent.cpp b/install_diff/examples/src/gradient_descent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..019f7e64f22f6af278de7f3d66634ada8d9e0b35 --- /dev/null +++ b/install_diff/examples/src/gradient_descent.cpp @@ -0,0 +1,27 @@ +#include + +#include "../../root/include/vectmath.h" +#include "../../root/include/node.h" + +Node function(std::vector& x){ + return pow(x[0], 2) + pow(x[1], 2); // x^2 + y^2 +} + +int main(int argc, char const *argv[]) { + Graph* graph = Graph::getInstance(); + + std::vector x = {50, 50}; + Node f; + + int epochs = 30; + float learning_rate = 0.1; + for(size_t i=0 ; inew_recording(); + } + + std::cout << "f = " << f << std::endl; + std::cout << "x = " << x << std::endl; + return 0; +} diff --git a/install_diff/examples/src/simple.cpp b/install_diff/examples/src/simple.cpp new file mode 100644 index 0000000000000000000000000000000000000000..37dda1bdeac7640aeb9483ca57f1bdd5153d68fb --- /dev/null +++ b/install_diff/examples/src/simple.cpp @@ -0,0 +1,13 @@ +#include +#include "../../root/include/node.h" + +int main(int argc, char const *argv[]) { + Node x=2, y=3; + Node f = x*y + sin(x); + + std::cout << "f(x,y) = x*y + sin(x)" << std::endl; + std::cout << "f(" << x << "," << y << ") = " << f << std::endl; + std::cout << "∂f/∂x = " << f.gradient(x) << std::endl; + std::cout << "∂f/∂y = " << f.gradient(y) << std::endl; + return 0; +} diff --git a/install_diff/examples/src/speed.cpp b/install_diff/examples/src/speed.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fe58f78419606466adff35f4eaca9208010d842b --- /dev/null +++ b/install_diff/examples/src/speed.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include + +#include "../../root/include/vectmath.h" +#include "../../root/include/node.h" + +template +std::vector > get_random_matrix(const int& height, const int& width, T t){ + std::vector > mat(height, std::vector(width)); + for(auto& v : mat){ + for(auto& e : v){ + e = rand()/(double)RAND_MAX; + } + } + return mat; +} + +int main(int argc, char const *argv[]) { + srand(time(0)); + + int size = 30; + std::vector > a = get_random_matrix(size, size, double()); + std::vector > b = get_random_matrix(size, size, double()); + std::vector > c = get_random_matrix(size, size, Node()); + std::vector > d = get_random_matrix(size, size, Node()); + + std::cout << std::fixed; + std::cout << std::setprecision(10); + + std::cout << "Running with double...\t"; + std::cout.flush(); + auto start = std::chrono::high_resolution_clock::now(); + std::vector > ab = dot(a, b); + auto finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed = finish - start; + std::cout << "Elapsed time: " << elapsed.count() << " s" << std::endl; + + std::cout << "Running with Node...\t"; + std::cout.flush(); + start = std::chrono::high_resolution_clock::now(); + std::vector > cd = dot(c, d); + finish = std::chrono::high_resolution_clock::now(); + elapsed = finish - start; + std::cout << "Elapsed time: " << elapsed.count() << " s" << std::endl; + + std::cout << "Yet to be improved..." << std::endl; + return 0; +} diff --git a/install_diff/root/Makefile b/install_diff/root/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..1e9a102263e87393421c35aa54a462cb528d304b --- /dev/null +++ b/install_diff/root/Makefile @@ -0,0 +1,22 @@ +CXX = g++ +ODIR = obj +CXXFLAGS = -std=c++11 -O3 +OBJS = $(ODIR)/graph.o $(ODIR)/node.o + +all : $(ODIR) $(OBJS) + +$(ODIR)/graph.o : src/graph.cpp include/graph.h + $(CXX) -c $< -o $@ $(CXXFLAGS) + +$(ODIR)/node.o : src/node.cpp include/node.h + $(CXX) -c $< -o $@ $(CXXFLAGS) + +$(ODIR) : + if [ ! -d $(ODIR) ]; then mkdir $(ODIR); fi + +clean : + if [ -d $(ODIR) ]; then rm $(ODIR) -r; fi + +.PHONY : all +.PHONY : clean + diff --git a/install_diff/root/include/dor.h b/install_diff/root/include/dor.h new file mode 100644 index 0000000000000000000000000000000000000000..cfff375f13bb716beaa064935c9ad206953f5de1 --- /dev/null +++ b/install_diff/root/include/dor.h @@ -0,0 +1,16 @@ +#ifndef DYADIC_OPERATION_RESULT +#define DYADIC_OPERATION_RESULT + +struct DyadicOperationResult { + double value; + double left_grad; + double right_grad; + + DyadicOperationResult(double value, double left_grad, double right_grad){ + this->value = value; + this->left_grad = left_grad; + this->right_grad = right_grad; + } +}; + +#endif /* end of include guard: DYADIC_OPERATION_RESULT */ diff --git a/install_diff/root/include/graph.h b/install_diff/root/include/graph.h new file mode 100644 index 0000000000000000000000000000000000000000..441ccf8a5406c9c234d5bbd654023c60912cd831 --- /dev/null +++ b/install_diff/root/include/graph.h @@ -0,0 +1,26 @@ +#ifndef GRAPH_H +#define GRAPH_H + +#include +#include +#include + +class Graph { + private: + std::map > > nodes; + static Graph* instance; + Graph(); + + public: + static long int uid_counter; + static long int uid(); + static Graph* getInstance(); + + void connect(const long int& uid, const std::pair& edge); + std::vector > get(const long int& uid) const; + bool has(const long int& uid) const; + + void new_recording(); +}; + +#endif /* end of include guard: GRAPH_H */ diff --git a/install_diff/root/include/mor.h b/install_diff/root/include/mor.h new file mode 100644 index 0000000000000000000000000000000000000000..b2fe98bc5c49ab92dc050a849451ee1a2d75aa14 --- /dev/null +++ b/install_diff/root/include/mor.h @@ -0,0 +1,14 @@ +#ifndef MONADIC_OPERATION_RESULT +#define MONADIC_OPERATION_RESULT + +struct MonadicOperationResult { + double value; + double grad; + + MonadicOperationResult(double value, double grad){ + this->value = value; + this->grad = grad; + } +}; + +#endif /* end of include guard: MONADIC_OPERATION_RESULT */ diff --git a/install_diff/root/include/node.h b/install_diff/root/include/node.h new file mode 100644 index 0000000000000000000000000000000000000000..34c13304041ac7701f9a0d26e23cbc59e63c7837 --- /dev/null +++ b/install_diff/root/include/node.h @@ -0,0 +1,72 @@ +#ifndef NODE_H +#define NODE_H + +#include +#include + +#include "graph.h" +#include "mor.h" +#include "dor.h" +#include "por.h" + +class Node { + private: + double value; + long int uid; + + double gradient_recursive(Graph* graph, const long int& current_uid, const long int& stop_uid) const; + + public: + Node(const double& value=0); + Node(const Node& node); + + static Node monadic_operation(const Node& n, MonadicOperationResult (*)(const double&)); + static Node dyadic_operation(const Node& l, const Node& r, DyadicOperationResult (*)(const double&, const double&)); + static Node polyadic_operation(const std::vector& nodes, PolyadicOperationResult (*)(const std::vector&)); + + double gradient(const Node& node) const; + std::vector gradient(const std::vector& nodes) const; + std::vector > gradient(const std::vector >& nodes) const; + + friend Node operator+(const Node& l, const Node& r); + friend Node operator-(const Node& l, const Node& r); + friend Node operator*(const Node& l, const Node& r); + friend Node operator/(const Node& l, const Node& r); + + Node& operator+=(const Node& r); + Node& operator-=(const Node& r); + Node& operator*=(const Node& r); + Node& operator/=(const Node& r); + + friend bool operator==(const Node& l, const Node& r); + friend bool operator<(const Node& l, const Node& r); + friend bool operator>(const Node& l, const Node& r); + friend bool operator<=(const Node& l, const Node& r); + friend bool operator>=(const Node& l, const Node& r); + + friend Node sin(const Node& x); + friend Node cos(const Node& x); + friend Node tan(const Node& x); + friend Node sinh(const Node& x); + friend Node cosh(const Node& x); + friend Node tanh(const Node& x); + friend Node asin(const Node& x); + friend Node acos(const Node& x); + friend Node atan(const Node& x); + + friend Node log(const Node& x, const Node& base); + friend Node log10(const Node& x); + friend Node ln(const Node& x); + + friend Node pow(const Node& x, const Node& p); + friend Node exp(const Node& x); + friend Node sqrt(const Node& x); + + friend Node abs(const Node& x); + friend Node min(const Node& l, const Node& r); + friend Node max(const Node& l, const Node& r); + + friend std::ostream& operator<<(std::ostream& os, const Node& node); +}; + +#endif /* end of include guard: NODE_H */ diff --git a/install_diff/root/include/por.h b/install_diff/root/include/por.h new file mode 100644 index 0000000000000000000000000000000000000000..6eedd5099379fc242c6a05e1894616748fa55b44 --- /dev/null +++ b/install_diff/root/include/por.h @@ -0,0 +1,16 @@ +#ifndef POLYADIC_OPERATION_RESULT +#define POLYADIC_OPERATION_RESULT + +#include + +struct PolyadicOperationResult { + double value; + std::vector gradients; + + PolyadicOperationResult(double value, const std::vector& gradients){ + this->value = value; + this->gradients = gradients; + } +}; + +#endif /* end of include guard: POLYADIC_OPERATION_RESULT */ diff --git a/install_diff/root/include/vectmath.h b/install_diff/root/include/vectmath.h new file mode 100644 index 0000000000000000000000000000000000000000..7426398dead976a02f1ef50b123771215c098c46 --- /dev/null +++ b/install_diff/root/include/vectmath.h @@ -0,0 +1,99 @@ +#ifndef VECTMATH +#define VECTMATH + +#include +#include +#include +#include +#include + +// dot product +template +std::vector > dot(const std::vector >& a, const std::vector >& b){ + assert(a[0].size()==b.size()); + + T w=0; + std::vector > result(a.size(), std::vector(b[0].size())); + for (int i=0 ; i +std::vector& operator-=(std::vector& u, const std::vector& v){ + assert(u.size()==v.size()); + for(size_t i=0 ; i +std::vector operator+(const std::vector& u, const std::vector& v){ + assert(u.size()==v.size()); + std::vector w(u.size()); + for(size_t i=0 ; i +std::vector operator*(const S& s, const std::vector& u){ + std::vector result(u.size()); + for(size_t i=0 ; i +std::vector& operator>>(U (*fun)(U&), std::vector& u){ + std::transform(u.begin(), u.end(), u.begin(), fun); + return u; +} + +template +std::vector& operator>>(S (*fun)(S&), std::vector& u){ + for(auto& v : u){ + fun >> v; + } + return u; +} + +template +std::vector& operator>>(U (*fun)(), std::vector& u){ + for(auto& e : u){ + e = fun(); + } + return u; +} + +template +std::vector& operator>>(S (*fun)(), std::vector& u){ + for(auto& v : u){ + fun >> v; + } + return u; +} + +template +std::ostream& operator<<(std::ostream& os, const std::vector& u){ + os << "["; + for(size_t i=0 ; i& edge){ + nodes[uid].push_back(edge); +} + +std::vector > Graph::get(const long int& uid) const{ + return nodes.at(uid); +} + +bool Graph::has(const long int& uid) const{ + return nodes.find(uid)!=nodes.end(); +} + +void Graph::new_recording(){ + nodes.clear(); +} diff --git a/install_diff/root/src/node.cpp b/install_diff/root/src/node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dab3e44ed1519fbd41db3347b9a0a97a0580dc1f --- /dev/null +++ b/install_diff/root/src/node.cpp @@ -0,0 +1,275 @@ +#include "../include/node.h" + +Node::Node(const double& value) { + this->value = value; + this->uid = Graph::uid(); +} + +Node::Node(const Node& node){ + this->value = node.value; + this->uid = node.uid; +} + +double Node::gradient_recursive(Graph* graph, const long int& current_uid, const long int& stop_uid) const{ + if(current_uid==stop_uid){ + return 1.0; + } + + double sum=0.0; + if(graph->has(current_uid)){ + for(auto& pair : graph->get(current_uid)){ + sum += pair.first*gradient_recursive(graph, pair.second, stop_uid); + } + } + return sum; +} + +double Node::gradient(const Node& node) const{ + Graph* graph = Graph::getInstance(); + return gradient_recursive(graph, this->uid, node.uid); +} + +std::vector Node::gradient(const std::vector& nodes) const{ + Graph* graph = Graph::getInstance(); + std::vector grad(nodes.size()); + for(size_t i=0 ; iuid, nodes[i].uid); + } + return grad; +} + +std::vector > Node::gradient(const std::vector >& nodes) const{ + Graph* graph = Graph::getInstance(); + std::vector > grad(nodes.size()); + for(size_t i=0 ; iuid, nodes[i][j].uid); + } + } + return grad; +} + +Node Node::monadic_operation(const Node& n, MonadicOperationResult (*fun)(const double&)){ + MonadicOperationResult res = fun(n.value); + Node result(res.value); + Graph* graph = Graph::getInstance(); + graph->connect(result.uid, std::make_pair(res.grad, n.uid)); + return result; +} + +Node Node::dyadic_operation(const Node& left, const Node& right, DyadicOperationResult (*fun)(const double&, const double&)){ + DyadicOperationResult res = fun(left.value, right.value); + Node result(res.value); + Graph* graph = Graph::getInstance(); + graph->connect(result.uid, std::make_pair(res.left_grad, left.uid)); + graph->connect(result.uid, std::make_pair(res.right_grad, right.uid)); + return result; +} + +Node Node::polyadic_operation(const std::vector& nodes, PolyadicOperationResult (*fun)(const std::vector&)){ + std::vector values(nodes.size()); + for(size_t i=0 ; iconnect(result.uid, std::make_pair(res.gradients[i], nodes[i].uid)); + } + return result; +} + +Node operator+(const Node& left, const Node& right){ + return Node::dyadic_operation(left, right, [](const double& l, const double& r){ + return DyadicOperationResult(l+r, 1.0, 1.0); + }); +} + +Node operator-(const Node& left, const Node& right){ + return Node::dyadic_operation(left, right, [](const double& l, const double& r){ + return DyadicOperationResult(l-r, 1.0, -1.0); + }); +} + +Node operator*(const Node& left, const Node& right){ + return Node::dyadic_operation(left, right, [](const double& l, const double& r){ + return DyadicOperationResult(l*r, r, l); + }); +} + +Node operator/(const Node& left, const Node& right){ + return Node::dyadic_operation(left, right, [](const double& l, const double& r){ + return DyadicOperationResult(l/r, 1.0/r, -1.0*l/(r*r)); + }); +} + +Node& Node::operator+=(const Node& r){ + *this = *this + r; + return *this; +} + +Node& Node::operator-=(const Node& r){ + *this = *this - r; + return *this; +} + +Node& Node::operator*=(const Node& r){ + *this = *this * r; + return *this; +} + +Node& Node::operator/=(const Node& r){ + *this = *this / r; + return *this; +} + +bool operator==(const Node& left, const Node& right){ + return left.value==right.value; +} + +bool operator<(const Node& left, const Node& right){ + return left.value(const Node& left, const Node& right){ + return left.value>right.value; +} + +bool operator<=(const Node& left, const Node& right){ + return left.value<=right.value; +} + +bool operator>=(const Node& left, const Node& right){ + return left.value>=right.value; +} + +Node sin(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::sin(n), ::cos(n)); + }); +} + +Node cos(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::cos(n), -1.0*::sin(n)); + }); +} + +Node tan(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::tan(n), 1.0/::pow(::cos(n), 2)); + }); +} + +Node sinh(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::sinh(n), ::cosh(n)); + }); +} + +Node cosh(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::cosh(n), ::sinh(n)); + }); +} + +Node asin(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::asin(n), 1.0/(::sqrt(1-n*n))); + }); +} + +Node acos(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::acos(n), -1.0/(::sqrt(1-n*n))); + }); +} + +Node atan(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::atan(n), 1.0/(1+n*n)); + }); +} + +Node tanh(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::tanh(n), 1.0-::pow(::tanh(n), 2)); + }); +} + +Node log(const Node& x, const Node& base){ + return Node::dyadic_operation(x, base, [](const double& a, const double& b){ + return DyadicOperationResult(::log(a)/::log(b), 1.0/(a*::log(b)), -1.0*::log(a)/(b*::log(b))); + }); +} + +Node log10(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::log(n)/::log(10), 1.0/(n*::log(10))); + }); +} + +Node ln(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::log(n), 1.0/::log(n)); + }); +} + +Node pow(const Node& x, const Node& base){ + return Node::dyadic_operation(x, base, [](const double& a, const double& b){ + if(a<=0){ + return DyadicOperationResult(::pow(a,b), b*::pow(a,b-1), 0); + } + return DyadicOperationResult(::pow(a,b), b*::pow(a,b-1), ::log(a)*::pow(a,b)); + }); +} + +Node exp(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::exp(n), ::exp(n)); + }); +} + +Node sqrt(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + return MonadicOperationResult(::sqrt(n), 1.0/(2*::sqrt(n))); + }); +} + +Node abs(const Node& x){ + return Node::monadic_operation(x, [](const double& n){ + int sign = n==0 ? 0 : n/::abs(n); + return MonadicOperationResult(::abs(n), sign); + }); +} + +Node min(const Node& left, const Node& right){ + return Node::dyadic_operation(left, right, [](const double& a, const double& b){ + if(ab){ + return DyadicOperationResult(b, 0, 1); + } + return DyadicOperationResult(a, 0, 0); + }); +} + +Node max(const Node& left, const Node& right){ + return Node::dyadic_operation(left, right, [](const double& a, const double& b){ + if(a>b){ + return DyadicOperationResult(a, 1, 0); + } + if(a