Skip to content
Snippets Groups Projects
cube_root.hpp 12.21 KiB
// Copyright 2021 Thomas A. R. Purcell
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/cube_root.hpp
 *  @brief Defines a class for the cube root operator
 *
 *  @author Thomas A. R. Purcell (tpurcell90)
 *  @bug No known bugs.
 *
 *  This class represents the unary operator -> (A)^(1/3)
 */

#ifndef CBRT_NODE
#define CBRT_NODE

#include "feature_creation/node/operator_nodes/OperatorNode.hpp"
#include <fmt/core.h>

// DocString: cls_cbrt_node
/**
 * @brief Node for the cube root operator
 *
 */
class CbrtNode: public OperatorNode<1>
{
    friend class boost::serialization::access;

    /**
     * @brief Serialization function to send over MPI
     *
     * @param ar Archive representation of node
     */
    template <typename Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        ar & boost::serialization::base_object<OperatorNode>(*this);
    }
public:
    /**
     * @brief Base Constructor
     * @details This is only used for serialization
     */
    CbrtNode();

    /**
     * @brief Constructor excluding bounds on the maximum absolute value of the Node
     *
     * @param feat (Node) shared_ptr of the feature to operate on (A)
     * @param feat_ind (int) Index of the new feature
     */
    CbrtNode(const node_ptr feat, const unsigned long int feat_ind);

    // DocString: cbrt_node_init
    /**
     * @brief Constructor including bounds on the maximum absolute value of the Node
     *
     * @param feat (Node) shared_ptr of the feature to operate on (A)
     * @param feat_ind (int) Index of the new feature
     * @param l_bound (double) Minimum absolute value allowed for the feature.
     * @param u_bound (double) Maximum absolute value allowed for the feature.
     */
    CbrtNode(const node_ptr feat, const unsigned long int feat_ind, const double l_bound, const double u_bound);

    /**
     * @brief Makes a hard copy node (All members of the Node are independent of the original one)
     * @return A shared_ptr to the copied node
     */
    virtual node_ptr hard_copy()const;

    // DocString: cbrt_node_unit
    /**
     * @brief Get the unit of the feature (combine the units of _feats)
     */
    inline Unit unit() const {return _feats[0]->unit()^(1.0 / 3.0);}

    // DocString: cbrt_node_expr
    /**
     * @brief A human readable equation representing the feature
     */
    inline std::string expr() const
    {
        return fmt::format(
            "cbrt({})",
            _feats[0]->expr()
        );
    }

    // DocString: cbrt_node_latex_expr
    /**
     * @brief Get the valid LaTeX expression that represents the feature
     */
    inline std::string get_latex_expr() const
    {
        return fmt::format(
            "\\left(\\sqrt[3]{{ {} }}\\right)",
            _feats[0]->get_latex_expr()
        );
    }

    // DocString: cbrt_node_set_value
    /**
     * @brief Set the value of all training samples for the feature inside the central data storage array
     *
     * @param offset (int) Where the current node is in the binary expression tree relative to other nodes at the same depth
     * @param for_comp (bool) If true then the evaluation is used for comparing features
     */
    virtual void set_value(int offset=-1, const bool for_comp=false) const;

    // DocString: cbrt_node_set_test_value
    /**
     * @brief Set the value of all test samples for the feature inside the central data storage array
     *
     * @param offset (int) Where the current node is in the binary expression tree relative to other nodes at the same depth
     * @param for_comp (bool) If true then the evaluation is used for comparing features
     */
    virtual void set_test_value(int offset=-1, const bool for_comp=false) const;

    // DocString: cbrt_node_rung
    /**
     * @brief return the rung of the feature (Height of the binary expression tree - 1)
     *
     */
    inline int rung(const int cur_rung=0) const {return _feats[0]->rung(cur_rung + 1);}

    /**
     * @brief Returns the type of node this is
     */
    virtual inline NODE_TYPE type() const {return NODE_TYPE::CBRT;}
    /**
     * @brief Get the term used in the postfix expression for this Node
     */
    inline std::string get_postfix_term() const {return "cbrt";}

    // DocString: cbrt_node_matlab_expr
    /**
     * @brief Get the string that corresponds to the code needed to evaluate the node in matlab
     *
     * @return The matlab code for the feature
     */
    inline std::string matlab_fxn_expr() const
    {
        return fmt::format(
            "nthroot({}, 3)",
            _feats[0]->matlab_fxn_expr()
        );
    }

    /**
     * @brief update the dictionary used to check if an Add/Sub node is valid
     *
     * @param add_sub_leaves the dictionary used to check if an Add/Sub node
     * @param expected_abs_tot The expected absolute sum of all values in add_sub_leavesis valid
     * @param pl_mn if for an addition node: 1 if for a subtraction node: -1
     */
    void update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, const int pl_mn, int& expected_abs_tot) const;

    /**
     * @brief update the dictionary used to check if an Mult/Div node is valid
     *
     * @param div_mult_leaves the dictionary used to check if an Mult/Div node is valid
     * @param fact amount to increment the element (a primary features) of the dictionary by
     * @param expected_abs_tot The expected absolute sum of all values in div_mult_leaves
     */
    void update_div_mult_leaves(std::map<std::string, double>& div_mult_leaves, const double fact, double& expected_abs_tot) const;

   #ifdef PARAMETERIZE
    /**
     * @brief The parameters used for including individual scale and bias terms to each operator in the Node
     */
    virtual std::vector<double> parameters() const {return {};}

    /**
     * @brief Optimize the scale and bias terms for each operation in the Node.
     * @details Use optimizer to find the scale and bias terms that minimizes the associated loss function
     *
     * @param optimizer The optimizer used to evaluate the loss function for each optimization and find the optimal parameters
     */
    virtual void get_parameters(std::shared_ptr<NLOptimizer> optimizer){return;}

    /**
     * @brief Set the non-linear parameters
     * @param params The new parameters for the feature
     * @param check_sz if True check the size of the params vector with the expected size
     */
    virtual void set_parameters(const std::vector<double> params, const bool check_sz=true){return;}

    /**
     * @brief Set the non-linear parameters
     * @param params The new scale and bias terms of this node
     */
    virtual void set_parameters(const double* params){return;}

    /**
     * @brief Set the value of all training samples for the feature inside the central data storage array
     *
     * @param params A pointer to the bias and scale terms for this Node and its children
     * @param offset (int) Where the current node is in the binary expression tree relative to other nodes at the same depth
     * @param for_comp (bool) If true then the evaluation is used for comparing features
     * @param depth (int) How far down a given Node is from the root OperatorNode
     */
    void set_value(const double* params, int offset=-1, const bool for_comp=false, const int depth=1) const;

    /**
     * @brief Set the value of all test samples for the feature inside the central data storage array
     *
     * @param params A pointer to the bias and scale terms for this Node and its children
     * @param offset (int) Where the current node is in the binary expression tree relative to other nodes at the same depth
     * @param for_comp (bool) If true then the evaluation is used for comparing features
     * @param depth (int) How far down a given Node is from the root OperatorNode
     */
    void set_test_value(const double* params, int offset=-1, const bool for_comp=false, const int depth=1) const;

    /**
     * @brief A human readable equation representing the feature
     *
     * @param params A pointer to the bias and scale terms for this Node and its children
     * @return A human readable equation representing the feature
     */
    inline std::string expr(const double* params, const int depth=1) const
    {
        return fmt::format(
            "(cbrt({}{:+11.6e}))",
            (depth < nlopt_wrapper::MAX_PARAM_DEPTH ? _feats[0]->expr(params + 2, depth + 1) : _feats[0]->expr()),
            params[1]
        );
    }

    /**
     * @brief Get the valid LaTeX expression that represents the feature
     *
     * @param params A pointer to the bias and scale terms for this Node and its children
     * @param depth How far down a given Node is from the root OperatorNode
     * @return Get the valid LaTeX expression that represents the feature
     */
    inline std::string get_latex_expr(const double* params, const int depth=1) const
    {
        return fmt::format(
            "\\left(\\sqrt[3]{{{}{:+8.3e} }}\\right)",
            (depth < nlopt_wrapper::MAX_PARAM_DEPTH ? _feats[0]->get_latex_expr(params + 2, depth + 1) : _feats[0]->get_latex_expr()),
            params[1]
        );
    }

    /**
     * @brief Get the string that corresponds to the code needed to evaluate the node in matlab
     *
     * @param params A pointer to the bias and scale terms for this Node and its children
     * @param depth How far down a given Node is from the root OperatorNode
     * @return The matlab code for the feature
     */
    inline std::string matlab_fxn_expr(const double* params, const int depth=1) const
    {
        return fmt::format(
            "nthroot({}{:+11.6e}, 3)",
            (depth < nlopt_wrapper::MAX_PARAM_DEPTH ? _feats[0]->matlab_fxn_expr(params + 2, depth + 1) : _feats[0]->matlab_fxn_expr()),
            params[1]
        );
    }

    /**
     * @brief Set the upper and lower bounds for the scale and bias term of this Node and its children
     *
     * @param lb A pointer to the location where the lower bounds for the scale and bias term of this Node is set
     * @param ub A pointer to the location where the upper bounds for the scale and bias term of this Node is set
     * @param depth How far down a given Node is from the root OperatorNode
     */
    void set_bounds(double* lb, double* ub, const int depth=1) const;
    /**
     * @brief Initialize the scale and bias terms for this Node and its children
     *
     * @param params A pointer to the bias and scale terms for this Node and its children
     * @param depth How far down a given Node is from the root OperatorNode
     */
    void initialize_params(double* params, const int depth = 1) const;

    /**
     * @brief Calculates the derivative of an operation with respect to the parameters for a given sample
     *
     * @param params A pointer to the bias and scale terms for this Node and its children
     * @param dfdp pointer to where the feature derivative pointers are located
     */
    inline void param_derivative(const double* params, double* dfdp) const
    {
        double* val_ptr = _feats[0]->value_ptr(params);
        std::transform(val_ptr, val_ptr + _n_samp, dfdp, [params](double vp){return 1.0 / 3.0 * std::pow(params[0] * vp + params[1], -2.0 / 3.0);});
    }
    #endif
};

/**
 * @brief Attempt to generate a new parameterized cube root node A^(1/3) and add it to feat_list
 *
 * @param feat_list list of features already generated
 * @param feat feature to attempt to take the absolute value of
 * @param feat_ind Index of the new feature
 * @param l_bound Minimum absolute value allowed for the feature.
 * @param u_bound Maximum absolute value allowed for the feature.
 */
void generateCbrtNode(
    std::vector<node_ptr>& feat_list,
    const node_ptr feat,
    unsigned long int& feat_ind,
    const double l_bound,
    const double u_bound
);

#endif