From e54b24b1f0940ebe27b6e1f6e9c465b12feaf280 Mon Sep 17 00:00:00 2001
From: Thomas <purcell@fhi-berlin.mpg.de>
Date: Wed, 6 Jan 2021 10:46:28 +0100
Subject: [PATCH] Add parameterization for top level only

add support similar to what was used with ceres
---
 .../feature_space/FeatureSpace.cpp            |  26 +++-
 .../feature_space/FeatureSpace.hpp            |  11 +-
 .../node/operator_nodes/OperatorNode.hpp      |   2 +-
 .../parameterize_top_lev_absolute_value.cpp   |  59 ++++++++
 .../parameterize_top_lev_absolute_value.hpp   | 131 +++++++++++++++++
 .../abs/parameterized_absolute_value.cpp      |   2 +
 .../abs/parameterized_absolute_value.hpp      |  15 +-
 ...rameterize_top_lev_absolute_difference.cpp |  88 ++++++++++++
 ...rameterize_top_lev_absolute_difference.hpp | 133 ++++++++++++++++++
 .../parameterized_absolute_difference.cpp     |   2 +
 .../parameterized_absolute_difference.hpp     |  16 +--
 .../add/parameterize_top_lev_add.cpp          |  62 ++++++++
 .../add/parameterize_top_lev_add.hpp          | 131 +++++++++++++++++
 .../add/parameterized_add.cpp                 |   2 +
 .../add/parameterized_add.hpp                 |  18 +--
 .../cb/parameterize_top_lev_cube.cpp          |  63 +++++++++
 .../cb/parameterize_top_lev_cube.hpp          | 131 +++++++++++++++++
 .../cb/parameterized_cube.cpp                 |   2 +
 .../cb/parameterized_cube.hpp                 |  16 +--
 .../cbrt/parameterize_top_lev_cube_root.cpp   |  65 +++++++++
 .../cbrt/parameterize_top_lev_cube_root.hpp   | 130 +++++++++++++++++
 .../cbrt/parameterized_cube_root.cpp          |   2 +
 .../cbrt/parameterized_cube_root.hpp          |  16 +--
 .../allowed_operator_nodes/cos/cos.hpp        |   4 +-
 .../cos/parameterize_top_lev_cos.cpp          |  68 +++++++++
 .../cos/parameterize_top_lev_cos.hpp          | 131 +++++++++++++++++
 .../cos/parameterized_cos.cpp                 |   2 +
 .../cos/parameterized_cos.hpp                 |  16 +--
 .../div/parameterize_top_lev_divide.cpp       |  73 ++++++++++
 .../div/parameterize_top_lev_divide.hpp       | 131 +++++++++++++++++
 .../div/parameterized_divide.cpp              |   2 +
 .../div/parameterized_divide.hpp              |  18 +--
 .../exp/parameterize_top_lev_exponential.cpp  |  68 +++++++++
 .../exp/parameterize_top_lev_exponential.hpp  | 130 +++++++++++++++++
 .../exp/parameterized_exponential.cpp         |   2 +
 .../exp/parameterized_exponential.hpp         |  16 +--
 .../inv/parameterize_top_lev_inverse.cpp      |  73 ++++++++++
 .../inv/parameterize_top_lev_inverse.hpp      | 130 +++++++++++++++++
 .../inv/parameterized_inverse.cpp             |   2 +
 .../inv/parameterized_inverse.hpp             |  16 +--
 .../log/parameterize_top_lev_log.cpp          |  73 ++++++++++
 .../log/parameterize_top_lev_log.hpp          | 130 +++++++++++++++++
 .../log/parameterized_log.cpp                 |   1 +
 .../log/parameterized_log.hpp                 |  17 ++-
 .../mult/parameterize_top_lev_multiply.cpp    |  60 ++++++++
 .../mult/parameterize_top_lev_multiply.hpp    | 130 +++++++++++++++++
 .../mult/parameterized_multiply.cpp           |   2 +
 .../mult/parameterized_multiply.hpp           |  18 +--
 ...ameterize_top_lev_negative_exponential.cpp |  69 +++++++++
 ...ameterize_top_lev_negative_exponential.hpp | 130 +++++++++++++++++
 .../parameterized_negative_exponential.cpp    |   2 +
 .../parameterized_negative_exponential.hpp    |  16 +--
 .../sin/parameterize_top_lev_sin.cpp          |  67 +++++++++
 .../sin/parameterize_top_lev_sin.hpp          | 130 +++++++++++++++++
 .../sin/parameterized_sin.cpp                 |   2 +
 .../sin/parameterized_sin.hpp                 |  16 +--
 .../parameterize_top_lev_sixth_power.cpp      |  60 ++++++++
 .../parameterize_top_lev_sixth_power.hpp      | 131 +++++++++++++++++
 .../six_pow/parameterized_sixth_power.cpp     |   2 +
 .../six_pow/parameterized_sixth_power.hpp     |  16 +--
 .../sq/parameterize_top_lev_square.cpp        |  60 ++++++++
 .../sq/parameterize_top_lev_square.hpp        | 130 +++++++++++++++++
 .../sq/parameterized_square.cpp               |   2 +
 .../sq/parameterized_square.hpp               |  16 +--
 .../sqrt/parameterize_top_lev_square_root.cpp |  66 +++++++++
 .../sqrt/parameterize_top_lev_square_root.hpp | 130 +++++++++++++++++
 .../sqrt/parameterized_square_root.cpp        |   2 +
 .../sqrt/parameterized_square_root.hpp        |  16 +--
 .../sub/parameterize_top_lev_subtract.cpp     |  60 ++++++++
 .../sub/parameterize_top_lev_subtract.hpp     | 132 +++++++++++++++++
 .../sub/parameterized_subtract.cpp            |   2 +
 .../sub/parameterized_subtract.hpp            |  18 +--
 .../node/operator_nodes/allowed_ops.hpp       |  54 +++----
 .../operator_nodes/allowed_parameter_ops.cpp  |  58 +++++---
 src/inputs/InputParser.cpp                    |   5 +-
 src/inputs/InputParser.hpp                    |   1 +
 src/nl_opt/NLOptWrapper.cpp                   |  17 ++-
 src/nl_opt/NLOptWrapper.hpp                   |  22 +--
 src/python/bindings_docstring_keyed.cpp       |  14 +-
 src/python/feature_creation/FeatureSpace.cpp  |  15 +-
 80 files changed, 3667 insertions(+), 230 deletions(-)
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize_top_lev_absolute_value.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize_top_lev_absolute_value.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize_top_lev_absolute_difference.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize_top_lev_absolute_difference.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize_top_lev_add.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize_top_lev_add.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize_top_lev_cube.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize_top_lev_cube.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize_top_lev_cube_root.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize_top_lev_cube_root.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize_top_lev_cos.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize_top_lev_cos.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize_top_lev_divide.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize_top_lev_divide.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize_top_lev_exponential.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize_top_lev_exponential.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize_top_lev_inverse.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize_top_lev_inverse.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize_top_lev_log.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize_top_lev_log.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize_top_lev_multiply.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize_top_lev_multiply.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize_top_lev_negative_exponential.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize_top_lev_negative_exponential.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize_top_lev_sin.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize_top_lev_sin.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize_top_lev_sixth_power.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize_top_lev_sixth_power.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize_top_lev_square.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize_top_lev_square.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize_top_lev_square_root.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize_top_lev_square_root.hpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize_top_lev_subtract.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize_top_lev_subtract.hpp

diff --git a/src/feature_creation/feature_space/FeatureSpace.cpp b/src/feature_creation/feature_space/FeatureSpace.cpp
index 6e185735..4cee1bf8 100644
--- a/src/feature_creation/feature_space/FeatureSpace.cpp
+++ b/src/feature_creation/feature_space/FeatureSpace.cpp
@@ -38,6 +38,24 @@ BOOST_CLASS_EXPORT_GUID(InvParamNode, "InvParamNode")
 BOOST_CLASS_EXPORT_GUID(SinParamNode, "SinParamNode")
 BOOST_CLASS_EXPORT_GUID(CosParamNode, "CosParamNode")
 
+BOOST_CLASS_EXPORT_GUID(AddParamTopLevNode, "AddParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(SubParamTopLevNode, "SubParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(AbsDiffParamTopLevNode, "AbsDiffParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(MultParamTopLevNode, "MultParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(DivParamTopLevNode, "DivParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(SqParamTopLevNode, "SqParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(SqrtParamTopLevNode, "SqrtParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(CbParamTopLevNode, "CbParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(CbrtParamTopLevNode, "CbrtParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(SixPowParamTopLevNode, "SixPowParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(ExpParamTopLevNode, "ExpParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(NegExpParamTopLevNode, "NegExpParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(LogParamTopLevNode, "LogParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(AbsParamTopLevNode, "AbsParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(InvParamTopLevNode, "InvParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(SinParamTopLevNode, "SinParamTopLevNode")
+BOOST_CLASS_EXPORT_GUID(CosParamTopLevNode, "CosParamTopLevNode")
+
 FeatureSpace::FeatureSpace(
     std::shared_ptr<MPI_Interface> mpi_comm,
     std::vector<node_ptr> phi_0,
@@ -52,7 +70,8 @@ FeatureSpace::FeatureSpace(
     int n_rung_generate,
     double cross_corr_max,
     double min_abs_feat_val,
-    double max_abs_feat_val
+    double max_abs_feat_val,
+    bool param_internal
 ):
     _phi(phi_0),
     _phi_0(phi_0),
@@ -73,7 +92,8 @@ FeatureSpace::FeatureSpace(
     _n_samp(phi_0[0]->n_samp()),
     _n_feat(phi_0.size()),
     _n_rung_store(max_store_rung),
-    _n_rung_generate(n_rung_generate)
+    _n_rung_generate(n_rung_generate),
+    _param_internal(param_internal)
 {
     initialize_fs(project_type);
 }
@@ -84,7 +104,7 @@ void FeatureSpace::initialize_fs(std::string project_type)
         if(_allowed_param_ops.size() != 0)
             throw std::logic_error("Parameterization is not possible recompile with -DPARAMETERIZE");
     #else
-        nlopt_wrapper::set_objective(project_type, _prop.data(), _task_sizes, _max_phi);
+        nlopt_wrapper::set_objective(project_type, _prop.data(), _task_sizes, _max_phi, _param_internal);
     #endif
 
     if(_n_rung_store == -1)
diff --git a/src/feature_creation/feature_space/FeatureSpace.hpp b/src/feature_creation/feature_space/FeatureSpace.hpp
index 1f4beba1..c828581a 100644
--- a/src/feature_creation/feature_space/FeatureSpace.hpp
+++ b/src/feature_creation/feature_space/FeatureSpace.hpp
@@ -82,6 +82,8 @@ class FeatureSpace
     const int _n_sis_select; //!< Number of features to select for each dimensions
     const int _n_samp; //!< Number of samples (training data)
     const int _n_rung_generate; //!< Total number of rungs to generate on the fly
+
+    bool _param_internal; //!< True if parameterize all scale and shift parameters in a feature
 public:
 
     /**
@@ -117,7 +119,8 @@ public:
         int n_rung_generate=0,
         double cross_corr_max=1.0,
         double min_abs_feat_val=1e-50,
-        double max_abs_feat_val=1e50
+        double max_abs_feat_val=1e50,
+        bool param_internal=true
     );
 
     /**
@@ -299,7 +302,8 @@ public:
             int n_rung_generate=0,
             double cross_corr_max=1.0,
             double min_abs_feat_val=1e-50,
-            double max_abs_feat_val=1e50
+            double max_abs_feat_val=1e50,
+            bool param_internal=true
         );
 
         /**
@@ -333,7 +337,8 @@ public:
             int n_rung_generate=0,
             double cross_corr_max=1.0,
             double min_abs_feat_val=1e-50,
-            double max_abs_feat_val=1e50
+            double max_abs_feat_val=1e50,
+            bool param_internal=true
         );
 
         /**
diff --git a/src/feature_creation/node/operator_nodes/OperatorNode.hpp b/src/feature_creation/node/operator_nodes/OperatorNode.hpp
index 72203f05..6faee746 100644
--- a/src/feature_creation/node/operator_nodes/OperatorNode.hpp
+++ b/src/feature_creation/node/operator_nodes/OperatorNode.hpp
@@ -346,7 +346,7 @@ public:
          * @brief returns the number of theoretical parameters for this feature
          * @return the number of theoretical parameters
          */
-        inline int n_params(int n_cur = 0){return std::accumulate(_feats.begin(), _feats.end(), 2, [](double tot, node_ptr feat){return tot + feat->n_params();});}
+        virtual inline int n_params(int n_cur = 0){return std::accumulate(_feats.begin(), _feats.end(), 2, [](double tot, node_ptr feat){return tot + feat->n_params();});}
 
         /**
          * @brief Set the values of the training data for the feature inside of the value storage arrays
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize_top_lev_absolute_value.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize_top_lev_absolute_value.cpp
new file mode 100644
index 00000000..252cf5a2
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize_top_lev_absolute_value.cpp
@@ -0,0 +1,59 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize_top_lev_absolute_value.hpp>
+
+void generateAbsParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+    node_ptr new_feat = std::make_shared<AbsParamTopLevNode>(feat, feat_ind, prop);
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+AbsParamTopLevNode::AbsParamTopLevNode()
+{}
+
+AbsParamTopLevNode::AbsParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    AbsParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+AbsParamTopLevNode::AbsParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop) :
+    AbsParamNode(feat, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+AbsParamTopLevNode::AbsParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    AbsParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void AbsParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::abs(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::abs(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+void AbsParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::abs(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void AbsParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[0] = _sign_alpha;
+    ub[0] = _sign_alpha;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize_top_lev_absolute_value.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize_top_lev_absolute_value.hpp
new file mode 100644
index 00000000..19fb2e01
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize_top_lev_absolute_value.hpp
@@ -0,0 +1,131 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize_top_levabsolute_value.hpp
+ *  @brief Class describing the parameterized absolute value operator
+ *
+ *  This class represents the parameterized unary operator -> |alpha * A + a|
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_ABS_VAL_NODE
+#define PARAM_TOP_LEV_ABS_VAL_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class AbsParamTopLevNode: public AbsParamNode
+{
+    using AbsParamNode::set_value;
+    using AbsParamNode::set_test_value;
+    using AbsParamNode::value_ptr;
+    using AbsParamNode::test_value_ptr;
+    using AbsParamNode::domain;
+    using AbsParamNode::expr;
+
+    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<AbsParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    AbsParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     * @param prop The property to fit to
+     */
+    AbsParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    AbsParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    AbsParamTopLevNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: abs_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: abs_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: abs_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: abs_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "|" + std::to_string(_params[0]) + "*" + _feats[0]->expr() + " + " + std::to_string(_params[1]) + "|";}
+
+    // DocString: abs_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().abs(_params[0], _params[1]);}
+
+    // DocString: abs_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateAbsParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.cpp
index d5d66e32..08df4ae9 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbsParamNode)
+
 void generateAbsParamNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.hpp
index 8d6bc6a9..6f7674ae 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.hpp
@@ -19,13 +19,6 @@
  */
 class AbsParamNode: public AbsNode
 {
-    using AbsNode::set_value;
-    using AbsNode::set_test_value;
-    using AbsNode::value_ptr;
-    using AbsNode::test_value_ptr;
-    using AbsNode::domain;
-    using AbsNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -42,6 +35,12 @@ class AbsParamNode: public AbsNode
     }
 
 protected:
+    using AbsNode::set_value;
+    using AbsNode::set_test_value;
+    using AbsNode::value_ptr;
+    using AbsNode::test_value_ptr;
+    using AbsNode::domain;
+    using AbsNode::expr;
     std::vector<double> _params; //!< The parameters vector
     double _sign_alpha; //!< 1 if alpha is positive, -1 if alpha is negative
 
@@ -85,7 +84,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    AbsParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+    AbsParamNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: abs_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize_top_lev_absolute_difference.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize_top_lev_absolute_difference.cpp
new file mode 100644
index 00000000..9b76933d
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize_top_lev_absolute_difference.cpp
@@ -0,0 +1,88 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize_top_lev_absolute_difference.hpp>
+
+void generateAbsDiffParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+    node_ptr new_feat = std::make_shared<AbsDiffParamTopLevNode>(feat_1, feat_2, feat_ind, prop);
+
+    std::map<std::string, int> add_sub_leaves;
+    int expected_abs_tot = 0;
+    feat_1->update_add_sub_leaves(add_sub_leaves, 1, expected_abs_tot);
+    feat_2->update_add_sub_leaves(add_sub_leaves,-1, expected_abs_tot);
+
+    if((add_sub_leaves.size() < 2))
+        return;
+
+    double* params = new_feat->parameters().data();
+    double* val_ptr = new_feat->value_ptr();
+    const int offset = new_feat->rung();
+    allowed_op_funcs::sub(feat_1->n_samp(), feat_1->value_ptr(offset + 2), feat_2->value_ptr(offset + 1), params[0], params[1], val_ptr);
+    if(*std::min_element(val_ptr, val_ptr + new_feat->n_samp()) > l_bound)
+        return;
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound) || (util_funcs::max_abs_val<double>(val_ptr, feat_1->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+AbsDiffParamTopLevNode::AbsDiffParamTopLevNode()
+{}
+
+AbsDiffParamTopLevNode::AbsDiffParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop):
+    AbsDiffParamNode(feat_1, feat_2, feat_ind, l_bound, u_bound)
+{
+    std::map<std::string, int> add_sub_leaves;
+    int expected_abs_tot = 0;
+    feat_1->update_add_sub_leaves(add_sub_leaves, 1, expected_abs_tot);
+    feat_2->update_add_sub_leaves(add_sub_leaves,-1, expected_abs_tot);
+
+    if((add_sub_leaves.size() < 2))
+        throw InvalidFeatureException();
+
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    double* params = _params.data();
+    double* val_ptr = value_ptr();
+    const int offset = rung();
+
+    allowed_op_funcs::sub(feat_1->n_samp(), feat_1->value_ptr(offset + 2), feat_2->value_ptr(offset + 1), params[0], params[1], val_ptr);
+    if(*std::min_element(val_ptr, val_ptr + _n_samp) > l_bound)
+        throw InvalidFeatureException();
+
+    if(is_nan() || is_const()|| (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+AbsDiffParamTopLevNode::AbsDiffParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, std::vector<double>& prop):
+    AbsDiffParamNode(feat_1, feat_2, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+AbsDiffParamTopLevNode::AbsDiffParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound):
+    AbsDiffParamNode(feat_1, feat_2, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void AbsDiffParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+
+    if(_selected)
+        allowed_op_funcs::abs_diff(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+    allowed_op_funcs::abs_diff(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void AbsDiffParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::abs_diff(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _feats[1]->test_value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void AbsDiffParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize_top_lev_absolute_difference.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize_top_lev_absolute_difference.hpp
new file mode 100644
index 00000000..6f4a1d11
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize_top_lev_absolute_difference.hpp
@@ -0,0 +1,133 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize_top_levabsolute_difference.hpp
+ *  @brief Class describing the parameterized absolute difference operator
+ *
+ *  This class represents the parameterized unary operator -> |A - alpha * B + a|
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_ABS_DIFF_NODE
+#define PARAM_TOP_LEV_ABS_DIFF_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class AbsDiffParamTopLevNode: public AbsDiffParamNode
+{
+    using AbsDiffParamNode::set_value;
+    using AbsDiffParamNode::set_test_value;
+    using AbsDiffParamNode::value_ptr;
+    using AbsDiffParamNode::test_value_ptr;
+    using AbsDiffParamNode::domain;
+    using AbsDiffParamNode::expr;
+
+    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<AbsDiffParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    AbsDiffParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_2 shared_ptr of the feature to operate on (B)
+     * @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.
+     * @param prop The property to fit to
+     */
+    AbsDiffParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_2 shared_ptr of the feature to operate on (B)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    AbsDiffParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     * @param prop The property to fit to
+     */
+    AbsDiffParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: abs_diff_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: abs_diff_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: abs_diff_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: abs_diff_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "|(" + _feats[0]->expr() + ") - (" + std::to_string(_params[0]) + "*" + _feats[1]->expr() + " + " + std::to_string(_params[1]) + ")|";}
+
+    // DocString: abs_diff_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().sub(_feats[1]->domain(), _params[0], _params[1]).abs();}
+
+    // DocString: abs_diff_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateAbsDiffParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.cpp
index 025dc164..eab182e7 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbsDiffParamNode)
+
 void generateAbsDiffParamNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.hpp
index c7d27aad..059ca89a 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.hpp
@@ -19,13 +19,6 @@
  */
 class AbsDiffParamNode: public AbsDiffNode
 {
-    using AbsDiffNode::set_value;
-    using AbsDiffNode::set_test_value;
-    using AbsDiffNode::value_ptr;
-    using AbsDiffNode::test_value_ptr;
-    using AbsDiffNode::domain;
-    using AbsDiffNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -40,6 +33,13 @@ class AbsDiffParamNode: public AbsDiffNode
         ar & _params;
     }
 protected:
+    using AbsDiffNode::set_value;
+    using AbsDiffNode::set_test_value;
+    using AbsDiffNode::value_ptr;
+    using AbsDiffNode::test_value_ptr;
+    using AbsDiffNode::domain;
+    using AbsDiffNode::expr;
+
     std::vector<double> _params;
 public:
     /**
@@ -83,7 +83,7 @@ public:
      * @param param_list The list of parameters to optimize using non-linear least squares
      * @param prop The property to fit to
      */
-    AbsDiffParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
+    AbsDiffParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: abs_diff_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize_top_lev_add.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize_top_lev_add.cpp
new file mode 100644
index 00000000..42c5e983
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize_top_lev_add.cpp
@@ -0,0 +1,62 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize_top_lev_add.hpp>
+
+void generateAddParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+    node_ptr new_feat = std::make_shared<AddParamTopLevNode>(feat_1, feat_2, feat_ind, prop);
+
+    new_feat->set_value();
+
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+AddParamTopLevNode::AddParamTopLevNode()
+{}
+
+AddParamTopLevNode::AddParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    AddParamNode(feat_1, feat_2, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+AddParamTopLevNode::AddParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, std::vector<double>& prop) :
+    AddParamNode(feat_1, feat_2, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+AddParamTopLevNode::AddParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound) :
+    AddParamNode(feat_1, feat_2, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void AddParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+
+    if(_selected)
+        allowed_op_funcs::add(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::add(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void AddParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::add(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _feats[1]->test_value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void AddParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[1] = 0.0;
+    ub[1] = 0.0;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize_top_lev_add.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize_top_lev_add.hpp
new file mode 100644
index 00000000..2516ba16
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize_top_lev_add.hpp
@@ -0,0 +1,131 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize_top_levadd.hpp
+ *  @brief Class describing the parameterized addition operator
+ *
+ *  This class represents the parameterized unary operator -> A + alpha * B + a
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_ADD_NODE
+#define PARAM_TOP_LEV_ADD_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_param_add_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class AddParamTopLevNode: public AddParamNode
+{
+    using AddParamNode::set_value;
+    using AddParamNode::set_test_value;
+    using AddParamNode::value_ptr;
+    using AddParamNode::test_value_ptr;
+    using AddParamNode::domain;
+    using AddParamNode::expr;
+
+    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<AddParamNode>(*this);
+    }
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    AddParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_2 shared_ptr of the feature to operate on (B)
+     * @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.
+     * @param prop The property to fit to
+     */
+    AddParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_2 shared_ptr of the feature to operate on (B)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    AddParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    AddParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: add_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: add_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: add_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: add_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "[" + _feats[0]->expr() + " + (" + std::to_string(_params[0]) + "*" + _feats[1]->expr() + " + " + std::to_string(_params[1]) + ")]";}
+
+    // DocString: add_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().add(_feats[1]->domain(), _params[0], _params[1]);}
+
+    // DocString: add_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateAddParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.cpp
index 9f791026..a7161dbf 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(AddParamNode)
+
 void generateAddParamNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.hpp
index 4cf303b7..f5b0d7b1 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.hpp
@@ -19,13 +19,6 @@
  */
 class AddParamNode: public AddNode
 {
-    using AddNode::set_value;
-    using AddNode::set_test_value;
-    using AddNode::value_ptr;
-    using AddNode::test_value_ptr;
-    using AddNode::domain;
-    using AddNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -39,8 +32,17 @@ class AddParamNode: public AddNode
         ar & boost::serialization::base_object<AddNode>(*this);
         ar & _params;
     }
+
 protected:
+    using AddNode::set_value;
+    using AddNode::set_test_value;
+    using AddNode::value_ptr;
+    using AddNode::test_value_ptr;
+    using AddNode::domain;
+    using AddNode::expr;
+
     std::vector<double> _params;
+
 public:
     /**
      * @brief Base Constructor
@@ -82,7 +84,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    AddParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
+    AddParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: add_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize_top_lev_cube.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize_top_lev_cube.cpp
new file mode 100644
index 00000000..95f865b8
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize_top_lev_cube.cpp
@@ -0,0 +1,63 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize_top_lev_cube.hpp>
+
+void generateCbParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+    node_ptr new_feat = std::make_shared<CbParamTopLevNode>(feat, feat_ind, prop);
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+CbParamTopLevNode::CbParamTopLevNode()
+{}
+
+CbParamTopLevNode::CbParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    CbParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+    set_value();
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+CbParamTopLevNode::CbParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop) :
+    CbParamNode(feat, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+    set_value();
+}
+
+CbParamTopLevNode::CbParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    CbParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void CbParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::cb(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::cb(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void CbParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::cb(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void CbParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[0] = 1.0;
+    ub[0] = 1.0;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize_top_lev_cube.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize_top_lev_cube.hpp
new file mode 100644
index 00000000..2eb13937
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize_top_lev_cube.hpp
@@ -0,0 +1,131 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize_top_levcube.hpp
+ *  @brief Class describing the parameterized cube operator
+ *
+ *  This class represents the parameterized unary operator -> (alpha * A + a)^3
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_CUBE_NODE
+#define PARAM_TOP_LEV_CUBE_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class CbParamTopLevNode: public CbParamNode
+{
+    using CbParamNode::set_value;
+    using CbParamNode::set_test_value;
+    using CbParamNode::value_ptr;
+    using CbParamNode::test_value_ptr;
+    using CbParamNode::domain;
+    using CbParamNode::expr;
+
+    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<CbParamNode>(*this);
+    }
+
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    CbParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param prop The property to fit to
+     */
+    CbParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    CbParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    CbParamTopLevNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: cb_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: cb_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: cb_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: cb_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "(" + std::to_string(_params[0]) + "*" + _feats[0]->expr() + " + " + std::to_string(_params[1]) + ")^3";}
+
+    // DocString: cb_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().pow(3.0, _params[0], _params[1]);}
+
+    // DocString: cb_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateCbParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.cpp
index e1dffd6d..5eb08d7d 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(CbParamNode)
+
 void generateCbParamNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.hpp
index e133a90c..8b9cec61 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.hpp
@@ -19,13 +19,6 @@
  */
 class CbParamNode: public CbNode
 {
-    using CbNode::set_value;
-    using CbNode::set_test_value;
-    using CbNode::value_ptr;
-    using CbNode::test_value_ptr;
-    using CbNode::domain;
-    using CbNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -41,6 +34,13 @@ class CbParamNode: public CbNode
     }
 
 protected:
+    using CbNode::set_value;
+    using CbNode::set_test_value;
+    using CbNode::value_ptr;
+    using CbNode::test_value_ptr;
+    using CbNode::domain;
+    using CbNode::expr;
+
     std::vector<double> _params; //!< The parameters vector
 
 public:
@@ -82,7 +82,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    CbParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+    CbParamNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: cb_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize_top_lev_cube_root.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize_top_lev_cube_root.cpp
new file mode 100644
index 00000000..ae37c160
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize_top_lev_cube_root.cpp
@@ -0,0 +1,65 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize_top_lev_cube_root.hpp>
+
+void generateCbrtParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+    node_ptr new_feat = std::make_shared<CbrtParamTopLevNode>(feat, feat_ind, prop);
+
+    if(new_feat->parameters()[0] * feat->domain() + new_feat->parameters()[1] <= 0.0)
+        return;
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+CbrtParamTopLevNode::CbrtParamTopLevNode()
+{}
+
+CbrtParamTopLevNode::CbrtParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    CbrtParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+    if((_params[0] * _feats[0]->domain() + _params[1]) <= 0.0)
+        throw InvalidFeatureException();
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+CbrtParamTopLevNode::CbrtParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop) :
+    CbrtParamNode(feat, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+CbrtParamTopLevNode::CbrtParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    CbrtParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void CbrtParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::cbrt(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::cbrt(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void CbrtParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::cbrt(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void CbrtParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[0] = _sign_alpha;
+    ub[0] = _sign_alpha;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize_top_lev_cube_root.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize_top_lev_cube_root.hpp
new file mode 100644
index 00000000..b9e74a19
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize_top_lev_cube_root.hpp
@@ -0,0 +1,130 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize_top_levcube_root.hpp
+ *  @brief Class describing the parameterized cube root operator
+ *
+ *  This class represents the parameterized unary operator -> cbrt(alpha * A + a)
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_CBRT_NODE
+#define PARAM_TOP_LEV_CBRT_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class CbrtParamTopLevNode: public CbrtParamNode
+{
+    using CbrtParamNode::set_value;
+    using CbrtParamNode::set_test_value;
+    using CbrtParamNode::value_ptr;
+    using CbrtParamNode::test_value_ptr;
+    using CbrtParamNode::domain;
+    using CbrtParamNode::expr;
+
+    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<CbrtParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    CbrtParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param prop The property to fit to
+     */
+    CbrtParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    CbrtParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    CbrtParamTopLevNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50d);
+
+    // DocString: cbrt_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: cbrt_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: cbrt_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: cbrt_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "cbrt(" + std::to_string(_params[0]) + "*" + _feats[0]->expr() + " + " + std::to_string(_params[1]) + ")";}
+
+    // DocString: cbrt_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().pow(1.0 / 3.0, _params[0], _params[1]);}
+
+    // DocString: cbrt_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateCbrtParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.cpp
index 4e3fc857..de18674e 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(CbrtParamNode)
+
 void generateCbrtParamNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.hpp
index c2bf5b74..f185ba86 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.hpp
@@ -19,13 +19,6 @@
  */
 class CbrtParamNode: public CbrtNode
 {
-    using CbrtNode::set_value;
-    using CbrtNode::set_test_value;
-    using CbrtNode::value_ptr;
-    using CbrtNode::test_value_ptr;
-    using CbrtNode::domain;
-    using CbrtNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -42,6 +35,13 @@ class CbrtParamNode: public CbrtNode
     }
 
 protected:
+    using CbrtNode::set_value;
+    using CbrtNode::set_test_value;
+    using CbrtNode::value_ptr;
+    using CbrtNode::test_value_ptr;
+    using CbrtNode::domain;
+    using CbrtNode::expr;
+
     std::vector<double> _params; //!< The parameters vector
     double _sign_alpha; //!< 1 if alpha is positive, -1 if alpha is negative
 public:
@@ -83,7 +83,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    CbrtParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+    CbrtParamNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: cbrt_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/cos.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/cos.hpp
index da9bb0f1..9a9ae1d3 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/cos.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/cos.hpp
@@ -170,7 +170,7 @@ public:
          * @param params parameter values for non-linear operations
          * @return the domain
          */
-        inline Domain domain(double* params) {return Domain(-1.0, 1.0);}
+        inline Domain domain(double* params){return Domain(-1.0, 1.0);}
 
         /**
          * @brief The expression of the feature
@@ -178,7 +178,7 @@ public:
          * @param params parameter values for non-linear operations
          * @return feature expression
          */
-        inline std::string expr(double* params) {return "cos(" + std::to_string(params[0]) + "*" + _feats[0]->expr( params + 2) + " + " + std::to_string(params[1]) + ")";}
+        inline std::string expr(double* params){return "cos(" + std::to_string(params[0]) + "*" + _feats[0]->expr( params + 2) + " + " + std::to_string(params[1]) + ")";}
 
         /**
          * @brief Set the bounds for the nl parameterization
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize_top_lev_cos.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize_top_lev_cos.cpp
new file mode 100644
index 00000000..186e0c32
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize_top_lev_cos.cpp
@@ -0,0 +1,68 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize_top_lev_cos.hpp>
+
+void generateCosParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+
+    if((feat->type() == NODE_TYPE::SIN) || (feat->type() == NODE_TYPE::COS))
+        return;
+
+    node_ptr new_feat = std::make_shared<CosParamTopLevNode>(feat, feat_ind, prop);
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+CosParamTopLevNode::CosParamTopLevNode()
+{}
+
+CosParamTopLevNode::CosParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    CosParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    if((feat->type() == NODE_TYPE::SIN) || (feat->type() == NODE_TYPE::COS))
+        throw InvalidFeatureException();
+
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+CosParamTopLevNode::CosParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop) :
+    CosParamNode(feat, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+CosParamTopLevNode::CosParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    CosParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void CosParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::cos(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::cos(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void CosParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::cos(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+
+void CosParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[1] = -1.0 * M_PI;
+    ub[1] = M_PI;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize_top_lev_cos.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize_top_lev_cos.hpp
new file mode 100644
index 00000000..6805c388
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize_top_lev_cos.hpp
@@ -0,0 +1,131 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize_top_levcos.hpp
+ *  @brief Class describing the parameterized cos operator
+ *
+ *  This class represents the parameterized unary operator -> cos(alpha * A + a)
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_COS_NODE
+#define PARAM_TOP_LEV_COS_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class CosParamTopLevNode: public CosParamNode
+{
+    using CosParamNode::set_value;
+    using CosParamNode::set_test_value;
+    using CosParamNode::value_ptr;
+    using CosParamNode::test_value_ptr;
+    using CosParamNode::domain;
+    using CosParamNode::expr;
+
+    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<CosParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    CosParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     * @param prop The property to fit to
+     */
+    CosParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    CosParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    CosParamTopLevNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: cos_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: cos_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: cos_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: cos_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "cos(" + std::to_string(_params[0]) + "*" + _feats[0]->expr() + " + " + std::to_string(_params[1]) + ")";}
+
+    // DocString: cos_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return Domain(-1.0, 1.0);}
+
+    // DocString: cos_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateCosParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.cpp
index 20127ad9..8d1c93f0 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(CosParamNode)
+
 void generateCosParamNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.hpp
index 0df8f40a..af7cf0e7 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.hpp
@@ -19,13 +19,6 @@
  */
 class CosParamNode: public CosNode
 {
-    using CosNode::set_value;
-    using CosNode::set_test_value;
-    using CosNode::value_ptr;
-    using CosNode::test_value_ptr;
-    using CosNode::domain;
-    using CosNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -41,6 +34,13 @@ class CosParamNode: public CosNode
     }
 
 protected:
+    using CosNode::set_value;
+    using CosNode::set_test_value;
+    using CosNode::value_ptr;
+    using CosNode::test_value_ptr;
+    using CosNode::domain;
+    using CosNode::expr;
+
     std::vector<double> _params; //!< The parameters vector
 
 public:
@@ -83,7 +83,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    CosParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+    CosParamNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: cos_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize_top_lev_divide.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize_top_lev_divide.cpp
new file mode 100644
index 00000000..673ff3b6
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize_top_lev_divide.cpp
@@ -0,0 +1,73 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize_top_lev_divide.hpp>
+
+void generateDivParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+    if((feat_1->type() == NODE_TYPE::INV) || (feat_2->type() == NODE_TYPE::INV) || (feat_2->type() == NODE_TYPE::DIV))
+        return;
+
+    node_ptr new_feat = std::make_shared<DivParamTopLevNode>(feat_1, feat_2, feat_ind, prop);
+
+    if((new_feat->parameters()[0] * feat_2->domain() + new_feat->parameters()[1]).contains(0.0))
+        return;
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+DivParamTopLevNode::DivParamTopLevNode()
+{}
+
+DivParamTopLevNode::DivParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    DivParamNode(feat_1, feat_2, feat_ind, l_bound, u_bound)
+{
+     if((feat_1->type() == NODE_TYPE::INV) || (feat_2->type() == NODE_TYPE::INV) || (feat_2->type() == NODE_TYPE::DIV))
+        throw InvalidFeatureException();
+
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if((_params[0] * _feats[1]->domain() + _params[1]).contains(0.0))
+        throw InvalidFeatureException();
+
+    set_value();
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+DivParamTopLevNode::DivParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, std::vector<double>& prop) :
+    DivParamNode(feat_1, feat_2, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+}
+
+DivParamTopLevNode::DivParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound) :
+    DivParamNode(feat_1, feat_2, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void DivParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+
+    if(_selected)
+        allowed_op_funcs::div(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+    allowed_op_funcs::div(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+void DivParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::div(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _feats[1]->test_value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void DivParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[0] = 1.0;
+    ub[0] = 1.0;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize_top_lev_divide.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize_top_lev_divide.hpp
new file mode 100644
index 00000000..96081581
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize_top_lev_divide.hpp
@@ -0,0 +1,131 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize_top_levdivide.hpp
+ *  @brief Class describing the parameterized addition operator
+ *
+ *  This class represents the parameterized unary operator -> A / (alpha * B + a)
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_DIV_NODE
+#define PARAM_TOP_LEV_DIV_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_param_add_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class DivParamTopLevNode: public DivParamNode
+{
+    using DivParamNode::set_value;
+    using DivParamNode::set_test_value;
+    using DivParamNode::value_ptr;
+    using DivParamNode::test_value_ptr;
+    using DivParamNode::domain;
+    using DivParamNode::expr;
+
+    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<DivParamNode>(*this);
+    }
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    DivParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_2 shared_ptr of the feature to operate on (B)
+     * @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.
+     * @param prop The property to fit to
+     */
+    DivParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_2 shared_ptr of the feature to operate on (B)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    DivParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    DivParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: div_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: div_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: div_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: div_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "[(" + _feats[0]->expr() + ") / (" + std::to_string(_params[0]) + "*" + _feats[1]->expr() + " + " + std::to_string(_params[1]) + ")]";}
+
+    // DocString: div_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().div(_feats[1]->domain(), _params[0], _params[1]);}
+
+    // DocString: div_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateDivParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.cpp
index a2e6bc0d..cbdc9114 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(DivParamNode)
+
 void generateDivParamNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.hpp
index 66f871d8..926165e4 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.hpp
@@ -19,13 +19,6 @@
  */
 class DivParamNode: public DivNode
 {
-    using DivNode::set_value;
-    using DivNode::set_test_value;
-    using DivNode::value_ptr;
-    using DivNode::test_value_ptr;
-    using DivNode::domain;
-    using DivNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -39,8 +32,17 @@ class DivParamNode: public DivNode
         ar & boost::serialization::base_object<DivNode>(*this);
         ar & _params;
     }
+
 protected:
+    using DivNode::set_value;
+    using DivNode::set_test_value;
+    using DivNode::value_ptr;
+    using DivNode::test_value_ptr;
+    using DivNode::domain;
+    using DivNode::expr;
+
     std::vector<double> _params;
+
 public:
     /**
      * @brief Base Constructor
@@ -82,7 +84,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    DivParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
+    DivParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: div_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize_top_lev_exponential.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize_top_lev_exponential.cpp
new file mode 100644
index 00000000..db984723
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize_top_lev_exponential.cpp
@@ -0,0 +1,68 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize_top_lev_exponential.hpp>
+
+void generateExpParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+
+    if((feat->type() == NODE_TYPE::NEG_EXP) || (feat->type() == NODE_TYPE::EXP) || (feat->type() == NODE_TYPE::ADD) || (feat->type() == NODE_TYPE::SUB) || (feat->type() == NODE_TYPE::LOG))
+        return;
+
+    node_ptr new_feat = std::make_shared<ExpParamTopLevNode>(feat, feat_ind, prop);
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+ExpParamTopLevNode::ExpParamTopLevNode()
+{}
+
+ExpParamTopLevNode::ExpParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    ExpParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    if((feat->type() == NODE_TYPE::NEG_EXP) || (feat->type() == NODE_TYPE::EXP) || (feat->type() == NODE_TYPE::ADD) || (feat->type() == NODE_TYPE::SUB) || (feat->type() == NODE_TYPE::LOG))
+        throw InvalidFeatureException();
+
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+ExpParamTopLevNode::ExpParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop) :
+    ExpParamNode(feat, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+ExpParamTopLevNode::ExpParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    ExpParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void ExpParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::exp(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::exp(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+void ExpParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::exp(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void ExpParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[0] = 0.0;
+
+    *(lb - from_parent) = 1.0;
+    *(ub - from_parent) = 1.0;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize_top_lev_exponential.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize_top_lev_exponential.hpp
new file mode 100644
index 00000000..4bfc5ca8
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize_top_lev_exponential.hpp
@@ -0,0 +1,130 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize_top_levexponetial.hpp
+ *  @brief Class describing the parameterized exp operator
+ *
+ *  This class represents the parameterized unary operator -> exp(alpha * A + a)
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_EXP_NODE
+#define PARAM_TOP_LEV_EXP_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class ExpParamTopLevNode: public ExpParamNode
+{
+    using ExpParamNode::set_value;
+    using ExpParamNode::set_test_value;
+    using ExpParamNode::value_ptr;
+    using ExpParamNode::test_value_ptr;
+    using ExpParamNode::domain;
+    using ExpParamNode::expr;
+
+    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<ExpParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    ExpParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param prop The property to fit to
+     */
+    ExpParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    ExpParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    ExpParamTopLevNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: exp_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: exp_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: exp_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: exp_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "exp(" + std::to_string(_params[0]) + "*" + _feats[0]->expr() + " + " + std::to_string(_params[1]) + ")";}
+
+    // DocString: exp_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().exp(_params[0], _params[1]);}
+
+    // DocString: exp_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateExpParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.cpp
index cce7c760..71587810 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(ExpParamNode)
+
 void generateExpParamNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.hpp
index 9b444415..6f3556d5 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.hpp
@@ -19,13 +19,6 @@
  */
 class ExpParamNode: public ExpNode
 {
-    using ExpNode::set_value;
-    using ExpNode::set_test_value;
-    using ExpNode::value_ptr;
-    using ExpNode::test_value_ptr;
-    using ExpNode::domain;
-    using ExpNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -41,6 +34,13 @@ class ExpParamNode: public ExpNode
     }
 
 protected:
+    using ExpNode::set_value;
+    using ExpNode::set_test_value;
+    using ExpNode::value_ptr;
+    using ExpNode::test_value_ptr;
+    using ExpNode::domain;
+    using ExpNode::expr;
+
     std::vector<double> _params; //!< The parameters vector
 
 public:
@@ -82,7 +82,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    ExpParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+    ExpParamNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: exp_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize_top_lev_inverse.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize_top_lev_inverse.cpp
new file mode 100644
index 00000000..4e7f32c7
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize_top_lev_inverse.cpp
@@ -0,0 +1,73 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize_top_lev_inverse.hpp>
+
+void generateInvParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+
+    if((feat->type() == NODE_TYPE::DIV) || (feat->type() == NODE_TYPE::INV))
+        return;
+
+    node_ptr new_feat = std::make_shared<InvParamTopLevNode>(feat, feat_ind, prop);
+
+    if(new_feat->parameters()[0] * feat->domain() + new_feat->parameters()[1] <= 0.0)
+        return;
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+InvParamTopLevNode::InvParamTopLevNode()
+{}
+
+InvParamTopLevNode::InvParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    InvParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    if((feat->type() == NODE_TYPE::DIV) || (feat->type() == NODE_TYPE::INV))
+        throw InvalidFeatureException();
+
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if((_params[0] * _feats[0]->domain() + _params[1]).contains(0.0))
+        throw InvalidFeatureException();
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+InvParamTopLevNode::InvParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop) :
+    InvParamNode(feat, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+InvParamTopLevNode::InvParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    InvParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void InvParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::inv(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::inv(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void InvParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::inv(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void InvParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[0] = 1.0;
+    ub[0] = 1.0;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize_top_lev_inverse.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize_top_lev_inverse.hpp
new file mode 100644
index 00000000..9916ad7f
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize_top_lev_inverse.hpp
@@ -0,0 +1,130 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize_top_levinverse.hpp
+ *  @brief Class describing the parameterized inverse operator
+ *
+ *  This class represents the parameterized unary operator -> 1.0 / (alpha * A + a)
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_INV_NODE
+#define PARAM_TOP_LEV_INV_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class InvParamTopLevNode: public InvParamNode
+{
+    using InvParamNode::set_value;
+    using InvParamNode::set_test_value;
+    using InvParamNode::value_ptr;
+    using InvParamNode::test_value_ptr;
+    using InvParamNode::domain;
+    using InvParamNode::expr;
+
+    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<InvParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    InvParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param prop The property to fit to
+     */
+    InvParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    InvParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    InvParamTopLevNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: inv_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: inv_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: inv_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: inv_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "1.0 / (" + std::to_string(_params[0]) + "*" + _feats[0]->expr() + " + " + std::to_string(_params[1]) + ")";}
+
+    // DocString: inv_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().inv(_params[0], _params[1]);}
+
+    // DocString: inv_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateInvParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.cpp
index 4e61fb6f..a9dc1db9 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(InvParamNode)
+
 void generateInvParamNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.hpp
index 2ee68930..b0cb4375 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.hpp
@@ -19,13 +19,6 @@
  */
 class InvParamNode: public InvNode
 {
-    using InvNode::set_value;
-    using InvNode::set_test_value;
-    using InvNode::value_ptr;
-    using InvNode::test_value_ptr;
-    using InvNode::domain;
-    using InvNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -41,6 +34,13 @@ class InvParamNode: public InvNode
     }
 
 protected:
+    using InvNode::set_value;
+    using InvNode::set_test_value;
+    using InvNode::value_ptr;
+    using InvNode::test_value_ptr;
+    using InvNode::domain;
+    using InvNode::expr;
+
     std::vector<double> _params; //!< The parameters vector
 
 public:
@@ -82,7 +82,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    InvParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+    InvParamNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: inv_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize_top_lev_log.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize_top_lev_log.cpp
new file mode 100644
index 00000000..95c62095
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize_top_lev_log.cpp
@@ -0,0 +1,73 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize_top_lev_log.hpp>
+
+
+void generateLogParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+
+    if((feat->type() == NODE_TYPE::NEG_EXP) || (feat->type() == NODE_TYPE::EXP) || (feat->type() == NODE_TYPE::DIV) || (feat->type() == NODE_TYPE::INV) || (feat->type() == NODE_TYPE::MULT) || (feat->type() == NODE_TYPE::LOG) || (feat->type() == NODE_TYPE::SIX_POW) || (feat->type() == NODE_TYPE::CB) || (feat->type() == NODE_TYPE::SQ) || (feat->type() == NODE_TYPE::CBRT) || (feat->type() == NODE_TYPE::SQRT))
+        return;
+
+    node_ptr new_feat = std::make_shared<LogParamTopLevNode>(feat, feat_ind, prop);
+    if(new_feat->parameters()[0] * feat->domain() + new_feat->parameters()[1] <= 0.0)
+        return;
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+LogParamTopLevNode::LogParamTopLevNode()
+{}
+
+LogParamTopLevNode::LogParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    LogParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    if((feat->type() == NODE_TYPE::NEG_EXP) || (feat->type() == NODE_TYPE::EXP) || (feat->type() == NODE_TYPE::DIV) || (feat->type() == NODE_TYPE::INV) || (feat->type() == NODE_TYPE::MULT) || (feat->type() == NODE_TYPE::LOG) || (feat->type() == NODE_TYPE::SIX_POW) || (feat->type() == NODE_TYPE::CB) || (feat->type() == NODE_TYPE::SQ) || (feat->type() == NODE_TYPE::CBRT) || (feat->type() == NODE_TYPE::SQRT))
+        throw InvalidFeatureException();
+
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if((_params[0] * _feats[0]->domain() + _params[1]) <= 0.0)
+        throw InvalidFeatureException();
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+LogParamTopLevNode::LogParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop) :
+    LogParamNode(feat, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+LogParamTopLevNode::LogParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    LogParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void LogParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::log(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::log(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void LogParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::log(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void LogParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    *(lb - from_parent + 1) = 0.0;
+    *(ub - from_parent + 1) = 0.0;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize_top_lev_log.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize_top_lev_log.hpp
new file mode 100644
index 00000000..368671cd
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize_top_lev_log.hpp
@@ -0,0 +1,130 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize_top_levlog.hpp
+ *  @brief Class describing the parameterized log operator
+ *
+ *  This class represents the parameterized unary operator -> log(alpha * A + a)
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_LOG_NODE
+#define PARAM_TOP_LEV_LOG_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class LogParamTopLevNode: public LogParamNode
+{
+    using LogParamNode::set_value;
+    using LogParamNode::set_test_value;
+    using LogParamNode::value_ptr;
+    using LogParamNode::test_value_ptr;
+    using LogParamNode::domain;
+    using LogParamNode::expr;
+
+    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<LogParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    LogParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param prop The property to fit to
+     */
+    LogParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    LogParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    LogParamTopLevNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: log_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: log_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: log_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: log_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "log(" + std::to_string(_params[0]) + "*" + _feats[0]->expr() + " + " + std::to_string(_params[1]) + ")";}
+
+    // DocString: log_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().log(_params[0], _params[1]);}
+
+    // DocString: log_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateLogParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.cpp
index a2830d1b..7822ce70 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.cpp
@@ -1,5 +1,6 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(LogParamNode)
 
 void generateLogParamNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.hpp
index c44051ab..24009560 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.hpp
@@ -19,14 +19,6 @@
  */
 class LogParamNode: public LogNode
 {
-    using LogNode::set_value;
-    using LogNode::set_test_value;
-    using LogNode::domain;
-    using LogNode::expr;
-    using LogNode::value_ptr;
-    using LogNode::test_value_ptr;
-
-
     friend class boost::serialization::access;
 
     /**
@@ -42,6 +34,13 @@ class LogParamNode: public LogNode
     }
 
 protected:
+    using LogNode::set_value;
+    using LogNode::set_test_value;
+    using LogNode::domain;
+    using LogNode::expr;
+    using LogNode::value_ptr;
+    using LogNode::test_value_ptr;
+
     std::vector<double> _params; //!< The parameters vector
 
 public:
@@ -83,7 +82,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    LogParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+    LogParamNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: log_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize_top_lev_multiply.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize_top_lev_multiply.cpp
new file mode 100644
index 00000000..fbd219c0
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize_top_lev_multiply.cpp
@@ -0,0 +1,60 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize_top_lev_multiply.hpp>
+
+void generateMultParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+    node_ptr new_feat = std::make_shared<MultParamTopLevNode>(feat_1, feat_2, feat_ind, prop);
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+MultParamTopLevNode::MultParamTopLevNode()
+{}
+
+MultParamTopLevNode::MultParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop):
+    MultParamNode(feat_1, feat_2, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+MultParamTopLevNode::MultParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound):
+    MultParamNode(feat_1, feat_2, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+MultParamTopLevNode::MultParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, std::vector<double>& prop):
+    MultParamNode(feat_1, feat_2, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+void MultParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+
+    if(_selected)
+        allowed_op_funcs::mult(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+    allowed_op_funcs::mult(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void MultParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::mult(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _feats[1]->test_value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void MultParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[0] = 1.0;
+    ub[0] = 1.0;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize_top_lev_multiply.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize_top_lev_multiply.hpp
new file mode 100644
index 00000000..7e630725
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize_top_lev_multiply.hpp
@@ -0,0 +1,130 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize_top_levmultiply.hpp
+ *  @brief Class describing the parameterized addition operator
+ *
+ *  This class represents the parameterized unary operator -> A * (alpha * B + a)
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_MULT_NODE
+#define PARAM_TOP_LEV_MULT_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_param_add_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class MultParamTopLevNode: public MultParamNode
+{
+    using MultParamNode::set_value;
+    using MultParamNode::set_test_value;
+    using MultParamNode::value_ptr;
+    using MultParamNode::test_value_ptr;
+    using MultParamNode::domain;
+    using MultParamNode::expr;
+
+    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<MultParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    MultParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param prop The property to fit to
+     */
+    MultParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    MultParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    MultParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: mult_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: mult_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: mult_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: mult_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "[(" + _feats[0]->expr() + ") * (" + std::to_string(_params[0]) + "*" + _feats[1]->expr() + " + " + std::to_string(_params[1]) + ")]";}
+
+    // DocString: mult_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().mult(_feats[1]->domain(), _params[0], _params[1]);}
+
+    // DocString: add_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateMultParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.cpp
index 0e61b8b9..d86d1814 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(MultParamNode)
+
 void generateMultParamNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.hpp
index f404b839..171bea8e 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.hpp
@@ -19,13 +19,6 @@
  */
 class MultParamNode: public MultNode
 {
-    using MultNode::set_value;
-    using MultNode::set_test_value;
-    using MultNode::value_ptr;
-    using MultNode::test_value_ptr;
-    using MultNode::domain;
-    using MultNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -39,8 +32,17 @@ class MultParamNode: public MultNode
         ar & boost::serialization::base_object<MultNode>(*this);
         ar & _params;
     }
+
 protected:
+    using MultNode::set_value;
+    using MultNode::set_test_value;
+    using MultNode::value_ptr;
+    using MultNode::test_value_ptr;
+    using MultNode::domain;
+    using MultNode::expr;
+
     std::vector<double> _params;
+
 public:
     /**
      * @brief Base Constructor
@@ -80,7 +82,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    MultParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
+    MultParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: mult_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize_top_lev_negative_exponential.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize_top_lev_negative_exponential.cpp
new file mode 100644
index 00000000..2f11c69c
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize_top_lev_negative_exponential.cpp
@@ -0,0 +1,69 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize_top_lev_negative_exponential.hpp>
+
+void generateNegExpParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+
+    if((feat->type() == NODE_TYPE::NEG_EXP) || (feat->type() == NODE_TYPE::EXP) || (feat->type() == NODE_TYPE::ADD) || (feat->type() == NODE_TYPE::SUB) || (feat->type() == NODE_TYPE::LOG))
+        return;
+
+    node_ptr new_feat = std::make_shared<NegExpParamTopLevNode>(feat, feat_ind, prop);
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+NegExpParamTopLevNode::NegExpParamTopLevNode()
+{}
+
+NegExpParamTopLevNode::NegExpParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    NegExpParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    if((feat->type() == NODE_TYPE::NEG_EXP) || (feat->type() == NODE_TYPE::EXP) || (feat->type() == NODE_TYPE::ADD) || (feat->type() == NODE_TYPE::SUB) || (feat->type() == NODE_TYPE::LOG))
+        throw InvalidFeatureException();
+
+    _params.resize(n_params(),  0.0);
+    get_parameters(prop);
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+NegExpParamTopLevNode::NegExpParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop) :
+    NegExpParamNode(feat, feat_ind)
+{
+    _params.resize(n_params(),  0.0);
+    get_parameters(prop);
+}
+
+NegExpParamTopLevNode::NegExpParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    NegExpParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(),  0.0);
+}
+
+void NegExpParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::neg_exp(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::neg_exp(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void NegExpParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::neg_exp(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void NegExpParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[0] = 0.0;
+
+    *(lb - from_parent) = 1.0;
+    *(ub - from_parent) = 1.0;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize_top_lev_negative_exponential.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize_top_lev_negative_exponential.hpp
new file mode 100644
index 00000000..c33ecb06
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize_top_lev_negative_exponential.hpp
@@ -0,0 +1,130 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize_top_levnegative_exponetial.hpp
+ *  @brief Class describing the parameterized neg_exp operator
+ *
+ *  This class represents the parameterized unary operator -> exp(-alpha * A + a)
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_NEG_EXP_NODE
+#define PARAM_TOP_LEV_NEG_EXP_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class NegExpParamTopLevNode: public NegExpParamNode
+{
+    using NegExpParamNode::set_value;
+    using NegExpParamNode::set_test_value;
+    using NegExpParamNode::value_ptr;
+    using NegExpParamNode::test_value_ptr;
+    using NegExpParamNode::domain;
+    using NegExpParamNode::expr;
+
+    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<NegExpParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    NegExpParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param prop The property to fit to
+     */
+    NegExpParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    NegExpParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    NegExpParamTopLevNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: neg_exp_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: neg_exp_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: neg_exp_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: neg_exp_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "exp[-1.0*(" + std::to_string(_params[0]) + "*" + _feats[0]->expr() + " + " + std::to_string(_params[1]) + ")]";}
+
+    // DocString: neg_exp_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().exp(-1.0 * _params[0], _params[1]);}
+
+    // DocString: add_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateNegExpParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.cpp
index 71a0eb94..f5251c06 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(NegExpParamNode)
+
 void generateNegExpParamNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.hpp
index 9f3c67dc..5f6c8e2a 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.hpp
@@ -19,13 +19,6 @@
  */
 class NegExpParamNode: public NegExpNode
 {
-    using NegExpNode::set_value;
-    using NegExpNode::set_test_value;
-    using NegExpNode::value_ptr;
-    using NegExpNode::test_value_ptr;
-    using NegExpNode::domain;
-    using NegExpNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -41,6 +34,13 @@ class NegExpParamNode: public NegExpNode
     }
 
 protected:
+    using NegExpNode::set_value;
+    using NegExpNode::set_test_value;
+    using NegExpNode::value_ptr;
+    using NegExpNode::test_value_ptr;
+    using NegExpNode::domain;
+    using NegExpNode::expr;
+
     std::vector<double> _params; //!< The parameters vector
 
 public:
@@ -82,7 +82,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    NegExpParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+    NegExpParamNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: neg_exp_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize_top_lev_sin.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize_top_lev_sin.cpp
new file mode 100644
index 00000000..aa48bf56
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize_top_lev_sin.cpp
@@ -0,0 +1,67 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize_top_lev_sin.hpp>
+
+void generateSinParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+
+    if((feat->type() == NODE_TYPE::SIN) || (feat->type() == NODE_TYPE::COS))
+        return;
+
+    node_ptr new_feat = std::make_shared<SinParamTopLevNode>(feat, feat_ind, prop);
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+SinParamTopLevNode::SinParamTopLevNode()
+{}
+
+SinParamTopLevNode::SinParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SinParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    if((feat->type() == NODE_TYPE::SIN) || (feat->type() == NODE_TYPE::COS))
+        throw InvalidFeatureException();
+
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+SinParamTopLevNode::SinParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop) :
+    SinParamNode(feat, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+SinParamTopLevNode::SinParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    SinParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void SinParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::sin(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::sin(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void SinParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::sin(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void SinParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[1] = -1.0 * M_PI;
+    ub[1] = M_PI;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize_top_lev_sin.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize_top_lev_sin.hpp
new file mode 100644
index 00000000..4989ec8c
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize_top_lev_sin.hpp
@@ -0,0 +1,130 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize_top_levsin.hpp
+ *  @brief Class describing the parameterized sin operator
+ *
+ *  This class represents the parameterized unary operator -> sin(alpha * A + a)
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_SIN_NODE
+#define PARAM_TOP_LEV_SIN_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class SinParamTopLevNode: public SinParamNode
+{
+    using SinParamNode::set_value;
+    using SinParamNode::set_test_value;
+    using SinParamNode::value_ptr;
+    using SinParamNode::test_value_ptr;
+    using SinParamNode::domain;
+    using SinParamNode::expr;
+
+    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<SinParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    SinParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param prop The property to fit to
+     */
+    SinParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    SinParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    SinParamTopLevNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: sin_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: sin_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: sin_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: sin_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "sin(" + std::to_string(_params[0]) + "*" + _feats[0]->expr() + " + " + std::to_string(_params[1]) + ")";}
+
+    // DocString: sin_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return Domain(-1.0, 1.0);}
+
+    // DocString: add_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateSinParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.cpp
index 00df1259..b0a006ec 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(SinParamNode)
+
 void generateSinParamNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.hpp
index a08f04e6..1824afe5 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.hpp
@@ -19,13 +19,6 @@
  */
 class SinParamNode: public SinNode
 {
-    using SinNode::set_value;
-    using SinNode::set_test_value;
-    using SinNode::value_ptr;
-    using SinNode::test_value_ptr;
-    using SinNode::domain;
-    using SinNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -41,6 +34,13 @@ class SinParamNode: public SinNode
     }
 
 protected:
+    using SinNode::set_value;
+    using SinNode::set_test_value;
+    using SinNode::value_ptr;
+    using SinNode::test_value_ptr;
+    using SinNode::domain;
+    using SinNode::expr;
+
     std::vector<double> _params; //!< The parameters vector
 
 public:
@@ -82,7 +82,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    SinParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+    SinParamNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: sin_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize_top_lev_sixth_power.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize_top_lev_sixth_power.cpp
new file mode 100644
index 00000000..56f3fff8
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize_top_lev_sixth_power.cpp
@@ -0,0 +1,60 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize_top_lev_sixth_power.hpp>
+
+void generateSixPowParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+    node_ptr new_feat = std::make_shared<SixPowParamTopLevNode>(feat, feat_ind, prop);
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+SixPowParamTopLevNode::SixPowParamTopLevNode()
+{}
+
+SixPowParamTopLevNode::SixPowParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SixPowParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+SixPowParamTopLevNode::SixPowParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop) :
+    SixPowParamNode(feat, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+SixPowParamTopLevNode::SixPowParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    SixPowParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void SixPowParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::sixth_pow(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::sixth_pow(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void SixPowParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::sixth_pow(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void SixPowParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[0] = 1.0;
+    ub[0] = 1.0;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize_top_lev_sixth_power.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize_top_lev_sixth_power.hpp
new file mode 100644
index 00000000..b49b8ffa
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize_top_lev_sixth_power.hpp
@@ -0,0 +1,131 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize_top_levsixth_pow.hpp
+ *  @brief Class describing the parameterized sixth power operator
+ *
+ *  This class represents the parameterized unary operator -> (alpha * A + a)^6
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_SIX_POW_NODE
+#define PARAM_TOP_LEV_SIX_POW_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class SixPowParamTopLevNode: public SixPowParamNode
+{
+    using SixPowParamNode::set_value;
+    using SixPowParamNode::set_test_value;
+    using SixPowParamNode::value_ptr;
+    using SixPowParamNode::test_value_ptr;
+    using SixPowParamNode::domain;
+    using SixPowParamNode::expr;
+
+    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<SixPowParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    SixPowParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     * @param prop The property to fit to
+     */
+    SixPowParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    SixPowParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    SixPowParamTopLevNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: six_pow_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: six_pow_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: six_pow_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: six_pow_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "(" + std::to_string(_params[0]) + "*" + _feats[0]->expr() + " + " + std::to_string(_params[1]) + ")^6";}
+
+    // DocString: six_pow_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().pow(6.0, _params[0], _params[1]);}
+
+    // DocString: add_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateSixPowParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.cpp
index d317ebab..e650e8cf 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(SixPowParamNode)
+
 void generateSixPowParamNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.hpp
index 903c68bc..0ff6829d 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.hpp
@@ -19,13 +19,6 @@
  */
 class SixPowParamNode: public SixPowNode
 {
-    using SixPowNode::set_value;
-    using SixPowNode::set_test_value;
-    using SixPowNode::value_ptr;
-    using SixPowNode::test_value_ptr;
-    using SixPowNode::domain;
-    using SixPowNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -41,6 +34,13 @@ class SixPowParamNode: public SixPowNode
     }
 
 protected:
+    using SixPowNode::set_value;
+    using SixPowNode::set_test_value;
+    using SixPowNode::value_ptr;
+    using SixPowNode::test_value_ptr;
+    using SixPowNode::domain;
+    using SixPowNode::expr;
+
     std::vector<double> _params; //!< The parameters vector
 
 public:
@@ -83,7 +83,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    SixPowParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+    SixPowParamNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: six_pow_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize_top_lev_square.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize_top_lev_square.cpp
new file mode 100644
index 00000000..1aab7601
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize_top_lev_square.cpp
@@ -0,0 +1,60 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize_top_lev_square.hpp>
+
+void generateSqParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+    node_ptr new_feat = std::make_shared<SqParamTopLevNode>(feat, feat_ind, prop);
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+SqParamTopLevNode::SqParamTopLevNode()
+{}
+
+SqParamTopLevNode::SqParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SqParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+SqParamTopLevNode::SqParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop) :
+    SqParamNode(feat, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+SqParamTopLevNode::SqParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    SqParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void SqParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::sq(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::sq(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void SqParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::sq(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void SqParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[0] = 1.0;
+    ub[0] = 1.0;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize_top_lev_square.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize_top_lev_square.hpp
new file mode 100644
index 00000000..5c2a9c09
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize_top_lev_square.hpp
@@ -0,0 +1,130 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize_top_levsquare.hpp
+ *  @brief Class describing the parameterized square operator
+ *
+ *  This class represents the parameterized unary operator -> (alpha * A + a)^2
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_SQ_NODE
+#define PARAM_TOP_LEV_SQ_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class SqParamTopLevNode: public SqParamNode
+{
+    using SqParamNode::set_value;
+    using SqParamNode::set_test_value;
+    using SqParamNode::value_ptr;
+    using SqParamNode::test_value_ptr;
+    using SqParamNode::domain;
+    using SqParamNode::expr;
+
+    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<SqParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    SqParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param prop The property to fit to
+     */
+    SqParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    SqParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    SqParamTopLevNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: sq_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: sq_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: sq_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: sq_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "sqrt(" + std::to_string(_params[0]) + "*" + _feats[0]->expr() + " + " + std::to_string(_params[1]) + ")";}
+
+    // DocString: sq_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().pow(1.0 / 2.0, _params[0], _params[1]);}
+
+    // DocString: add_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateSqParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.cpp
index be9f569a..6982914a 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(SqParamNode)
+
 void generateSqParamNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.hpp
index f7e69bc0..d3eeb416 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.hpp
@@ -19,13 +19,6 @@
  */
 class SqParamNode: public SqNode
 {
-    using SqNode::set_value;
-    using SqNode::set_test_value;
-    using SqNode::value_ptr;
-    using SqNode::test_value_ptr;
-    using SqNode::domain;
-    using SqNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -41,6 +34,13 @@ class SqParamNode: public SqNode
     }
 
 protected:
+    using SqNode::set_value;
+    using SqNode::set_test_value;
+    using SqNode::value_ptr;
+    using SqNode::test_value_ptr;
+    using SqNode::domain;
+    using SqNode::expr;
+
     std::vector<double> _params; //!< The parameters vector
 
 public:
@@ -82,7 +82,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    SqParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+    SqParamNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: sq_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize_top_lev_square_root.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize_top_lev_square_root.cpp
new file mode 100644
index 00000000..fc4fe659
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize_top_lev_square_root.cpp
@@ -0,0 +1,66 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize_top_lev_square_root.hpp>
+
+void generateSqrtParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+    node_ptr new_feat = std::make_shared<SqrtParamTopLevNode>(feat, feat_ind, prop);
+
+    if(new_feat->parameters()[0] * feat->domain() + new_feat->parameters()[1] <= 0.0)
+        return;
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+SqrtParamTopLevNode::SqrtParamTopLevNode()
+{}
+
+SqrtParamTopLevNode::SqrtParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SqrtParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if((_params[0] * _feats[0]->domain() + _params[1]) <= 0.0)
+        throw InvalidFeatureException();
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+SqrtParamTopLevNode::SqrtParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop) :
+    SqrtParamNode(feat, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+SqrtParamTopLevNode::SqrtParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    SqrtParamNode(feat, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void SqrtParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::sqrt(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::sqrt(_n_samp, _feats[0]->value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void SqrtParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::sqrt(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void SqrtParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[0] = _sign_alpha;
+    ub[0] = _sign_alpha;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize_top_lev_square_root.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize_top_lev_square_root.hpp
new file mode 100644
index 00000000..40158ff0
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize_top_lev_square_root.hpp
@@ -0,0 +1,130 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize_top_levsquare_root.hpp
+ *  @brief Class describing the parameterized square root operator
+ *
+ *  This class represents the parameterized unary operator -> sqrt(alpha * A + a)
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_SQRT_NODE
+#define PARAM_TOP_LEV_SQRT_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class SqrtParamTopLevNode: public SqrtParamNode
+{
+    using SqrtParamNode::set_value;
+    using SqrtParamNode::set_test_value;
+    using SqrtParamNode::value_ptr;
+    using SqrtParamNode::test_value_ptr;
+    using SqrtParamNode::domain;
+    using SqrtParamNode::expr;
+
+    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<SqrtParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    SqrtParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param prop The property to fit to
+     */
+    SqrtParamTopLevNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    SqrtParamTopLevNode(node_ptr feat, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    SqrtParamTopLevNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: sqrt_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: sqrt_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: sqrt_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: sqrt_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "sqrt(" + std::to_string(_params[0]) + "*" + _feats[0]->expr() + " + " + std::to_string(_params[1]) + ")";}
+
+    // DocString: sqrt_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().pow(1.0 / 2.0, _params[0], _params[1]);}
+
+    // DocString: add_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateSqrtParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.cpp
index 589e599b..2edeb2da 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(SqrtParamNode)
+
 void generateSqrtParamNode(std::vector<node_ptr>& feat_list, node_ptr feat, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.hpp
index acfcf9db..d8ee9f35 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.hpp
@@ -19,13 +19,6 @@
  */
 class SqrtParamNode: public SqrtNode
 {
-    using SqrtNode::set_value;
-    using SqrtNode::set_test_value;
-    using SqrtNode::value_ptr;
-    using SqrtNode::test_value_ptr;
-    using SqrtNode::domain;
-    using SqrtNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -42,6 +35,13 @@ class SqrtParamNode: public SqrtNode
     }
 
 protected:
+    using SqrtNode::set_value;
+    using SqrtNode::set_test_value;
+    using SqrtNode::value_ptr;
+    using SqrtNode::test_value_ptr;
+    using SqrtNode::domain;
+    using SqrtNode::expr;
+
     std::vector<double> _params; //!< The parameters vector
     double _sign_alpha; //!< 1 if alpha is positive, -1 if alpha is negative
 
@@ -84,7 +84,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    SqrtParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+    SqrtParamNode(node_ptr feat, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: sqrt_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize_top_lev_subtract.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize_top_lev_subtract.cpp
new file mode 100644
index 00000000..da611609
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize_top_lev_subtract.cpp
@@ -0,0 +1,60 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize_top_lev_subtract.hpp>
+
+void generateSubParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
+{
+    ++feat_ind;
+    node_ptr new_feat = std::make_shared<SubParamTopLevNode>(feat_1, feat_2, feat_ind, prop);
+
+    new_feat->set_value();
+    if(new_feat->is_nan() || new_feat->is_const() || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) > u_bound) || (util_funcs::max_abs_val<double>(new_feat->value_ptr(), new_feat->n_samp()) < l_bound))
+        return;
+
+    feat_list.push_back(new_feat);
+}
+
+SubParamTopLevNode::SubParamTopLevNode()
+{}
+
+SubParamTopLevNode::SubParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SubParamNode(feat_1, feat_2, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+
+    if(is_nan() || is_const() || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) > u_bound) || (util_funcs::max_abs_val<double>(value_ptr(), _n_samp) < l_bound))
+        throw InvalidFeatureException();
+}
+
+SubParamTopLevNode::SubParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, std::vector<double>& prop) :
+    SubParamNode(feat_1, feat_2, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+    get_parameters(prop);
+}
+
+SubParamTopLevNode::SubParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound) :
+    SubParamNode(feat_1, feat_2, feat_ind, l_bound, u_bound)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void SubParamTopLevNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+
+    if(_selected)
+        allowed_op_funcs::sub(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+    allowed_op_funcs::sub(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void SubParamTopLevNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::sub(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _feats[1]->test_value_ptr(offset + 1), _params[0], _params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void SubParamTopLevNode::set_bounds(double* lb, double* ub, int from_parent)
+{
+    lb[1] = 0.0;
+    ub[1] = 0.0;
+}
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize_top_lev_subtract.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize_top_lev_subtract.hpp
new file mode 100644
index 00000000..5fac02dc
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize_top_lev_subtract.hpp
@@ -0,0 +1,132 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize_top_levsubtract.hpp
+ *  @brief Class describing the parameterized addition operator
+ *
+ *  This class represents the parameterized unary operator -> A - (alpha * B + a)
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PARAM_TOP_LEV_SUB_NODE
+#define PARAM_TOP_LEV_SUB_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.hpp>
+#include <nl_opt/NLOptWrapper.hpp>
+
+// DocString: cls_param_add_node
+/**
+ * @brief ParamNode for the absolute value operator
+ *
+ */
+class SubParamTopLevNode: public SubParamNode
+{
+    using SubParamNode::set_value;
+    using SubParamNode::set_test_value;
+    using SubParamNode::value_ptr;
+    using SubParamNode::test_value_ptr;
+    using SubParamNode::domain;
+    using SubParamNode::expr;
+
+    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<SubParamNode>(*this);
+    }
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    SubParamTopLevNode();
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_2 shared_ptr of the feature to operate on (B)
+     * @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.
+     * @param prop The property to fit to
+     */
+    SubParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @param feat_2 shared_ptr of the feature to operate on (B)
+     * @param feat_ind Index of the new feature
+     * @param prop The property to fit to
+     */
+    SubParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the ParamNode from node pointer of the feature to operate on
+     *
+     * @param feat_1 shared_ptr of the feature to operate on (A)
+     * @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.
+     * @param param_list The list of parameters to optimize using non-linear least squares
+     */
+    SubParamTopLevNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
+
+    // DocString: sub_param_top_lev_node_n_params
+    /**
+     * @brief returns the number of theoretical parameters for this feature
+     * @return the number of theoretical parameters
+     */
+    inline int n_params(int n_cur = 0){return 2;}
+
+    // DocString: sub_param_top_lev_node_set_value
+    /**
+     * @brief Set the values of the training data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_value(int offset = -1);
+
+    // DocString: sub_param_top_lev_node_set_test_value
+    /**
+     * @brief Set the values of the test data for the feature inside of the value storage arrays
+     *
+     * @param offset(int) Key to determine which part of the temporary storage array to look into
+     */
+    void set_test_value(int offset = -1);
+
+    // DocString: sub_param_top_lev_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return "[(" + _feats[0]->expr() + ") - (" + std::to_string(_params[0]) + "*" + _feats[1]->expr() + " + " + std::to_string(_params[1]) + ")]";}
+
+    // DocString: sub_param_top_lev_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return _feats[0]->domain().sub(_feats[1]->domain(), _params[0], _params[1]);}
+
+    // DocString: add_param_top_lev_node_set_bounds
+    /**
+     * @brief Set the bounds for the nl parameterization
+     *
+     * @param lb pointer to the lower bounds data
+     * @param ub pointer to the upper bounds data
+     */
+    void set_bounds(double* lb, double* ub, int from_parent=2);
+};
+
+void generateSubParamTopLevNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.cpp
index cb5b4824..cb223580 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(SubParamNode)
+
 void generateSubParamNode(std::vector<node_ptr>& feat_list, node_ptr feat_1, node_ptr feat_2, int& feat_ind, double l_bound, double u_bound, std::vector<double>& prop)
 {
     ++feat_ind;
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.hpp
index 2a0a3aec..611bdb04 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.hpp
@@ -19,13 +19,6 @@
  */
 class SubParamNode: public SubNode
 {
-    using SubNode::set_value;
-    using SubNode::set_test_value;
-    using SubNode::value_ptr;
-    using SubNode::test_value_ptr;
-    using SubNode::domain;
-    using SubNode::expr;
-
     friend class boost::serialization::access;
 
     /**
@@ -39,8 +32,17 @@ class SubParamNode: public SubNode
         ar & boost::serialization::base_object<SubNode>(*this);
         ar & _params;
     }
+
 protected:
+    using SubNode::set_value;
+    using SubNode::set_test_value;
+    using SubNode::value_ptr;
+    using SubNode::test_value_ptr;
+    using SubNode::domain;
+    using SubNode::expr;
+
     std::vector<double> _params;
+
 public:
     /**
      * @brief Base Constructor
@@ -82,7 +84,7 @@ public:
      * @param u_bound Maximum absolute value allowed for the feature.
      * @param param_list The list of parameters to optimize using non-linear least squares
      */
-    SubParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
+    SubParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound=1e-50, double u_bound=1e50);
 
     // DocString: sub_param_node_set_value
     /**
diff --git a/src/feature_creation/node/operator_nodes/allowed_ops.hpp b/src/feature_creation/node/operator_nodes/allowed_ops.hpp
index dfd9d17f..07cc1fed 100644
--- a/src/feature_creation/node/operator_nodes/allowed_ops.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_ops.hpp
@@ -7,41 +7,23 @@
 #ifndef ALLOWED_OP_NODES
 #define ALLOWED_OP_NODES
 
-#include <feature_creation/node/Node.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/add/add.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sub/subtract.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs/absolute_value.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/absolute_difference.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/mult/multiply.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/div/divide.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/inv/inverse.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/exp/exponential.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/negative_exponential.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/log/log.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sq/square.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cb/cube.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/sixth_power.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/square_root.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/cube_root.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sin/sin.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cos/cos.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.hpp>
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize_top_lev_add.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize_top_lev_subtract.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize_top_lev_absolute_value.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize_top_lev_absolute_difference.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize_top_lev_multiply.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize_top_lev_divide.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize_top_lev_inverse.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize_top_lev_exponential.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize_top_lev_negative_exponential.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize_top_lev_log.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize_top_lev_square.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize_top_lev_cube.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize_top_lev_sixth_power.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize_top_lev_square_root.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize_top_lev_cube_root.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize_top_lev_sin.hpp>
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize_top_lev_cos.hpp>
 #include <map>
 #include <iostream>
 
@@ -69,4 +51,4 @@ namespace allowed_op_maps
 };
 
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_parameter_ops.cpp b/src/feature_creation/node/operator_nodes/allowed_parameter_ops.cpp
index 17fea3f1..6d089f0b 100644
--- a/src/feature_creation/node/operator_nodes/allowed_parameter_ops.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_parameter_ops.cpp
@@ -5,22 +5,46 @@ std::map<std::string, bin_param_op_node_gen> allowed_op_maps::binary_param_opera
 
 void allowed_op_maps::set_param_node_maps()
 {
-    allowed_op_maps::binary_param_operator_map["add"] = generateAddParamNode;
-    allowed_op_maps::binary_param_operator_map["sub"] = generateSubParamNode;
-    allowed_op_maps::binary_param_operator_map["abs_diff"] = generateAbsDiffParamNode;
-    allowed_op_maps::binary_param_operator_map["mult"] = generateMultParamNode;
-    allowed_op_maps::binary_param_operator_map["div"] = generateDivParamNode;
+    if(nlopt_wrapper::_param_internal)
+    {
+        allowed_op_maps::binary_param_operator_map["add"] = generateAddParamNode;
+        allowed_op_maps::binary_param_operator_map["sub"] = generateSubParamNode;
+        allowed_op_maps::binary_param_operator_map["abs_diff"] = generateAbsDiffParamNode;
+        allowed_op_maps::binary_param_operator_map["mult"] = generateMultParamNode;
+        allowed_op_maps::binary_param_operator_map["div"] = generateDivParamNode;
 
-    allowed_op_maps::unary_param_operator_map["exp"] = generateExpParamNode;
-    allowed_op_maps::unary_param_operator_map["neg_exp"] = generateNegExpParamNode;
-    allowed_op_maps::unary_param_operator_map["inv"] = generateInvParamNode;
-    allowed_op_maps::unary_param_operator_map["sq"] = generateSqParamNode;
-    allowed_op_maps::unary_param_operator_map["cb"] = generateCbParamNode;
-    allowed_op_maps::unary_param_operator_map["six_pow"] = generateSixPowParamNode;
-    allowed_op_maps::unary_param_operator_map["sqrt"] = generateSqrtParamNode;
-    allowed_op_maps::unary_param_operator_map["cbrt"] = generateCbrtParamNode;
-    allowed_op_maps::unary_param_operator_map["log"] = generateLogParamNode;
-    allowed_op_maps::unary_param_operator_map["abs"] = generateAbsParamNode;
-    allowed_op_maps::unary_param_operator_map["sin"] = generateSinParamNode;
-    allowed_op_maps::unary_param_operator_map["cos"] = generateCosParamNode;
+        allowed_op_maps::unary_param_operator_map["exp"] = generateExpParamNode;
+        allowed_op_maps::unary_param_operator_map["neg_exp"] = generateNegExpParamNode;
+        allowed_op_maps::unary_param_operator_map["inv"] = generateInvParamNode;
+        allowed_op_maps::unary_param_operator_map["sq"] = generateSqParamNode;
+        allowed_op_maps::unary_param_operator_map["cb"] = generateCbParamNode;
+        allowed_op_maps::unary_param_operator_map["six_pow"] = generateSixPowParamNode;
+        allowed_op_maps::unary_param_operator_map["sqrt"] = generateSqrtParamNode;
+        allowed_op_maps::unary_param_operator_map["cbrt"] = generateCbrtParamNode;
+        allowed_op_maps::unary_param_operator_map["log"] = generateLogParamNode;
+        allowed_op_maps::unary_param_operator_map["abs"] = generateAbsParamNode;
+        allowed_op_maps::unary_param_operator_map["sin"] = generateSinParamNode;
+        allowed_op_maps::unary_param_operator_map["cos"] = generateCosParamNode;
+    }
+    else
+    {
+        allowed_op_maps::binary_param_operator_map["add"] = generateAddParamTopLevNode;
+        allowed_op_maps::binary_param_operator_map["sub"] = generateSubParamTopLevNode;
+        allowed_op_maps::binary_param_operator_map["abs_diff"] = generateAbsDiffParamTopLevNode;
+        allowed_op_maps::binary_param_operator_map["mult"] = generateMultParamTopLevNode;
+        allowed_op_maps::binary_param_operator_map["div"] = generateDivParamTopLevNode;
+
+        allowed_op_maps::unary_param_operator_map["exp"] = generateExpParamTopLevNode;
+        allowed_op_maps::unary_param_operator_map["neg_exp"] = generateNegExpParamTopLevNode;
+        allowed_op_maps::unary_param_operator_map["inv"] = generateInvParamTopLevNode;
+        allowed_op_maps::unary_param_operator_map["sq"] = generateSqParamTopLevNode;
+        allowed_op_maps::unary_param_operator_map["cb"] = generateCbParamTopLevNode;
+        allowed_op_maps::unary_param_operator_map["six_pow"] = generateSixPowParamTopLevNode;
+        allowed_op_maps::unary_param_operator_map["sqrt"] = generateSqrtParamTopLevNode;
+        allowed_op_maps::unary_param_operator_map["cbrt"] = generateCbrtParamTopLevNode;
+        allowed_op_maps::unary_param_operator_map["log"] = generateLogParamTopLevNode;
+        allowed_op_maps::unary_param_operator_map["abs"] = generateAbsParamTopLevNode;
+        allowed_op_maps::unary_param_operator_map["sin"] = generateSinParamTopLevNode;
+        allowed_op_maps::unary_param_operator_map["cos"] = generateCosParamTopLevNode;
+    }
 }
diff --git a/src/inputs/InputParser.cpp b/src/inputs/InputParser.cpp
index fe95fd4d..da318daf 100644
--- a/src/inputs/InputParser.cpp
+++ b/src/inputs/InputParser.cpp
@@ -20,7 +20,8 @@ InputParser::InputParser(pt::ptree IP, std::string fn, std::shared_ptr<MPI_Inter
     _n_samp(0),
     _n_residuals(IP.get<int>("n_residual", 1)),
     _n_models_store(IP.get<int>("n_models_store", _n_residuals)),
-    _fix_intercept(IP.get<bool>("fix_intercept", false))
+    _fix_intercept(IP.get<bool>("fix_intercept", false)),
+    _param_internal(IP.get<bool>("param_internal", true))
 {
     std::ifstream data_stream;
     std::string line;
@@ -306,7 +307,7 @@ void InputParser::generate_feature_space(std::shared_ptr<MPI_Interface> comm, st
     }
 
     if(_calc_type.compare("log_regression_fit_c") == 0)
-        nlopt_wrapper::setup_data(_task_sizes_train, _n_dim, _max_rung);
+        nlopt_wrapper::setup_data(_task_sizes_train, _n_dim, _max_rung, _param_internal);
 
     _feat_space = std::make_shared<FeatureSpace>(comm, phi_0, _opset, _param_opset, _prop_train, _task_sizes_train, _calc_type, _max_rung, _n_sis_select, _max_store_rung, _n_rung_generate, _cross_cor_max, _l_bound, _u_bound);
 }
diff --git a/src/inputs/InputParser.hpp b/src/inputs/InputParser.hpp
index e71265ac..b31e44d5 100644
--- a/src/inputs/InputParser.hpp
+++ b/src/inputs/InputParser.hpp
@@ -69,6 +69,7 @@ public:
     int _n_models_store; //!< Number of models to store
 
     bool _fix_intercept; //!< If true force intercept to be 0.0
+    bool _param_internal; //!< If true parameterize features fully (not just top level)
     /**
      * @brief Constructor
      *
diff --git a/src/nl_opt/NLOptWrapper.cpp b/src/nl_opt/NLOptWrapper.cpp
index 16932548..99fc9a59 100644
--- a/src/nl_opt/NLOptWrapper.cpp
+++ b/src/nl_opt/NLOptWrapper.cpp
@@ -11,12 +11,15 @@ std::vector<double> nlopt_wrapper::_work;
 std::vector<double> nlopt_wrapper::_a_copy;
 std::vector<double> nlopt_wrapper::_prop_copy;
 
+bool nlopt_wrapper::_param_internal;
+
 nlopt::algorithm nlopt_wrapper::_local_opt_alg = nlopt::LD_VAR2;
 double nlopt_wrapper::_cauchy_scaling = 0.5 * 0.5;
 int nlopt_wrapper::_n_samp = 0;
 
-void nlopt_wrapper::setup_data(std::vector<int> task_sizes, int max_dim, int n_rung)
+void nlopt_wrapper::setup_data(std::vector<int> task_sizes, int max_dim, int n_rung, bool param_internal)
 {
+    _param_internal = param_internal;
     _task_sizes = task_sizes;
     _zeros.resize(50, 0.0);
     _n_samp = std::accumulate(task_sizes.begin(), task_sizes.end(), 0.0);
@@ -36,24 +39,24 @@ void nlopt_wrapper::setup_data(std::vector<int> task_sizes, int max_dim, int n_r
     }
 }
 
-void nlopt_wrapper::set_objective(std::string calc_type, double* prop, std::vector<int> sizes, int n_rung)
+void nlopt_wrapper::set_objective(std::string calc_type, double* prop, std::vector<int> sizes, int n_rung, bool param_internal)
 {
     if(calc_type.compare("classification") == 0)
     {
         convex_hull::initialize_projection(sizes, prop);
         _objective = objective_class;
         _local_opt_alg = nlopt::LN_SBPLX;
-        setup_data(sizes, 1, 0);
+        setup_data(sizes, 1, 0, param_internal);
     }
     else if(calc_type.compare("regression") == 0)
     {
         _objective = objective_reg;
-        setup_data(sizes, 1, n_rung);
+        setup_data(sizes, 1, n_rung, param_internal);
     }
     else if(calc_type.compare("log_regression") == 0)
     {
         _objective = objective_log_reg;
-        setup_data(sizes, 1, n_rung);
+        setup_data(sizes, 1, n_rung, param_internal);
     }
     else if(calc_type.compare("log_regression_fit_c") == 0)
     {
@@ -70,8 +73,8 @@ double nlopt_wrapper::optimize_feature_params(feat_data data, bool use_simplex)
 {
     double minf = 0.0;
 
-    std::vector<double> params(data._feat->parameters().size() + 2 * _task_sizes.size(), 1.0);
-    std::vector<double> params_final(data._feat->parameters().size(), 1.0);
+    std::vector<double> params((_param_internal ? data._feat->parameters().size() : 2) + 2 * _task_sizes.size(), 1.0);
+    std::vector<double> params_final((_param_internal ? data._feat->parameters().size() : 2), 1.0);
 
     dcopy_(params.size() / 2, _zeros.data(), 1, &params[1], 2);
     dcopy_(params_final.size() / 2, _zeros.data(), 1, &params_final[1], 2);
diff --git a/src/nl_opt/NLOptWrapper.hpp b/src/nl_opt/NLOptWrapper.hpp
index 3fc792e2..a5880a7c 100644
--- a/src/nl_opt/NLOptWrapper.hpp
+++ b/src/nl_opt/NLOptWrapper.hpp
@@ -27,6 +27,8 @@ namespace nlopt_wrapper
     extern int _n_samp; //!< total number of samples
     extern nlopt::algorithm _local_opt_alg; //!< Algorithm used for local optimization
 
+    extern bool _param_internal; //!< parameterize features to all depths of the tree
+
     #pragma omp threadprivate(_log_trans, _work, _a_copy, _residuals, _feature_gradient,_prop_copy)
 
     typedef struct
@@ -250,7 +252,7 @@ namespace nlopt_wrapper
      * @param max_dim Maximum dimension of the features
      * @param n_rung maximum rung of a feature
      */
-    void setup_data(std::vector<int> task_sizes, int max_dim, int n_rung);
+    void setup_data(std::vector<int> task_sizes, int max_dim, int n_rung, bool param_internal=true);
 
     /**
      * @brief Set up the projection operator for the objective function
@@ -260,7 +262,7 @@ namespace nlopt_wrapper
      * @param sizes number of samples per task
      * @param n_rung maximum rung of a feature
      */
-    void set_objective(std::string calc_type, double* prop, std::vector<int> sizes, int n_rung);
+    void set_objective(std::string calc_type, double* prop, std::vector<int> sizes, int n_rung, bool param_internal=true);
 
     #ifdef PY_BINDINGS
         /**
@@ -270,7 +272,7 @@ namespace nlopt_wrapper
          * @param max_dim Maximum dimension of the features
          * @param n_rung maximum rung of a feature
          */
-        inline void setup_data(py::list task_sizes, int max_dim, int n_rung){setup_data(python_conv_utils::from_list<int>(task_sizes), max_dim, n_rung);}
+        inline void setup_data(py::list task_sizes, int max_dim, int n_rung, bool param_internal=true){setup_data(python_conv_utils::from_list<int>(task_sizes), max_dim, n_rung, param_internal);}
 
         /**
          * @brief Set up the projection operator for the objective function
@@ -279,7 +281,7 @@ namespace nlopt_wrapper
          * @param max_dim Maximum dimension of the features
          * @param n_rung maximum rung of a feature
          */
-        inline void setup_data(np::ndarray task_sizes, int max_dim, int n_rung){setup_data(python_conv_utils::from_ndarray<int>(task_sizes), max_dim, n_rung);}
+        inline void setup_data(np::ndarray task_sizes, int max_dim, int n_rung, bool param_internal=true){setup_data(python_conv_utils::from_ndarray<int>(task_sizes), max_dim, n_rung, param_internal);}
 
         /**
          * @brief Set up the projection operator for the objective function
@@ -289,10 +291,10 @@ namespace nlopt_wrapper
          * @param N number of samples per task
          * @param n_rung maximum rung of a feature
          */
-        inline void set_objective(std::string calc_type, py::list prop, py::list sizes, int n_rung)
+        inline void set_objective(std::string calc_type, py::list prop, py::list sizes, int n_rung, bool param_internal=true)
         {
             std::vector<double> prop_vec = python_conv_utils::from_list<double>(prop);
-            return set_objective(calc_type, prop_vec.data(), python_conv_utils::from_list<int>(sizes), n_rung);
+            return set_objective(calc_type, prop_vec.data(), python_conv_utils::from_list<int>(sizes), n_rung, param_internal);
         }
 
         /**
@@ -303,10 +305,10 @@ namespace nlopt_wrapper
          * @param N number of samples per task
          * @param n_rung maximum rung of a feature
          */
-        inline void set_objective(std::string calc_type, np::ndarray prop, py::list sizes, int n_rung)
+        inline void set_objective(std::string calc_type, np::ndarray prop, py::list sizes, int n_rung, bool param_internal=true)
         {
             std::vector<double> prop_vec = python_conv_utils::from_ndarray<double>(prop);
-            return set_objective(calc_type, prop_vec.data(), python_conv_utils::from_list<int>(sizes), n_rung);
+            return set_objective(calc_type, prop_vec.data(), python_conv_utils::from_list<int>(sizes), n_rung, param_internal);
         }
 
         /**
@@ -317,10 +319,10 @@ namespace nlopt_wrapper
          * @param N number of samples per task
          * @param n_rung maximum rung of a feature
          */
-        inline void set_objective(std::string calc_type, np::ndarray prop, np::ndarray sizes, int n_rung)
+        inline void set_objective(std::string calc_type, np::ndarray prop, np::ndarray sizes, int n_rung, bool param_internal=true)
         {
             std::vector<double> prop_vec = python_conv_utils::from_ndarray<double>(prop);
-            return set_objective(calc_type, prop_vec.data(), python_conv_utils::from_ndarray<int>(sizes), n_rung);
+            return set_objective(calc_type, prop_vec.data(), python_conv_utils::from_ndarray<int>(sizes), n_rung, param_internal);
         }
     #endif
 }
diff --git a/src/python/bindings_docstring_keyed.cpp b/src/python/bindings_docstring_keyed.cpp
index 22248de5..0b554fd4 100644
--- a/src/python/bindings_docstring_keyed.cpp
+++ b/src/python/bindings_docstring_keyed.cpp
@@ -61,16 +61,16 @@ void sisso::register_all()
     def("initialize_values_arr", &node_value_arrs::initialize_values_arr);
     def("initialize_d_matrix_arr", &node_value_arrs::initialize_d_matrix_arr);
 
-    void (*set_objective_list_list)(std::string, py::list, py::list, int) = &nlopt_wrapper::set_objective;
-    void (*set_objective_arr_list)(std::string, np::ndarray, py::list, int) = &nlopt_wrapper::set_objective;
-    void (*set_objective_arr_arr)(std::string, np::ndarray, np::ndarray, int) = &nlopt_wrapper::set_objective;
+    void (*set_objective_list_list)(std::string, py::list, py::list, int, bool) = &nlopt_wrapper::set_objective;
+    void (*set_objective_arr_list)(std::string, np::ndarray, py::list, int, bool) = &nlopt_wrapper::set_objective;
+    void (*set_objective_arr_arr)(std::string, np::ndarray, np::ndarray, int, bool) = &nlopt_wrapper::set_objective;
 
     def("set_objective", set_objective_list_list);
     def("set_objective", set_objective_arr_list);
     def("set_objective", set_objective_arr_arr);
 
-    void(*setup_data_list)(py::list, int, int) = &nlopt_wrapper::setup_data;
-    void(*setup_data_arr)(np::ndarray, int, int) = &nlopt_wrapper::setup_data;
+    void(*setup_data_list)(py::list, int, int, bool) = &nlopt_wrapper::setup_data;
+    void(*setup_data_arr)(np::ndarray, int, int, bool) = &nlopt_wrapper::setup_data;
     def("setup_data", setup_data_list);
     def("setup_data", setup_data_arr);
 }
@@ -80,8 +80,8 @@ void sisso::feature_creation::registerFeatureSpace()
     void (FeatureSpace::*sis_list)(list) = &FeatureSpace::sis;
     void (FeatureSpace::*sis_ndarray)(np::ndarray) = &FeatureSpace::sis;
 
-    class_<FeatureSpace>("FeatureSpace", init<list, list, list, np::ndarray, list, optional<std::string, int, int, int, int, double, double, double>>())
-        .def(init<list, list, list, list, list, optional<std::string, int, int, int, int, double, double, double>>())
+    class_<FeatureSpace>("FeatureSpace", init<list, list, list, np::ndarray, list, optional<std::string, int, int, int, int, double, double, double, bool>>())
+        .def(init<list, list, list, list, list, optional<std::string, int, int, int, int, double, double, double, bool>>())
         .def(init<std::string, list, list, optional<std::string, int, double>>())
         .def("sis", sis_list, "@DocString_feat_space_sis_list@")
         .def("sis", sis_ndarray, "@DocString_feat_space_sis_arr@")
diff --git a/src/python/feature_creation/FeatureSpace.cpp b/src/python/feature_creation/FeatureSpace.cpp
index 3d107fe3..5857678e 100644
--- a/src/python/feature_creation/FeatureSpace.cpp
+++ b/src/python/feature_creation/FeatureSpace.cpp
@@ -13,7 +13,8 @@ FeatureSpace::FeatureSpace(
     int n_rung_generate,
     double cross_corr_max,
     double min_abs_feat_val,
-    double max_abs_feat_val
+    double max_abs_feat_val,
+    bool param_internal
 ):
     _phi(python_conv_utils::shared_ptr_vec_from_list<Node, FeatureNode>(phi_0)),
     _phi_0(_phi),
@@ -34,7 +35,8 @@ FeatureSpace::FeatureSpace(
     _n_feat(py::len(phi_0)),
     _n_rung_store(max_store_rung),
     _n_rung_generate(n_rung_generate),
-    _n_samp(_phi[0]->n_samp())
+    _n_samp(_phi[0]->n_samp()),
+    _param_internal(param_internal)
 {
     initialize_fs(project_type);
 }
@@ -52,7 +54,8 @@ FeatureSpace::FeatureSpace(
     int n_rung_generate,
     double cross_corr_max,
     double min_abs_feat_val,
-    double max_abs_feat_val
+    double max_abs_feat_val,
+    bool param_internal
 ):
     _phi(python_conv_utils::shared_ptr_vec_from_list<Node, FeatureNode>(phi_0)),
     _phi_0(_phi),
@@ -73,7 +76,8 @@ FeatureSpace::FeatureSpace(
     _n_feat(py::len(phi_0)),
     _n_rung_store(max_store_rung),
     _n_rung_generate(n_rung_generate),
-    _n_samp(_phi[0]->n_samp())
+    _n_samp(_phi[0]->n_samp()),
+    _param_internal(param_internal)
 {
     initialize_fs(project_type);
 }
@@ -99,7 +103,8 @@ FeatureSpace::FeatureSpace(
     _n_feat(py::len(phi_0)),
     _n_rung_store(0),
     _n_rung_generate(0),
-    _n_samp(_phi_0[0]->n_samp())
+    _n_samp(_phi_0[0]->n_samp()),
+    _param_internal(true)
 {
     std::cout << "project: " << _phi_0.size() << std::endl;
     if(project_type.compare("regression") == 0)
-- 
GitLab