From 0a967d71e5fd14d80b350b58330c16c7c8507e6b Mon Sep 17 00:00:00 2001
From: Thomas Purcell <purcell@fhi-berlin.mpg.de>
Date: Thu, 15 Oct 2020 15:17:57 +0200
Subject: [PATCH] Upadated parameterization

Parameters now go down the tree not just the initial feature
---
 CMakeLists.txt                                | 143 +--
 src/CMakeLists.txt                            |  17 +-
 .../SISSO_DI/SISSORegressor.cpp               |   4 +
 src/feature_creation/domain/Domain.cpp        |   6 +-
 src/feature_creation/domain/Domain.hpp        |  29 +-
 .../feature_space/FeatureSpace.cpp            |  44 +-
 .../feature_space/FeatureSpace.hpp            |  16 +-
 src/feature_creation/node/FeatureNode.cpp     |   3 +
 src/feature_creation/node/FeatureNode.hpp     |  61 +-
 src/feature_creation/node/Node.hpp            |  63 +-
 .../node/operator_nodes/OperatorNode.hpp      | 162 +--
 .../abs/absolute_value.cpp                    |  42 +
 .../abs/absolute_value.hpp                    |  90 +-
 .../abs/parameterize.cpp                      | 208 ----
 .../abs/parameterized_absolute_value.cpp      |  65 ++
 .../abs/parameterized_absolute_value.hpp      | 153 +++
 .../abs_diff/absolute_difference.cpp          |  11 +-
 .../abs_diff/absolute_difference.hpp          |  96 +-
 .../abs_diff/parameterize.cpp                 | 212 ----
 .../parameterized_absolute_difference.cpp     |  65 ++
 .../parameterized_absolute_difference.hpp     | 153 +++
 .../allowed_operator_nodes/add/add.cpp        |   9 +
 .../allowed_operator_nodes/add/add.hpp        |  95 +-
 .../add/parameterize.cpp                      | 105 --
 .../add/parameterized_add.cpp                 |  65 ++
 .../add/parameterized_add.hpp                 | 151 +++
 .../allowed_operator_nodes/cb/cube.cpp        |  10 +
 .../allowed_operator_nodes/cb/cube.hpp        |  96 +-
 .../cb/parameterize.cpp                       | 105 --
 .../cb/parameterized_cube.cpp                 |  67 ++
 .../cb/parameterized_cube.hpp                 | 153 +++
 .../allowed_operator_nodes/cbrt/cube_root.cpp |  10 +
 .../allowed_operator_nodes/cbrt/cube_root.hpp |  96 +-
 .../cbrt/parameterize.cpp                     | 113 ---
 .../cbrt/parameterized_cube_root.cpp          |  75 ++
 .../cbrt/parameterized_cube_root.hpp          | 153 +++
 .../allowed_operator_nodes/cos/cos.cpp        |  26 +
 .../allowed_operator_nodes/cos/cos.hpp        |  88 +-
 .../cos/parameterize.cpp                      | 214 ----
 .../cos/parameterized_cos.cpp                 |  67 ++
 .../cos/parameterized_cos.hpp                 | 153 +++
 .../allowed_operator_nodes/div/divide.cpp     |   8 +
 .../allowed_operator_nodes/div/divide.hpp     |  96 +-
 .../div/parameterize.cpp                      | 113 ---
 .../div/parameterized_divide.cpp              |  74 ++
 .../div/parameterized_divide.hpp              | 151 +++
 .../exp/exponential.cpp                       |  10 +
 .../exp/exponential.hpp                       |  95 +-
 .../exp/parameterize.cpp                      | 105 --
 .../exp/parameterized_exponential.cpp         |  65 ++
 .../exp/parameterized_exponential.hpp         | 153 +++
 .../allowed_operator_nodes/inv/inverse.cpp    |  10 +
 .../allowed_operator_nodes/inv/inverse.hpp    |  97 +-
 .../inv/parameterize.cpp                      | 113 ---
 .../inv/parameterized_inverse.cpp             |  75 ++
 .../inv/parameterized_inverse.hpp             | 153 +++
 .../allowed_operator_nodes/log/log.cpp        |  14 +-
 .../allowed_operator_nodes/log/log.hpp        |  96 +-
 .../log/parameterize.cpp                      | 113 ---
 .../log/parameterized_log.cpp                 |  75 ++
 .../log/parameterized_log.hpp                 | 153 +++
 .../allowed_operator_nodes/mult/multiply.cpp  |   9 +
 .../allowed_operator_nodes/mult/multiply.hpp  |  93 +-
 .../mult/parameterize.cpp                     |  34 -
 .../mult/parameterized_multiply.cpp           |  67 ++
 .../mult/parameterized_multiply.hpp           | 151 +++
 .../neg_exp/negative_exponential.cpp          |  14 +-
 .../neg_exp/negative_exponential.hpp          |  96 +-
 .../neg_exp/parameterize.cpp                  | 105 --
 .../parameterized_negative_exponential.cpp    |  67 ++
 .../parameterized_negative_exponential.hpp    | 153 +++
 .../sin/parameterize.cpp                      | 214 ----
 .../sin/parameterized_sin.cpp                 |  69 ++
 .../sin/parameterized_sin.hpp                 | 153 +++
 .../allowed_operator_nodes/sin/sin.cpp        |  14 +-
 .../allowed_operator_nodes/sin/sin.hpp        |  93 +-
 .../six_pow/parameterize.cpp                  | 105 --
 .../six_pow/parameterized_sixth_power.cpp     |  69 ++
 .../six_pow/parameterized_sixth_power.hpp     | 153 +++
 .../six_pow/sixth_power.cpp                   |  10 +
 .../six_pow/sixth_power.hpp                   |  95 +-
 .../sq/parameterize.cpp                       | 105 --
 .../sq/parameterized_square.cpp               |  69 ++
 .../sq/parameterized_square.hpp               | 153 +++
 .../allowed_operator_nodes/sq/square.cpp      |  10 +
 .../allowed_operator_nodes/sq/square.hpp      |  95 +-
 .../sqrt/parameterize.cpp                     | 119 ---
 .../sqrt/parameterized_square_root.cpp        |  75 ++
 .../sqrt/parameterized_square_root.hpp        | 153 +++
 .../sqrt/square_root.cpp                      |  10 +
 .../sqrt/square_root.hpp                      |  95 +-
 .../sub/parameterize.cpp                      | 105 --
 .../sub/parameterized_subtract.cpp            |  67 ++
 .../sub/parameterized_subtract.hpp            | 151 +++
 .../allowed_operator_nodes/sub/subtract.cpp   |   9 +
 .../allowed_operator_nodes/sub/subtract.hpp   |  95 +-
 .../node/operator_nodes/allowed_ops.hpp       |  21 +-
 .../operator_nodes/allowed_parameter_ops.cpp  |  34 +-
 .../value_storage/nodes_value_containers.cpp  |   8 +-
 .../value_storage/nodes_value_containers.hpp  |   4 +-
 .../parameterization/NLOptWrapper.cpp         |  22 +
 .../parameterization/NLOptWrapper.hpp         |  49 +
 src/inputs/InputParser.cpp                    |  25 +-
 src/inputs/InputParser.hpp                    |   2 +-
 src/main.cpp                                  |   8 +-
 src/python/_sisso.cpp                         |  11 -
 src/python/bindings.cpp                       | 919 ++++++++++++++++++
 src/python/bindings.hpp                       | 378 +++++++
 src/python/bindings_docstring_keyed.cpp       | 573 +++++++++--
 src/python/bindings_docstring_keyed.hpp       | 128 ++-
 src/python/feature_creation/FeatureSpace.cpp  |  20 +-
 .../test_classification.py                    |  11 +-
 .../test_regressor.py                         |   2 +-
 .../test_feature_space/test_feature_space.py  |   2 +-
 tests/test_param.py                           |   6 +-
 tests/test_sisso.py                           |   2 +-
 116 files changed, 7398 insertions(+), 3165 deletions(-)
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.hpp
 delete mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.cpp
 create mode 100644 src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.hpp
 create mode 100644 src/feature_creation/parameterization/NLOptWrapper.cpp
 create mode 100644 src/feature_creation/parameterization/NLOptWrapper.hpp
 create mode 100644 src/python/bindings.cpp
 create mode 100644 src/python/bindings.hpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1ba1d988..dc9d9d95 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -286,134 +286,29 @@ set(MPI_LIBRARIES, ${MPI_CXX_LIBRARIES})
 list(GET MPI_CXX_LIBRARIES 0 MPI_LIBRARY)
 get_filename_component(MPI_DIR ${MPI_LIBRARY} DIRECTORY)
 
-# Find Ceres
+# Find NL Opt
 if(USE_PARAMS)
-    find_package(Eigen)
-    if(NOT EIGEN_FOUND)
-        set(EIGEN_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/external/eigen/build/")
-        set(EIGEN_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/external/eigen/bin/")
-        set(EIGEN_INCLUDE_DIRS "${EIGEN_INSTALL_DIR}/include/eigen3")
-
-        ExternalProject_Add(
-            external_eigen
-            PREFIX "external/eigen"
-            GIT_REPOSITORY "https://gitlab.com/libeigen/eigen.git"
-            GIT_TAG "3.3.7"
-            CMAKE_ARGS "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER};-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER};-DCMAKE_INSTALL_PREFIX=${EIGEN_INSTALL_DIR};-DINCLUDE_INSTALL_DIR=${EIGEN_INCLUDE_DIRS};"
-            BINARY_DIR "${EIGEN_BUILD_DIR}"
-            INSTALL_DIR "${EIGEN_INSTALL_DIR}"
-        )
-    endif()
-    include_directories(${EIGEN_INCLUDE_DIRS})
-
-    find_package(Gflags)
-    if(NOT GFLAGS_FOUND)
-        set(GFLAGS_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/external/gflags/bin/")
-        set(GFLAGS_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/external/gflags/bin/")
-        set(GFLAGS_INCLUDE_DIRS "${GFLAGS_INSTALL_DIR}/include")
-        set(GFLAGS_LIBRARY_DIRS "${GFLAGS_INSTALL_DIR}/lib")
-
-        ExternalProject_Add(
-            external_gflags
-            PREFIX "external/gflags"
-            GIT_REPOSITORY "https://github.com/gflags/gflags.git"
-            GIT_TAG "v2.2.2"
-            CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${GFLAGS_INSTALL_DIR};-DBUILD_SHARED_LIBS=ON;-DGFLAGS_NAMESPACE=google;-DINSTALL_HEADERS=ON"
-            BINARY_DIR "${GFLAGS_BUILD_DIR}"
-            INSTALL_DIR "${GFLAGS_INSTALL_DIR}"
-            INSTALL_COMMAND ""
-        )
-        add_library( Gflags SHARED IMPORTED )
-        set_property( TARGET Gflags PROPERTY IMPORTED_LOCATION ${GLOG_LIBRARY_DIRS}/libgflags.so )
-        set_property( TARGET Gflags PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${GLOG_INCLUDE_DIRS} )
-        add_dependencies(Gflags external_gflags)
-        set(GFLAG_LIBRARIES "${GFLAGS_LIBRARY_DIRS}/libgflags.so")
-    else()
-        list(GET GFLAG_LIBRARIES 0 GFLAGS_LIBRARY)
-        get_filename_component(GFLAGS_LIBRARY_DIRS ${GFLAGS_LIBRARY} DIRECTORY)
-    endif()
-
-    include_directories("${GFLAGS_INCLUDE_DIRS}")
-    # find_package(Glog)
-    # if(NOT GLOG_FOUND)
-    set(GLOG_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/external/glog/build/")
-    set(GLOG_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/external/glog/bin/")
-    set(GLOG_BIN_DIRS "${GLOG_INSTALL_DIR}/bin")
-    set(GLOG_INCLUDE_DIRS "${GLOG_INSTALL_DIR}/include")
-    set(GLOG_LIBRARY_DIRS "${GLOG_INSTALL_DIR}/lib")
-    set(GLOG_CMAKE_ARGS "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}")
-    list(APPEND GLOG_CMAKE_ARGS "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}")
-    list(APPEND GLOG_CMAKE_ARGS "-DWITH_GFLAGS=ON")
-    list(APPEND GLOG_CMAKE_ARGS "-DBUILD_SHARED_LIBS=ON")
-    list(APPEND GLOG_CMAKE_ARGS "-DCMAKE_INSTALL_BINDIR=${GLOG_BIN_DIRS}")
-    list(APPEND GLOG_CMAKE_ARGS "-DCMAKE_INSTALL_INCLUDEDIR=${GLOG_INCLUDE_DIRS}")
-    list(APPEND GLOG_CMAKE_ARGS "-DCMAKE_INSTALL_LIBDIR=${GLOG_LIBRARY_DIRS}")
-    list(APPEND GLOG_CMAKE_ARGS "-DWITH_UNWIND=OFF")
+    set(NLOPT_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/external/nlopt/build/")
+    set(NLOPT_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/external/nlopt/bin/")
+    set(NLOPT_INCLUDE_DIRS "${NLOPT_INSTALL_DIR}/include/")
+    set(NLOPT_LIBRARY_DIRS "${NLOPT_INSTALL_DIR}/lib/")
 
     ExternalProject_Add(
-        external_glog
-        PREFIX "external/glog"
-        GIT_REPOSITORY "https://github.com/google/glog.git"
-        GIT_TAG "v0.4.0"
-        CMAKE_ARGS "${GLOG_CMAKE_ARGS}"
-        BINARY_DIR "${GLOG_BUILD_DIR}"
-        INSTALL_DIR "${GLOG_INSTALL_DIR}"
+        external_nlopt
+        PREFIX "external/nlopt"
+        GIT_REPOSITORY "https://github.com/stevengj/nlopt.git"
+        GIT_TAG "v2.6.2"
+        CMAKE_ARGS "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER};-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER};-DCMAKE_INSTALL_PREFIX=${NLOPT_INSTALL_DIR};"
+        BINARY_DIR "${NLOPT_BUILD_DIR}"
+        INSTALL_DIR "${NLOPT_INSTALL_DIR}"
     )
-    add_dependencies(external_glog Gflags)
-    add_library( glog SHARED IMPORTED )
-    set_property( TARGET glog PROPERTY IMPORTED_LOCATION ${GLOG_LIBRARY_DIRS}/libglog.so )
-    set_property( TARGET glog PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${GLOG_INCLUDE_DIRS} )
-    add_dependencies(glog external_glog)
-    set(GLOG_LIBRARIES "${GLOG_LIBRARY_DIRS}/libglog.so")
-    # endif()
-
-    include_directories(${GLOG_INCLUDE_DIRS})
-
-    set(CERES_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/external/ceres/build/")
-    set(CERES_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/external/ceres/bin/")
-    set(CERES_INCLUDE_DIRS "${CERES_INSTALL_DIR}include")
-    set(CERES_LIBRARY_DIRS "${CERES_INSTALL_DIR}lib")
-    set(CERES_CMAKE_ARGS "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}")
-    list(APPEND CERES_CMAKE_ARGS "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}")
-    list(APPEND CERES_CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CERES_INSTALL_DIR}")
-    list(APPEND CERES_CMAKE_ARGS "-DGFLAGS=ON")
-    list(APPEND CERES_CMAKE_ARGS "-DMINIGLOG=OFF")
-    list(APPEND CERES_CMAKE_ARGS "-DBUILD_SHARED_LIBS=ON")
-    list(APPEND CERES_CMAKE_ARGS "-DSUITESPARSE=OFF")
-    list(APPEND CERES_CMAKE_ARGS "-DCXSPARSE=OFF")
-    list(APPEND CERES_CMAKE_ARGS "-DEIGENSPARSE=OFF")
-    list(APPEND CERES_CMAKE_ARGS "-DEIGEN_INCLUDE_DIR_HINTS=${EIGEN_INCLUDE_DIRS}")
-    list(APPEND CERES_CMAKE_ARGS "-DGLOG_INCLUDE_DIR_HINTS=${GLOG_INCLUDE_DIRS}")
-    list(APPEND CERES_CMAKE_ARGS "-DGLOG_LIBRARY_DIR_HINTS=${GLOG_LIBRARY_DIRS}")
-    list(APPEND CERES_CMAKE_ARGS "-DGFLAGS_INCLUDE_DIR_HINTS=${GFLAGS_INCLUDE_DIRS}")
-    list(APPEND CERES_CMAKE_ARGS "-DGFLAGS_LIBRARY_DIR_HINTS=${GFLAGS_LIBRARY_DIRS}")
-    list(APPEND CERES_CMAKE_ARGS "-DLIB_SUFFIX=''")
-    list(APPEND CERES_CMAKE_ARGS "-DBUILD_TESTING=OFF")
-    list(APPEND CERES_CMAKE_ARGS "-DBUILD_EXAMPLES=OFF")
-    ExternalProject_Add(
-        external_ceres
-        PREFIX "external/ceres"
-        GIT_REPOSITORY "https://ceres-solver.googlesource.com/ceres-solver.git"
-        GIT_TAG "1.14.0"
-        CMAKE_ARGS "${CERES_CMAKE_ARGS}"
-        BINARY_DIR "${CERES_BUILD_DIR}"
-    )
-    # if(NOT GFLAGS_FOUND)
-        add_dependencies(external_ceres Gflags)
-    # endif()
-    if(NOT GLOG_FOUND)
-        add_dependencies(external_ceres glog)
-    endif()
-    if(NOT EIGEN_FOUND)
-        add_dependencies(external_ceres external_eigen)
-    endif()
-    message(STATUS "CERES_LIBRARY_DIRS: ${CERES_LIBRARY_DIRS}")
-    add_library( ceres SHARED IMPORTED )
-    set_property( TARGET ceres PROPERTY IMPORTED_LOCATION "${CERES_LIBRARY_DIRS}/libceres.so" )
-    set_property( TARGET ceres PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CERES_INCLUDE_DIRS}" )
-    add_dependencies(ceres external_ceres)
-    set(CERES_LIBRARIES "${CERES_LIBRARY_DIRS}/libceres.so;${GLOG_LIBRARIES};${GFLAG_LIBRARIES}")
-    include_directories(${CERES_INCLUDE_DIRS})
+    include_directories(${NLOPT_INCLUDE_DIRS})
+    add_library( nlopt SHARED IMPORTED )
+    set_property(TARGET nlopt PROPERTY IMPORTED_LOCATION ${NLOPT_LIBRARY_DIRS}/libnlopt.so )
+    set_property(TARGET nlopt PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${NLOPT_INCLUDE_DIRS} )
+    ExternalProject_Add_StepDependencies(external_nlopt nlopt)
+
+    set(NLOPT_LIBRARIES "${NLOPT_LIBRARY_DIRS}/libnlopt.so")
 endif()
 
 # Coin-Clp for linear programing
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3ae9a7b0..9682a2fa 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,9 +3,9 @@ include_directories(${CMAKE_CURRENT_LIST_DIR})
 
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
 
-set(CMAKE_INSTALL_RPATH ${Boost_LIBRARY_DIRS};${LAPACK_DIR};${MPI_DIR})
+set(CMAKE_INSTALL_RPATH ${Boost_LIBRARY_DIRS};${LAPACK_DIR};${MPI_DIR};${COIN_CLP_LIBRARY_DIRS})
 if(USE_PARAMS)
-    list(APPEND CMAKE_INSTALL_RPATH ${CERES_LIBRARY_DIRS};${GLOG_LIBRARY_DIRS};${GFLAGS_LIBRARY_DIRS};${COIN_CLP_LIBRARY_DIRS})
+    list(APPEND CMAKE_INSTALL_RPATH ${NLOPT_LIBRARY_DIRS})
 endif()
 message(STATUS("CMAKE_INSTALL_RPATH = ${CMAKE_INSTALL_RPATH}"))
 
@@ -33,19 +33,14 @@ set_target_properties(sisso++
     LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
     RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
 )
-# add_dependencies(sisso++ ceres)
-message(STATUS "CERES_LIBRARIES = ${CERES_LIBRARIES}")
-message(STATUS "CERES_LIBRARY_DIRS = ${CERES_LIBRARY_DIRS}")
-target_link_libraries(sisso++  ${LAPACK_LIBRARIES} ${MPI_LIBRARIES} ${Boost_LIBRARIES} ${CERES_LIBRARIES})
-
-target_link_libraries(sisso++  ${LAPACK_LIBRARIES} ${MPI_LIBRARIES} -Wl,--rpath=${Boost_LIB_DIR} -Wl,--rpath=${LAPACK_DIR} ${Boost_LIBRARIES} ${COIN_CLP_LIBRARIES})
+target_link_libraries(sisso++  ${LAPACK_LIBRARIES} ${MPI_LIBRARIES} -Wl,--rpath=${Boost_LIB_DIR} -Wl,--rpath=${LAPACK_DIR} ${Boost_LIBRARIES} ${COIN_CLP_LIBRARIES} ${NLOPT_LIBRARIES})
 install(TARGETS sisso++ DESTINATION ${CMAKE_CURRENT_LIST_DIR}/../bin/)
 
 if(USE_PYTHON)
     include(${CMAKE_CURRENT_LIST_DIR}/../cmake/TransferDocStrings.cmake)
-    set(CMAKE_INSTALL_RPATH "${Boost_LIBRARY_DIRS};${PYTHON_PREFIX}/lib/;${MPI_DIR};${GLOG_LIBRARY_DIRS};${GFLAGS_LIBRARY_DIRS};${COIN_CLP_LIBRARY_DIRS}")
+    set(CMAKE_INSTALL_RPATH "${Boost_LIBRARY_DIRS};${PYTHON_PREFIX}/lib/;${MPI_DIR};${COIN_CLP_LIBRARY_DIRS}")
     if(USE_PARAMS)
-        list(APPEND CMAKE_INSTALL_RPATH ${CERES_LIBRARY_DIRS};${GLOG_LIBRARY_DIRS})
+        list(APPEND CMAKE_INSTALL_RPATH ${NLOPT_LIBRARY_DIRS})
     endif()
     message(STATUS "CMAKE_INSTALL_RPATH = ${CMAKE_INSTALL_RPATH}")
 
@@ -78,7 +73,7 @@ if(USE_PYTHON)
         SUFFIX ".so"
     )
 
-    target_link_libraries(_sisso ${MPI_LIBRARIES}  -Wl,--rpath=${CERES_LIBRARY_DIRS} -Wl,--rpath=${PYTHON_PREFIX}/lib/ ${LAPACK_LIBRARIES} ${PYTHON_LIBRARIES}  -Wl,--rpath=${Boost_LIB_DIR} ${Boost_LIBRARIES} ${Boost_PYTHON_LIBRARIES} ${CERES_LIBRARIES}  ${COIN_CLP_LIBRARIES})
+    target_link_libraries(_sisso ${MPI_LIBRARIES} -Wl,--rpath=${PYTHON_PREFIX}/lib/ ${LAPACK_LIBRARIES} ${PYTHON_LIBRARIES}  -Wl,--rpath=${Boost_LIB_DIR} ${Boost_LIBRARIES} ${Boost_PYTHON_LIBRARIES} ${NLOPT_LIBRARIES}  ${COIN_CLP_LIBRARIES})
     install(TARGETS _sisso DESTINATION "${PYTHON_INSTDIR}/cpp_sisso")
     install(
         DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/python/ DESTINATION ${PYTHON_INSTDIR}/cpp_sisso
diff --git a/src/descriptor_identifier/SISSO_DI/SISSORegressor.cpp b/src/descriptor_identifier/SISSO_DI/SISSORegressor.cpp
index eefbbc80..97053122 100644
--- a/src/descriptor_identifier/SISSO_DI/SISSORegressor.cpp
+++ b/src/descriptor_identifier/SISSO_DI/SISSORegressor.cpp
@@ -134,6 +134,7 @@ void SISSORegressor::l0_norm(std::vector<double>& prop, int n_dim)
     for(int rr = 0; rr < n_get_models; ++rr)
     {
         node_value_arrs::clear_temp_test_reg();
+        node_value_arrs::clear_temp_reg();
         for(int ii = 0; ii < n_dim; ++ii)
         {
             int index = all_inds_min[inds[rr] * n_dim + ii];
@@ -165,6 +166,9 @@ void SISSORegressor::fit()
     std::vector<ModelRegressor> models;
     for(int rr = 0; rr < std::max(_n_residual, _n_models_store); ++rr)
     {
+        node_value_arrs::clear_temp_test_reg();
+        node_value_arrs::clear_temp_reg();
+
         node_value_arrs::clear_temp_test_reg();
 
         model_node_ptr model_feat = std::make_shared<ModelNode>(_feat_space->phi_selected()[rr]->arr_ind(), _feat_space->phi_selected()[rr]->rung(), _feat_space->phi_selected()[rr]->expr(), _feat_space->phi_selected()[rr]->postfix_expr(), _feat_space->phi_selected()[rr]->value(), _feat_space->phi_selected()[rr]->test_value(), _feat_space->phi_selected()[rr]->domain(), _feat_space->phi_selected()[rr]->unit());
diff --git a/src/feature_creation/domain/Domain.cpp b/src/feature_creation/domain/Domain.cpp
index 38975cc8..8baac96f 100644
--- a/src/feature_creation/domain/Domain.cpp
+++ b/src/feature_creation/domain/Domain.cpp
@@ -284,11 +284,11 @@ Domain Domain::sub(Domain domain_2, double alpha, double a)
     return Domain(bnd, end_pts, new_excluded_pts);
 }
 
-Domain Domain::mult(Domain domain_2)
+Domain Domain::mult(Domain domain_2, double alpha, double a)
 {
     std::array<double, 4> test_bnd;
-    std::transform(_end_points.begin(), _end_points.end(), domain_2.end_points().begin(), test_bnd.begin(), std::multiplies<double>());
-    std::transform(_end_points.begin(), _end_points.end(), domain_2.end_points().rbegin(), test_bnd.rbegin(), std::multiplies<double>());
+    std::transform(_end_points.begin(), _end_points.end(), domain_2.end_points().begin(), test_bnd.begin(), [&alpha, &a](double d1, double d2){return d1 * (alpha * d2 + a);});
+    std::transform(_end_points.begin(), _end_points.end(), domain_2.end_points().rbegin(), test_bnd.rbegin(), [&alpha, &a](double d1, double d2){return d1 * (alpha * d2 + a);});
     std::transform(test_bnd.begin(), test_bnd.end(), test_bnd.begin(), [](double d1){return std::isnan(d1) ? 0.0 : d1;});
 
     int min_ind = std::min_element(test_bnd.begin(), test_bnd.end()) - test_bnd.begin();
diff --git a/src/feature_creation/domain/Domain.hpp b/src/feature_creation/domain/Domain.hpp
index db6d7476..c6930179 100644
--- a/src/feature_creation/domain/Domain.hpp
+++ b/src/feature_creation/domain/Domain.hpp
@@ -252,7 +252,7 @@ public:
      * @param a shift parameter
      * @return The resulting domain
      */
-    Domain add(Domain domain_2, double alpha, double a);
+    Domain add(Domain domain_2, double alpha=1.0, double a=0.0);
 
     // DocString: domain_sub
     /**
@@ -263,7 +263,7 @@ public:
      * @param a shift parameter
      * @return The resulting domain
      */
-    Domain sub(Domain domain_2, double alpha, double a);
+    Domain sub(Domain domain_2, double alpha=1.0, double a=0.0);
 
     // DocString: domain_mult
     /**
@@ -274,7 +274,7 @@ public:
      * @param a shift parameter
      * @return The resulting domain
      */
-    Domain mult(Domain domain_2);
+    Domain mult(Domain domain_2, double alpha=1.0, double a=0.0);
 
     // DocString: domain_div
     /**
@@ -285,7 +285,7 @@ public:
      * @param a shift parameter
      * @return The resulting domain
      */
-    Domain div(Domain domain_2, double alpha, double a);
+    Domain div(Domain domain_2, double alpha=1.0, double a=0.0);
 
     // DocString: domain_abs_parameters
     /**
@@ -296,18 +296,7 @@ public:
      *
      * @return The abs(alpha * domain + a) domain
      */
-    Domain abs(double alpha, double a);
-
-    // DocString: domain_abs
-    /**
-     * @brief Exponential of the domain
-     *
-     * @param alpha prefactor for the domain
-     * @param a The shift of the domain
-     *
-     * @return The abs(alpha * domain + a) domain
-     */
-    inline Domain abs(){return abs(1.0, 0.0);}
+    Domain abs(double alpha=1.0, double a=0.0);
 
     // DocString: domain_exp
     /**
@@ -318,7 +307,7 @@ public:
      *
      * @return The exp(alpha * domain + a) domain
      */
-    Domain exp(double alpha, double a);
+    Domain exp(double alpha=1.0, double a=0.0);
 
     // DocString: domain_log
     /**
@@ -329,7 +318,7 @@ public:
      *
      * @return The log(alpha * domain + a) domain
      */
-    Domain log(double alpha, double a);
+    Domain log(double alpha=1.0, double a=0.0);
 
     // DocString: domain_pow
     /**
@@ -341,7 +330,7 @@ public:
      *
      * @return The log(alpha * domain + a) domain
      */
-    Domain pow(double power, double alpha, double a);
+    Domain pow(double power, double alpha=1.0, double a=0.0);
 
     // DocString: domain_inv
     /**
@@ -352,7 +341,7 @@ public:
      *
      * @return The log(alpha * domain + a) domain
      */
-    Domain inv(double alpha, double a);
+    Domain inv(double alpha=1.0, double a=0.0);
 
     /**
      * @brief return true if domain_2 == this
diff --git a/src/feature_creation/feature_space/FeatureSpace.cpp b/src/feature_creation/feature_space/FeatureSpace.cpp
index f35144b3..a06f6238 100644
--- a/src/feature_creation/feature_space/FeatureSpace.cpp
+++ b/src/feature_creation/feature_space/FeatureSpace.cpp
@@ -19,11 +19,29 @@ BOOST_CLASS_EXPORT_GUID(InvNode, "InvNode")
 BOOST_CLASS_EXPORT_GUID(SinNode, "SinNode")
 BOOST_CLASS_EXPORT_GUID(CosNode, "CosNode")
 
+BOOST_CLASS_EXPORT_GUID(AddParamNode, "AddParamNode")
+BOOST_CLASS_EXPORT_GUID(SubParamNode, "SubParamNode")
+BOOST_CLASS_EXPORT_GUID(AbsDiffParamNode, "AbsDiffParamNode")
+BOOST_CLASS_EXPORT_GUID(MultParamNode, "MultParamNode")
+BOOST_CLASS_EXPORT_GUID(DivParamNode, "DivParamNode")
+BOOST_CLASS_EXPORT_GUID(SqParamNode, "SqParamNode")
+BOOST_CLASS_EXPORT_GUID(SqrtParamNode, "SqrtParamNode")
+BOOST_CLASS_EXPORT_GUID(CbParamNode, "CbParamNode")
+BOOST_CLASS_EXPORT_GUID(CbrtParamNode, "CbrtParamNode")
+BOOST_CLASS_EXPORT_GUID(SixPowParamNode, "SixPowParamNode")
+BOOST_CLASS_EXPORT_GUID(ExpParamNode, "ExpParamNode")
+BOOST_CLASS_EXPORT_GUID(NegExpParamNode, "NegExpParamNode")
+BOOST_CLASS_EXPORT_GUID(LogParamNode, "LogParamNode")
+BOOST_CLASS_EXPORT_GUID(AbsParamNode, "AbsParamNode")
+BOOST_CLASS_EXPORT_GUID(InvParamNode, "InvParamNode")
+BOOST_CLASS_EXPORT_GUID(SinParamNode, "SinParamNode")
+BOOST_CLASS_EXPORT_GUID(CosParamNode, "CosParamNode")
+
 FeatureSpace::FeatureSpace(
     std::shared_ptr<MPI_Interface> mpi_comm,
     std::vector<node_ptr> phi_0,
     std::vector<std::string> allowed_ops,
-    std::map<std::string, std::vector<std::string>> allowed_param_ops,
+    std::vector<std::string> allowed_param_ops,
     std::vector<double> prop,
     std::vector<int> task_sizes,
     std::string project_type,
@@ -56,14 +74,16 @@ FeatureSpace::FeatureSpace(
     _n_rung_store(max_store_rung),
     _n_rung_generate(n_rung_generate)
 {
-    initialize_fs(prop, project_type);
+    initialize_fs(project_type);
 }
 
-void FeatureSpace::initialize_fs(std::vector<double> prop, std::string project_type)
+void FeatureSpace::initialize_fs(std::string project_type)
 {
     #ifndef PARAMETERIZE
         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);
     #endif
 
     if(_n_rung_store == -1)
@@ -105,12 +125,12 @@ void FeatureSpace::initialize_fs(std::vector<double> prop, std::string project_t
     #ifdef PARAMETERIZE
         for(auto& op : _allowed_param_ops)
         {
-            if((op.first.compare("add") == 0) || (op.first.compare("sub") == 0) || (op.first.compare("mult") == 0) || (op.first.compare("abs_diff") == 0))
-                _com_bin_param_operators.push_back(std::make_pair(allowed_op_maps::binary_param_operator_map[op.first], op.second));
-            else if((op.first.compare("div") == 0))
-                _bin_param_operators.push_back(std::make_pair(allowed_op_maps::binary_param_operator_map[op.first], op.second));
+            if((op.compare("add") == 0) || (op.compare("sub") == 0) || (op.compare("mult") == 0) || (op.compare("abs_diff") == 0))
+                _com_bin_param_operators.push_back(allowed_op_maps::binary_param_operator_map[op]);
+            else if((op.compare("div") == 0))
+                _bin_param_operators.push_back(allowed_op_maps::binary_param_operator_map[op]);
             else
-                _un_param_operators.push_back(std::make_pair(allowed_op_maps::unary_param_operator_map[op.first], op.second));
+                _un_param_operators.push_back(allowed_op_maps::unary_param_operator_map[op]);
         }
     #endif
 
@@ -183,7 +203,7 @@ void FeatureSpace::generate_new_feats(std::vector<node_ptr>::iterator& feat, std
         {
             try
             {
-                feat_set.push_back(op.first(*feat, feat_ind, l_bound, u_bound, op.second, _prop));
+                feat_set.push_back(op(*feat, feat_ind, l_bound, u_bound, _prop));
                 ++feat_ind;
             }
             catch(const InvalidFeatureException& e)
@@ -197,7 +217,7 @@ void FeatureSpace::generate_new_feats(std::vector<node_ptr>::iterator& feat, std
             {
                 try
                 {
-                    feat_set.push_back(op.first(*feat, *feat_2, feat_ind, l_bound, u_bound, op.second, _prop));
+                    feat_set.push_back(op(*feat, *feat_2, feat_ind, l_bound, u_bound, _prop));
                     ++feat_ind;
                 }
                 catch(const InvalidFeatureException& e)
@@ -212,7 +232,7 @@ void FeatureSpace::generate_new_feats(std::vector<node_ptr>::iterator& feat, std
             {
                 try
                 {
-                    feat_set.push_back(op.first(*feat, *feat_2, feat_ind, l_bound, u_bound, op.second, _prop));
+                    feat_set.push_back(op(*feat, *feat_2, feat_ind, l_bound, u_bound, _prop));
                     ++feat_ind;
                 }
                 catch(const InvalidFeatureException& e)
@@ -221,7 +241,7 @@ void FeatureSpace::generate_new_feats(std::vector<node_ptr>::iterator& feat, std
                 }
                 try
                 {
-                    feat_set.push_back(op.first(*feat_2, *feat, feat_ind, l_bound, u_bound, op.second, _prop));
+                    feat_set.push_back(op(*feat_2, *feat, feat_ind, l_bound, u_bound, _prop));
                     ++feat_ind;
                 }
                 catch(const InvalidFeatureException& e)
diff --git a/src/feature_creation/feature_space/FeatureSpace.hpp b/src/feature_creation/feature_space/FeatureSpace.hpp
index a2a93fe3..2de1fde2 100644
--- a/src/feature_creation/feature_space/FeatureSpace.hpp
+++ b/src/feature_creation/feature_space/FeatureSpace.hpp
@@ -42,12 +42,12 @@ class FeatureSpace
     std::vector<node_ptr> _phi_0; //!< initial feature space
 
     #ifdef PARAMETERIZE
-        std::vector<std::pair<un_param_op_node_gen, std::vector<std::string>>> _un_param_operators; //!< list of all parameterized unary operators with free parameters
-        std::vector<std::pair<bin_param_op_node_gen, std::vector<std::string>>> _com_bin_param_operators; //!< list of all parameterized commutable binary operators with free parameters
-        std::vector<std::pair<bin_param_op_node_gen, std::vector<std::string>>> _bin_param_operators; //!< list of all parameterized binary operators with free parameters
+        std::vector<un_param_op_node_gen> _un_param_operators; //!< list of all parameterized unary operators with free parameters
+        std::vector<bin_param_op_node_gen> _com_bin_param_operators; //!< list of all parameterized commutable binary operators with free parameters
+        std::vector<bin_param_op_node_gen> _bin_param_operators; //!< list of all parameterized binary operators with free parameters
     #endif
 
-    std::map<std::string, std::vector<std::string>> _allowed_param_ops; //!< Map of parameterization operator set (set of operators and non-linear parameters used for a non-linear least squares fit to property)
+    std::vector<std::string> _allowed_param_ops; //!< Map of parameterization operator set (set of operators and non-linear parameters used for a non-linear least squares fit to property)
     std::vector<std::string> _allowed_ops; //!< list of all allowed operators strings
     std::vector<un_op_node_gen> _un_operators; //!< list of all unary operators
     std::vector<bin_op_node_gen> _com_bin_operators; //!< list of all commutable binary operators
@@ -100,7 +100,7 @@ public:
         std::shared_ptr<MPI_Interface> mpi_comm,
         std::vector<node_ptr> phi_0,
         std::vector<std::string> allowed_ops,
-        std::map<std::string, std::vector<std::string>> allowed_param_ops,
+        std::vector<std::string> allowed_param_ops,
         std::vector<double> prop,
         std::vector<int> task_sizes,
         std::string project_type="regression",
@@ -118,7 +118,7 @@ public:
      *
      * @param prop The property trying to be learned
      */
-    void initialize_fs(std::vector<double> prop, std::string project_type);
+    void initialize_fs(std::string project_type);
 
     /**
      * @brief Generate the full feature set from the allowed operators and initial feature set
@@ -292,7 +292,7 @@ public:
         FeatureSpace(
             py::list phi_0,
             py::list allowed_ops,
-            py::dict allowed_param_ops,
+            py::list allowed_param_ops,
             py::list prop,
             py::list task_sizes,
             std::string project_type="pearson",
@@ -326,7 +326,7 @@ public:
         FeatureSpace(
             py::list phi_0,
             py::list allowed_ops,
-            py::dict allowed_param_ops,
+            py::list allowed_param_ops,
             np::ndarray prop,
             py::list task_sizes,
             std::string project_type="pearson",
diff --git a/src/feature_creation/node/FeatureNode.cpp b/src/feature_creation/node/FeatureNode.cpp
index f25ac14f..ece46f55 100644
--- a/src/feature_creation/node/FeatureNode.cpp
+++ b/src/feature_creation/node/FeatureNode.cpp
@@ -12,7 +12,10 @@ FeatureNode::FeatureNode(int feat_ind, std::string expr, std::vector<double> val
     _expr(expr)
 {
     if((*std::max_element(value.begin(), value.end()) >  _domain) || (*std::min_element(value.begin(), value.end()) <  _domain))
+    {
+        std::cout << (*std::max_element(value.begin(), value.end())) << '\t' << (*std::min_element(value.begin(), value.end())) << std::endl;
         throw std::logic_error("The feature " + expr + " has values outside of the domain of " + _domain.toString());
+    }
 
     if(set_val)
     {
diff --git a/src/feature_creation/node/FeatureNode.hpp b/src/feature_creation/node/FeatureNode.hpp
index a5100a14..26082b09 100644
--- a/src/feature_creation/node/FeatureNode.hpp
+++ b/src/feature_creation/node/FeatureNode.hpp
@@ -210,7 +210,6 @@ public:
      */
     inline double* test_value_ptr(int offset = 0){return node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset);}
 
-
     // DocString: feat_node_rung
     /**
      * @brief return the rung of the feature
@@ -296,12 +295,68 @@ public:
         /**
          * @brief The parameters used for introducing more non linearity in the operators
          */
-        inline std::array<double, 2> parameters(){return {1.0, 0.0};}
+        inline std::vector<double> parameters(){return {};};
 
         /**
          * @brief Set the non-linear parameters
+        */
+        inline void set_parameters(std::vector<double> params){};
+
+        /**
+         * @brief returns the number of parameters for this feature
+         * @return the number of parameters (_params.size())
+         */
+        inline int n_params(int n_cur=0){return n_cur;};
+
+        /**
+         * @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
+         * @param params pointer to the parameter values
+         */
+        inline void set_value(const double* params, int offset = -1){set_value(offset);};
+
+        /**
+         * @brief The pointer to where the feature's training data is stored
+         *
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         * @returns the pointer to the feature's data
+         */
+        inline double* value_ptr(const double* params, int offset = -1){return value_ptr(offset);};
+
+        /**
+         * @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
+         * @param params pointer to the parameter values
+         */
+        inline void set_test_value(const double* params, int offset = -1){set_test_value(offset);};
+
+        /**
+         * @brief The pointer to where the feature's test data is stored
+         *
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         * @returns the pointer to the feature's data
+         */
+        inline double* test_value_ptr(const double* params, int offset = -1){return test_value_ptr(offset);};
+
+        /**
+         * @brief The domain for the feature (min/max values)
+         *
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        Domain domain(double* params){return _domain;};
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        inline void set_parameters(std::array<double, 2>){};
+        std::string expr(double* params){return _expr;};
     #endif
 };
 
diff --git a/src/feature_creation/node/Node.hpp b/src/feature_creation/node/Node.hpp
index 3be05b9c..a25190b6 100644
--- a/src/feature_creation/node/Node.hpp
+++ b/src/feature_creation/node/Node.hpp
@@ -327,12 +327,69 @@ public:
         /**
          * @brief The parameters used for introducing more non linearity in the operators
          */
-        virtual std::array<double, 2> parameters() = 0;
+        virtual std::vector<double> parameters() = 0;
 
         /**
          * @brief Set the non-linear parameters
         */
-        virtual void set_parameters(std::array<double, 2>) = 0;
+        virtual void set_parameters(std::vector<double>) = 0;
+
+        /**
+         * @brief returns the number of parameters for this feature
+         * @return the number of parameters (_params.size())
+         */
+        virtual int n_params(int n_cur = 0) = 0;
+
+        /**
+         * @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
+         * @param params pointer to the parameter values
+         */
+        virtual void set_value(const double* params, int offset = -1) = 0;
+
+        /**
+         * @brief The pointer to where the feature's training data is stored
+         *
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         * @returns the pointer to the feature's data
+         */
+        virtual double* value_ptr(const double* params, int offset = -1) = 0;
+
+        /**
+         * @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
+         * @param params pointer to the parameter values
+         */
+        virtual void set_test_value(const double* params, int offset = -1) = 0;
+
+        /**
+         * @brief The pointer to where the feature's test data is stored
+         *
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         * @returns the pointer to the feature's data
+         */
+        virtual double* test_value_ptr(const double* params, int offset = -1) = 0;
+
+        /**
+         * @brief The domain for the feature (min/max values)
+         *
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        virtual Domain domain(double* params) = 0;
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
+         */
+        virtual std::string expr(double* params) = 0;
+
     #endif
 
     //DocString: node_nfeats
@@ -373,7 +430,7 @@ public:
              * @brief The parameters used for non-linear operator nodes
              * @return The operator node parameters as a list [alpha, a]
              */
-            inline py::list parameters_py(){return python_conv_utils::to_list<double, 2>(parameters());}
+            inline py::list parameters_py(){return python_conv_utils::to_list<double>(parameters());}
         #endif
 
         // DocString: node_primary_feature_decomp
diff --git a/src/feature_creation/node/operator_nodes/OperatorNode.hpp b/src/feature_creation/node/operator_nodes/OperatorNode.hpp
index ea1fd9aa..d43b7201 100644
--- a/src/feature_creation/node/operator_nodes/OperatorNode.hpp
+++ b/src/feature_creation/node/operator_nodes/OperatorNode.hpp
@@ -22,17 +22,17 @@
 
 #include<iomanip>
 
-#ifdef PARAMETERIZE
-    #include "ceres/ceres.h"
-    #include "glog/logging.h"
-
-    using ceres::AutoDiffCostFunction;
-    using ceres::CauchyLoss;
-    using ceres::CostFunction;
-    using ceres::Problem;
-    using ceres::Solver;
-    using ceres::Solve;
-#endif
+// #ifdef PARAMETERIZE
+//     #include "ceres/ceres.h"
+//     #include "glog/logging.h"
+
+//     using ceres::AutoDiffCostFunction;
+//     using ceres::CauchyLoss;
+//     using ceres::CostFunction;
+//     using ceres::Problem;
+//     using ceres::Solver;
+//     using ceres::Solve;
+// #endif
 
 #ifdef PY_BINDINGS
     #include <python/conversion_utils.hpp>
@@ -63,11 +63,9 @@ class OperatorNode: public Node
     {
         ar & boost::serialization::base_object<Node>(*this);
         ar & _feats;
-        ar & _params;
     }
 protected:
     std::array<node_ptr, N> _feats; //!< The features for the operator nodes to act on (edges of the graph)
-    std::array<double, 2> _params; //!< The features for the operator nodes to act on (edges of the graph)
 
 public:
     /**
@@ -86,8 +84,7 @@ public:
      */
     OperatorNode(std::array<node_ptr, N> feats, int feat_ind) :
         Node(feat_ind, feats[0]->n_samp(), feats[0]->n_test_samp()),
-        _feats(feats),
-        _params({1.0, 0.0})
+        _feats(feats)
     {}
 
     /**
@@ -272,23 +269,15 @@ public:
      * @param cur_expr The current expression
      * @return The current postfix expression of the feature
      */
-    inline void update_postfix(std::string& cur_expr)
+    virtual void update_postfix(std::string& cur_expr)
     {
         std::stringstream postfix;
         postfix << get_postfix_term() << ":";
-        if((_params[0] == 1.0) && (_params[1] == 0.0))
-        {
-            postfix << "1.0,0.0";
-        }
-        else
-        {
-            postfix << std::setprecision(13) << std::scientific << _params[0] << ",";
-            postfix << std::setprecision(13) << std::scientific << _params[1];
-        }
+        postfix << "1.0,0.0";
         cur_expr = postfix.str() + "|" + cur_expr;
         for(int nn = N - 1; nn >= 0; --nn)
             _feats[nn]->update_postfix(cur_expr);
-    };
+    }
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -338,27 +327,98 @@ public:
         /**
          * @brief The parameters used for introducing more non linearity in the operators
          */
-        inline std::array<double, 2> parameters(){return _params;}
+        virtual std::vector<double> parameters() = 0;
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         *
+         * @param prop property to fit to get the parameters
+         */
+        virtual void get_parameters(std::vector<double>& prop) = 0;
 
         /**
          * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>) = 0;
+
+        /**
+         * @brief returns the number of theoretical parameters for this feature
+         * @return the number of theoretical parameters
          */
-        inline void set_parameters(std::array<double, 2> params){_params = params;}
+        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 Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief Set the values of the training data for the feature inside of the value storage arrays
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        virtual void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list) = 0;
+        virtual void set_value(const double* params, int offset = -1) = 0;
 
-        // DocString: op_node_reset_params
         /**
-         * @brief Reset the parameters to initial values
+         * @brief The pointer to where the feature's training data is stored
+         *
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         * @returns the pointer to the feature's data
          */
-        inline void reset_parameters(){_params = {1.0, 0.0};}
+        double* value_ptr(const double* params, int offset = -1)
+        {
+            if(_selected)
+                return node_value_arrs::get_d_matrix_ptr(_d_mat_ind);
+
+            offset = (offset == -1) ? rung() : offset;
+            set_value(params, offset);
+
+            return node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false);
+        }
+
+        /**
+         * @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
+         * @param params pointer to the parameter values
+         */
+        virtual void set_test_value(const double* params, int offset = -1) = 0;
+
+        /**
+         * @brief The pointer to where the feature's test data is stored
+         *
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         * @returns the pointer to the feature's data
+         */
+        double* test_value_ptr(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            set_test_value(params, offset);
+
+            return node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false);
+        }
+
+        /**
+         * @brief Converts a feature into a postfix expression (reverse polish notation)
+         *
+         * @details Recursively creates a postfix representation of the string
+         *
+         * @param cur_expr The current expression
+         * @return The current postfix expression of the feature
+         */
+        void update_postfix(std::string& cur_expr, double* params)
+        {
+            std::stringstream postfix;
+            postfix << get_postfix_term() << ":";
+            postfix << std::setprecision(13) << std::scientific << params[0] << ",";
+            postfix << std::setprecision(13) << std::scientific << params[1];
+            cur_expr = postfix.str() + "|" + cur_expr;
+            int n_used = 2;
+            for(int nn = N - 1; nn >= 0; --nn)
+            {
+                _feats[nn]->update_postfix(cur_expr, params + n_used);
+                n_used += _feats[nn]->n_params();
+            }
+        }
 
         #ifdef PY_BINDINGS
             // DocString: op_node_param_arr
@@ -369,11 +429,10 @@ public:
              * @param prop property to fit to get the parameters
              * @param param_list List describing the parameters to fit
              */
-            inline void get_parameters(np::ndarray prop, py::list param_list)
+            inline void get_parameters(np::ndarray prop)
             {
                 std::vector<double> prop_vec = python_conv_utils::from_ndarray<double>(prop);
-                std::vector<std::string> param_vec = python_conv_utils::from_list<std::string>(param_list);
-                get_parameters(prop_vec, param_vec);
+                get_parameters(prop_vec);
             }
 
             // DocString: op_node_param_list
@@ -384,41 +443,24 @@ public:
              * @param prop property to fit to get the parameters
              * @param param_list List describing the parameters to fit
              */
-            inline void get_parameters(py::list prop, py::list param_list)
+            inline void get_parameters(py::list prop)
             {
                 std::vector<double> prop_vec = python_conv_utils::from_list<double>(prop);
-                std::vector<std::string> param_vec = python_conv_utils::from_list<std::string>(param_list);
-                get_parameters(prop_vec, param_vec);
+                get_parameters(prop_vec);
             }
 
             // DocString: op_node_set_param_list
             /**
              * @brief Set the non-linear parameters
              */
-            inline void set_parameters(py::list params){_params = python_conv_utils::arr_from_list<double, 2>(params);}
+            inline void set_parameters(py::list params){set_parameters(python_conv_utils::from_list<double>(params));}
 
             // DocString: op_node_set_param_arr
             /**
              * @brief Set the non-linear parameters
              */
-            inline void set_parameters(np::ndarray params){_params = python_conv_utils::arr_from_ndarray<double, 2>(params);}
-
+            inline void set_parameters(np::ndarray params){set_parameters( python_conv_utils::from_ndarray<double>(params));}
         #endif
-        /**
-         * @brief Find the parameters for the operation
-         *
-         * @param problem A Problem that ceres can solve
-         */
-        void parameterize(Problem& problem)
-        {
-            Solver::Options options;
-            options.max_num_iterations = 50;
-            options.linear_solver_type = ceres::DENSE_QR;
-            options.minimizer_progress_to_stdout = false;
-            options.logging_type = ceres::SILENT;
-            Solver::Summary summary;
-            Solve(options, &problem, &summary);
-        }
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/absolute_value.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/absolute_value.cpp
index e201cd70..b269acc8 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/absolute_value.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/absolute_value.cpp
@@ -1,5 +1,7 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs/absolute_value.hpp>
 
+BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbsNode)
+
 // BOOST_CLASS_EXPORT(AbsNode)
 
 AbsNode::AbsNode()
@@ -29,6 +31,16 @@ AbsNode::AbsNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
     set_test_value();
 }
 
+AbsNode::AbsNode(std::array<node_ptr, 1> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    if((feats[0]->type() == NODE_TYPE::ABS) || (feats[0]->type() == NODE_TYPE::ABS_DIFF))
+        throw InvalidFeatureException();
+
+    set_value();
+    set_test_value();
+}
+
 void AbsNode::update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot)
 {
     std::string key = expr();
@@ -50,3 +62,33 @@ void AbsNode::update_div_mult_leaves(std::map<std::string, double>& div_mult_lea
 
     expected_abs_tot += std::abs(fact);
 }
+
+void AbsNode::set_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+
+    if(_selected)
+        allowed_op_funcs::abs(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+
+    allowed_op_funcs::abs(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
+}
+
+void AbsNode::set_test_value(int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::abs(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
+}
+
+void AbsNode::set_value(const double* params, int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::abs(_n_samp, _feats[0]->value_ptr(params + 2, 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(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+void AbsNode::set_test_value(const double* params, int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::abs(_n_test_samp, _feats[0]->test_value_ptr(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/absolute_value.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/absolute_value.hpp
index d881d9e2..2bc4fb72 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/absolute_value.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/absolute_value.hpp
@@ -60,6 +60,15 @@ public:
      */
     AbsNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    AbsNode(std::array<node_ptr, 1> feats, int feat_ind);
+
     // DocString: abs_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -70,7 +79,7 @@ public:
     /**
      * @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]) + "|";}
+    virtual inline std::string expr(){return "|" + _feats[0]->expr() + "|";}
 
     // DocString: abs_node_set_value
     /**
@@ -78,14 +87,7 @@ public:
      *
      * @param offset(int) Key to determine which part of the temporary storage array to look into
      */
-    inline void set_value(int offset = -1)
-    {
-        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));
-
-        offset = (offset == -1) ? rung() : offset;
-        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));
-    }
+    virtual void set_value(int offset = -1);
 
     // DocString: abs_node_set_test_value
     /**
@@ -93,11 +95,7 @@ public:
      *
      * @param offset(int) Key to determine which part of the temporary storage array to look into
      */
-    inline void set_test_value(int offset = -1)
-    {
-        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));
-    }
+    virtual void set_test_value(int offset = -1);
 
     // DocString: abs_node_rung
     /**
@@ -116,7 +114,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().abs();}
+    virtual inline Domain domain(){return _feats[0]->domain().abs(1.0, 0.0);}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -145,39 +143,55 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         *
+         * @param prop property to fit to get the parameters
+         */
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
+
+        /**
+         * @brief Set the values of the training data for the feature inside of the value storage arrays
          *
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        AbsNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        void set_value(const double* params, int offset = -1);
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        AbsNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        void set_test_value(const double* params, int offset = -1);
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief The domain for the feature (min/max values)
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        inline Domain domain(double* params){return _feats[0]->domain(params + 2).abs(params[0], params[1]);}
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "|" + std::to_string(params[0]) + "*" + _feats[0]->expr(params + 2) + " + " + std::to_string(params[1]) + "|";}
+
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize.cpp
deleted file mode 100644
index e3863479..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterize.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs/absolute_value.hpp>
-
-AbsNode::AbsNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-AbsNode::AbsNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat}, feat_ind)
-{
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-void AbsNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double b = 1.0, c = 0.0;
-    double* val = _feats[0]->value_ptr(rung() + 2);
-    if(param_list.size() > 4)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 4)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<AbsResidual_alpha_a_b_c, 1, 1, 1, 1, 1>(
-                    new AbsResidual_alpha_a_b_c(val[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &b, &c
-            );
-        }
-    }
-    else if(param_list.size() == 3)
-    {
-        if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsResidual_a_b_c, 1, 1, 1, 1>(
-                        new AbsResidual_a_b_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &b, &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsResidual_alpha_b_c, 1, 1, 1, 1>(
-                        new AbsResidual_alpha_b_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &b, &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsResidual_alpha_a_c, 1, 1, 1, 1>(
-                        new AbsResidual_alpha_a_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1], &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsResidual_alpha_a_b, 1, 1, 1, 1>(
-                        new AbsResidual_alpha_a_b(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1], &b
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsResidual_alpha_a, 1, 1, 1>(
-                        new AbsResidual_alpha_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsResidual_alpha_b, 1, 1, 1>(
-                        new AbsResidual_alpha_b(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &b
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsResidual_alpha_c, 1, 1, 1>(
-                        new AbsResidual_alpha_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &c
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsResidual_a_b, 1, 1, 1>(
-                        new AbsResidual_a_b(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &b
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsResidual_a_c, 1, 1, 1>(
-                        new AbsResidual_a_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsResidual_alpha, 1, 1>(
-                        new AbsResidual_alpha(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0]
-                );
-            }
-        }
-        else if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsResidual_a, 1, 1>(
-                        new AbsResidual_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..59a02643
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.cpp
@@ -0,0 +1,65 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.hpp>
+
+AbsParamNode::AbsParamNode()
+{}
+
+AbsParamNode::AbsParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    AbsNode(feats, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+AbsParamNode::AbsParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    AbsNode({feat}, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+AbsParamNode::AbsParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound) :
+    AbsNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+AbsParamNode::AbsParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    AbsNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void AbsParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..97d5b7a3
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_value.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/abs/parameterized_absolute_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_ABS_VAL_NODE
+#define PARAM_ABS_VAL_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs/absolute_value.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class AbsParamNode: public AbsNode
+{
+    using AbsNode::set_value;
+    using AbsNode::set_test_value;
+    using AbsNode::domain;
+    using AbsNode::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<AbsNode>(*this);
+        ar & _params;
+    }
+
+protected:
+    std::vector<double> _params; //!< The parameters vector
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    AbsParamNode();
+
+    /**
+     * @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
+     */
+    AbsParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    AbsParamNode(std::array<node_ptr, 1> feats, 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 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
+     */
+    AbsParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    AbsParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: abs_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: abs_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: abs_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: abs_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/absolute_difference.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/absolute_difference.cpp
index d58cc832..73ad696e 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/absolute_difference.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/absolute_difference.cpp
@@ -1,7 +1,5 @@
 #include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/absolute_difference.hpp>
 
-// BOOST_CLASS_EXPORT(AbsDiffNode)
-
 AbsDiffNode::AbsDiffNode()
 {}
 
@@ -29,6 +27,15 @@ AbsDiffNode::AbsDiffNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double
     set_test_value();
 }
 
+AbsDiffNode::AbsDiffNode(std::array<node_ptr, 2> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    check_feats();
+
+    set_value();
+    set_test_value();
+}
+
 void AbsDiffNode::check_feats()
 {
     if(_feats[0]->unit() != _feats[1]->unit())
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/absolute_difference.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/absolute_difference.hpp
index 8b904b3f..0208c433 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/absolute_difference.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/absolute_difference.hpp
@@ -62,6 +62,15 @@ public:
      */
     AbsDiffNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    AbsDiffNode(std::array<node_ptr, 2> feats, int feat_ind);
+
     // DocString: abs_diff_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -72,7 +81,7 @@ public:
     /**
      * @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]) + ")|";}
+    inline std::string expr(){return "|" + _feats[0]->expr() + " - (" + _feats[1]->expr() + ")|";}
 
     // DocString: abs_diff_node_set_value
     /**
@@ -82,11 +91,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::abs_diff(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), 1.0, 0.0, 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), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: abs_diff_node_set_test_value
@@ -98,7 +107,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::abs_diff(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _feats[1]->test_value_ptr(offset + 1), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: abs_diff_node_rung
@@ -118,7 +127,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().sub(_feats[1]->domain(), _params[0], _params[1]).abs();}
+    Domain domain(){return _feats[0]->domain().sub(_feats[1]->domain(), 1.0, 0.0).abs();}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -153,39 +162,66 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         *
+         * @param prop property to fit to get the parameters
+         */
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
+
+        /**
+         * @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
+         * @param params pointer to the parameter values
+         */
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+
+            if(_selected)
+                allowed_op_funcs::abs_diff(_n_samp, _feats[0]->value_ptr(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->value_ptr(params + 2, 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(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->value_ptr(params + 2, offset + 1), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
+        }
+
+        /**
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        AbsDiffNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::abs_diff(_n_test_samp, _feats[0]->test_value_ptr(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->test_value_ptr(params + 2, offset + 1), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
+        }
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief The domain for the feature (min/max values)
          *
-         * @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
+         * @param params parameter values for non-linear operations
+         * @return the domain
          */
-        AbsDiffNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline Domain domain(double* params){return _feats[0]->domain(params + _feats[1]->n_params() + 2).sub(_feats[1]->domain(params + 2), params[0], params[1]).abs();}
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief The expression of the feature
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "|(" + _feats[0]->expr(params + _feats[1]->n_params() + 2) + ") - (" + std::to_string(params[0]) + "*" + _feats[1]->expr(params + 2) + " + " + std::to_string(params[1]) + ")|";}
+
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize.cpp
deleted file mode 100644
index 90bb4d1b..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterize.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/absolute_difference.hpp>
-
-AbsDiffNode::AbsDiffNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop):
-    OperatorNode(feats, feat_ind)
-{
-    check_feats();
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-AbsDiffNode::AbsDiffNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop):
-    OperatorNode({feat_1, feat_2}, feat_ind)
-{
-    check_feats();
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-void AbsDiffNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double b = 1.0, c = 0.0;
-    double* val_1 = _feats[0]->value_ptr(rung() + 2);
-    double* val_2 = _feats[1]->value_ptr(rung() + 1);
-
-    if(param_list.size() > 4)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 4)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<AbsDiffResidual_alpha_a_b_c, 1, 1, 1, 1, 1>(
-                    new AbsDiffResidual_alpha_a_b_c(val_1[ss], val_2[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &b, &c
-            );
-        }
-    }
-    else if(param_list.size() == 3)
-    {
-        if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsDiffResidual_a_b_c, 1, 1, 1, 1>(
-                        new AbsDiffResidual_a_b_c(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &b, &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsDiffResidual_alpha_b_c, 1, 1, 1, 1>(
-                        new AbsDiffResidual_alpha_b_c(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &b, &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsDiffResidual_alpha_a_c, 1, 1, 1, 1>(
-                        new AbsDiffResidual_alpha_a_c(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1], &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsDiffResidual_alpha_a_b, 1, 1, 1, 1>(
-                        new AbsDiffResidual_alpha_a_b(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1], &b
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsDiffResidual_alpha_a, 1, 1, 1>(
-                        new AbsDiffResidual_alpha_a(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsDiffResidual_alpha_b, 1, 1, 1>(
-                        new AbsDiffResidual_alpha_b(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &b
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsDiffResidual_alpha_c, 1, 1, 1>(
-                        new AbsDiffResidual_alpha_c(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &c
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsDiffResidual_a_b, 1, 1, 1>(
-                        new AbsDiffResidual_a_b(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &b
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsDiffResidual_a_c, 1, 1, 1>(
-                        new AbsDiffResidual_a_c(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsDiffResidual_alpha, 1, 1>(
-                        new AbsDiffResidual_alpha(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0]
-                );
-            }
-        }
-        else if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AbsDiffResidual_a, 1, 1>(
-                        new AbsDiffResidual_a(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..4d16edf3
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.cpp
@@ -0,0 +1,65 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.hpp>
+
+AbsDiffParamNode::AbsDiffParamNode()
+{}
+
+AbsDiffParamNode::AbsDiffParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop):
+    AbsDiffNode(feats, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+AbsDiffParamNode::AbsDiffParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop):
+    AbsDiffNode({feat_1, feat_2}, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+AbsDiffParamNode::AbsDiffParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound):
+    AbsDiffNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+AbsDiffParamNode::AbsDiffParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound):
+    AbsDiffNode({feat_1, feat_2}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void AbsDiffParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 00000000..30b3c919
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_difference.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/parameterized_absolute_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_ABS_DIFF_NODE
+#define PARAM_ABS_DIFF_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/abs_diff/absolute_difference.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class AbsDiffParamNode: public AbsDiffNode
+{
+    using AbsDiffNode::set_value;
+    using AbsDiffNode::set_test_value;
+    using AbsDiffNode::domain;
+    using AbsDiffNode::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<AbsDiffNode>(*this);
+        ar & _params;
+    }
+protected:
+    std::vector<double> _params;
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    AbsDiffParamNode();
+
+    /**
+     * @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
+     */
+    AbsDiffParamNode(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 Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    AbsDiffParamNode(std::array<node_ptr, 2> feats, 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 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
+     */
+    AbsDiffParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    AbsDiffParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: abs_diff_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: abs_diff_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: abs_diff_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: abs_diff_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/add.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/add.cpp
index d6b04aeb..242ba188 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/add.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/add.cpp
@@ -27,6 +27,15 @@ AddNode::AddNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound,
     set_test_value();
 }
 
+AddNode::AddNode(std::array<node_ptr, 2> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    check_feats();
+
+    set_value();
+    set_test_value();
+}
+
 void AddNode::check_feats()
 {
     if(_feats[0]->unit() != _feats[1]->unit())
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/add.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/add.hpp
index 25dab841..f3375b37 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/add.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/add.hpp
@@ -61,6 +61,15 @@ public:
      */
     AddNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    AddNode(std::array<node_ptr, 2> feats, int feat_ind);
+
     // DocString: add_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -71,7 +80,7 @@ public:
     /**
      * @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]) + ")";}
+    inline std::string expr(){return "[" + _feats[0]->expr() + " + " + _feats[1]->expr() + "]";}
 
     // DocString: add_node_set_value
     /**
@@ -81,11 +90,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::add(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), 1.0, 0.0, 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), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: add_node_set_test_value
@@ -97,7 +106,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::add(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _feats[1]->test_value_ptr(offset + 1), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: add_node_rung
@@ -117,7 +126,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().add(_feats[1]->domain(), _params[0], _params[1]);}
+    Domain domain(){return _feats[0]->domain().add(_feats[1]->domain(), 1.0, 0.0);}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -152,39 +161,65 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param prop property to fit to get the parameters
          */
-        AddNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief Set the values of the training data for the feature inside of the value storage arrays
          *
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        AddNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+
+            if(_selected)
+                allowed_op_funcs::add(_n_samp, _feats[0]->value_ptr(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->value_ptr(params + 2, 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(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->value_ptr(params + 2, offset + 1), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
+        }
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         */
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::add(_n_test_samp, _feats[0]->test_value_ptr(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->test_value_ptr(params + 2, offset + 1), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
+        }
+
+        /**
+         * @brief The domain for the feature (min/max values)
+         *
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        inline Domain domain(double* params){return _feats[0]->domain(params + 2 + _feats[1]->n_params()).add(_feats[1]->domain(params + 2), params[0], params[1]);}
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "[" + _feats[0]->expr(params + _feats[1]->n_params() + 2) + " + (" + std::to_string(params[0]) + "*" + _feats[1]->expr(params + 2) + " + " + std::to_string(params[1]) + ")]";}
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize.cpp
deleted file mode 100644
index 9a135f52..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterize.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/add/add.hpp>
-
-AddNode::AddNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    check_feats();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-AddNode::AddNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat_1, feat_2}, feat_ind)
-{
-    check_feats();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-void AddNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double b = 0.0, c = 0.0;
-    double* val_1 = _feats[0]->value_ptr(rung() + 2);
-    double* val_2 = _feats[1]->value_ptr(rung() + 1);
-
-    if(param_list.size() > 3)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 3)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<AddResidual_alpha_b_c, 1, 1, 1, 1>(
-                    new AddResidual_alpha_b_c(val_1[ss], val_2[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &b,  &c
-            );
-        }
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AddResidual_alpha_b, 1, 1, 1>(
-                        new AddResidual_alpha_b(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &b
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AddResidual_alpha_c, 1, 1, 1>(
-                        new AddResidual_alpha_c(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<AddResidual_alpha, 1, 1>(
-                        new AddResidual_alpha(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..c38656ce
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.cpp
@@ -0,0 +1,65 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.hpp>
+
+AddParamNode::AddParamNode()
+{}
+
+AddParamNode::AddParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    AddNode(feats, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+AddParamNode::AddParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    AddNode({feat_1, feat_2}, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+AddParamNode::AddParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound) :
+    AddNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+AddParamNode::AddParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound) :
+    AddNode({feat_1, feat_2}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void AddParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..8392f49a
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.hpp
@@ -0,0 +1,151 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/add/parameterized_add.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_ADD_NODE
+#define PARAM_ADD_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/add/add.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_param_add_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class AddParamNode: public AddNode
+{
+    using AddNode::set_value;
+    using AddNode::set_test_value;
+    using AddNode::domain;
+    using AddNode::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<AddNode>(*this);
+        ar & _params;
+    }
+protected:
+    std::vector<double> _params;
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    AddParamNode();
+
+    /**
+     * @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
+     */
+    AddParamNode(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 Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    AddParamNode(std::array<node_ptr, 2> feats, 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 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
+     */
+    AddParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    AddParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: add_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: add_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: add_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: add_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/cube.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/cube.cpp
index aa5b30ed..7bce9b46 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/cube.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/cube.cpp
@@ -29,6 +29,16 @@ CbNode::CbNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
     set_test_value();
 }
 
+CbNode::CbNode(std::array<node_ptr, 1> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    if((feats[0]->type() == NODE_TYPE::CBRT) || (feats[0]->type() == NODE_TYPE::SQ) || (feats[0]->type() == NODE_TYPE::INV))
+        throw InvalidFeatureException();
+
+    set_value();
+    set_test_value();
+}
+
 void CbNode::update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot)
 {
     std::string key = expr();
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/cube.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/cube.hpp
index f1124a88..81a1aef8 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/cube.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/cube.hpp
@@ -60,6 +60,15 @@ public:
      */
     CbNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    CbNode(std::array<node_ptr, 1> feats, int feat_ind);
+
     // DocString: cb_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -70,7 +79,7 @@ public:
     /**
      * @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";}
+    inline std::string expr(){return "(" + _feats[0]->expr() + ")^3";}
 
     // DocString: cb_node_set_value
     /**
@@ -80,11 +89,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::cb(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+        allowed_op_funcs::cb(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: cb_node_set_test_value
@@ -96,7 +105,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::cb(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: cb_node_rung
@@ -116,7 +125,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().pow(3.0, _params[0], _params[1]);}
+    Domain domain(){return _feats[0]->domain().pow(3.0, 1.0, 0.0);}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -145,39 +154,66 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         *
+         * @param prop property to fit to get the parameters
+         */
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
+
+        /**
+         * @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
+         * @param params pointer to the parameter values
+         */
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            if(_selected)
+                allowed_op_funcs::cb(_n_samp, _feats[0]->value_ptr(params + 2, 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(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
+
+        /**
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        CbNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::cb(_n_test_samp, _feats[0]->test_value_ptr(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief The domain for the feature (min/max values)
          *
-         * @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
+         * @param params parameter values for non-linear operations
+         * @return the domain
          */
-        CbNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline Domain domain(double* params){return _feats[0]->domain(params + 2).pow(3.0, params[0], params[1]);}
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief The expression of the feature
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "(" + std::to_string(params[0]) + "*" + _feats[0]->expr(params + 2) + " + " + std::to_string(params[1]) + ")^3";}
+
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize.cpp
deleted file mode 100644
index 738c7f53..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterize.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cb/cube.hpp>
-
-CbNode::CbNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    if((feats[0]->type() == NODE_TYPE::CBRT) || (feats[0]->type() == NODE_TYPE::SQ) || (feats[0]->type() == NODE_TYPE::INV))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-CbNode::CbNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat}, feat_ind)
-{
-    if((feat->type() == NODE_TYPE::CBRT) || (feat->type() == NODE_TYPE::SQ) || (feat->type() == NODE_TYPE::INV))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-void CbNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double c = 0.0;
-    double* val = _feats[0]->value_ptr(rung() + 2);
-    if(param_list.size() > 3)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 3)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<CbResidual_alpha_a_c, 1, 1, 1, 1>(
-                    new CbResidual_alpha_a_c(val[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &c
-            );
-        }
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CbResidual_alpha_a, 1, 1, 1>(
-                        new CbResidual_alpha_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CbResidual_a_c, 1, 1, 1>(
-                        new CbResidual_a_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CbResidual_a, 1, 1>(
-                        new CbResidual_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..31c761c5
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.cpp
@@ -0,0 +1,67 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.hpp>
+
+CbParamNode::CbParamNode()
+{}
+
+CbParamNode::CbParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    CbNode(feats, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+CbParamNode::CbParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    CbNode({feat}, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+CbParamNode::CbParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound) :
+    CbNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+CbParamNode::CbParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    CbNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void CbParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..8755debd
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/cb/parameterized_cube.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_CUBE_NODE
+#define PARAM_CUBE_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cb/cube.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class CbParamNode: public CbNode
+{
+    using CbNode::set_value;
+    using CbNode::set_test_value;
+    using CbNode::domain;
+    using CbNode::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<CbNode>(*this);
+        ar & _params;
+    }
+
+protected:
+    std::vector<double> _params; //!< The parameters vector
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    CbParamNode();
+
+    /**
+     * @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
+     */
+    CbParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    CbParamNode(std::array<node_ptr, 1> feats, 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 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
+     */
+    CbParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    CbParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: cb_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: cb_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: cb_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: cb_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/cube_root.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/cube_root.cpp
index 58644993..b0f29fde 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/cube_root.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/cube_root.cpp
@@ -35,6 +35,16 @@ CbrtNode::CbrtNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
     set_test_value();
 }
 
+CbrtNode::CbrtNode(std::array<node_ptr, 1> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    if((feats[0]->type() == NODE_TYPE::CB) || (feats[0]->type() == NODE_TYPE::SQ) || (feats[0]->type() == NODE_TYPE::SIX_POW) || (feats[0]->type() == NODE_TYPE::INV))
+        throw InvalidFeatureException();
+
+    set_value();
+    set_test_value();
+}
+
 void CbrtNode::update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot)
 {
     std::string key = expr();
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/cube_root.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/cube_root.hpp
index a6a568ee..fa660bab 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/cube_root.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/cube_root.hpp
@@ -60,6 +60,15 @@ public:
      */
     CbrtNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    CbrtNode(std::array<node_ptr, 1> feats, int feat_ind);
+
     // DocString: cbrt_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -70,7 +79,7 @@ public:
     /**
      * @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]) + ")";}
+    inline std::string expr(){return "cbrt(" + _feats[0]->expr() + ")";}
 
     // DocString: cbrt_node_set_value
     /**
@@ -80,11 +89,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::cbrt(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+        allowed_op_funcs::cbrt(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: cbrt_node_set_test_value
@@ -96,7 +105,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::cbrt(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: cbrt_node_rung
@@ -116,7 +125,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().pow(1.0 / 3.0, _params[0], _params[1]);}
+    Domain domain(){return _feats[0]->domain().pow(1.0 / 3.0, 1.0, 0.0);}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -145,39 +154,66 @@ public:
 
    #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         *
+         * @param prop property to fit to get the parameters
+         */
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
+
+        /**
+         * @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
+         * @param params pointer to the parameter values
+         */
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            if(_selected)
+                allowed_op_funcs::cbrt(_n_samp, _feats[0]->value_ptr(params + 2, 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(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
+
+        /**
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        CbrtNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::cbrt(_n_test_samp, _feats[0]->test_value_ptr(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief The domain for the feature (min/max values)
          *
-         * @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
+         * @param params parameter values for non-linear operations
+         * @return the domain
          */
-        CbrtNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline Domain domain(double* params){return _feats[0]->domain(params + 2).pow(1.0 / 3.0, params[0], params[1]);}
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief The expression of the feature
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "cbrt(" + std::to_string(params[0]) + "*" + _feats[0]->expr(params + 2) + " + " + std::to_string(params[1]) + ")";}
+
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize.cpp
deleted file mode 100644
index ef107ad2..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterize.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/cube_root.hpp>
-
-CbrtNode::CbrtNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    if((feats[0]->type() == NODE_TYPE::CB) || (feats[0]->type() == NODE_TYPE::SQ) || (feats[0]->type() == NODE_TYPE::SIX_POW) || (feats[0]->type() == NODE_TYPE::INV))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-
-    if(_params[0] * _feats[0]->domain() + _params[1] < 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();
-
-    set_test_value();
-}
-
-CbrtNode::CbrtNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat}, feat_ind)
-{
-    if((feat->type() == NODE_TYPE::CB) || (feat->type() == NODE_TYPE::SQ) || (feat->type() == NODE_TYPE::SIX_POW) || (feat->type() == NODE_TYPE::INV))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-
-    if(_params[0] * _feats[0]->domain() + _params[1] < 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();
-
-    set_test_value();
-}
-
-void CbrtNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double c = 0.0;
-    double* val = _feats[0]->value_ptr(rung() + 2);
-    if(param_list.size() > 3)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 3)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<CbrtResidual_alpha_a_c, 1, 1, 1, 1>(
-                    new CbrtResidual_alpha_a_c(val[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &c
-            );
-        }
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CbrtResidual_alpha_a, 1, 1, 1>(
-                        new CbrtResidual_alpha_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CbrtResidual_a_c, 1, 1, 1>(
-                        new CbrtResidual_a_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CbrtResidual_a, 1, 1>(
-                        new CbrtResidual_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
\ No newline at end of file
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
new file mode 100644
index 00000000..80bd717c
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.cpp
@@ -0,0 +1,75 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.hpp>
+
+CbrtParamNode::CbrtParamNode()
+{}
+
+CbrtParamNode::CbrtParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    CbrtNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+
+    if(_params[0] * _feats[0]->domain() + _params[1] <= 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();
+
+    set_test_value();
+}
+
+CbrtParamNode::CbrtParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    CbrtNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+
+    if(_params[0] * _feats[0]->domain() + _params[1] <= 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();
+
+    set_test_value();
+}
+
+CbrtParamNode::CbrtParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound) :
+    CbrtNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+CbrtParamNode::CbrtParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    CbrtNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void CbrtParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..f838256d
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_root.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/parameterized_cube_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_CBRT_NODE
+#define PARAM_CBRT_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cbrt/cube_root.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class CbrtParamNode: public CbrtNode
+{
+    using CbrtNode::set_value;
+    using CbrtNode::set_test_value;
+    using CbrtNode::domain;
+    using CbrtNode::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<CbrtNode>(*this);
+         ar & _params;
+    }
+
+protected:
+    std::vector<double> _params; //!< The parameters vector
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    CbrtParamNode();
+
+    /**
+     * @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
+     */
+    CbrtParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    CbrtParamNode(std::array<node_ptr, 1> feats, 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 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
+     */
+    CbrtParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    CbrtParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: cbrt_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: cbrt_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: cbrt_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: cbrt_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/cos.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/cos.cpp
index b60e9512..50f2afff 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/cos.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/cos.cpp
@@ -35,6 +35,16 @@ CosNode::CosNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
     set_test_value();
 }
 
+CosNode::CosNode(std::array<node_ptr, 1> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    if((feats[0]->type() == NODE_TYPE::SIN) || (feats[0]->type() == NODE_TYPE::COS))
+        throw InvalidFeatureException();
+
+    set_value();
+    set_test_value();
+}
+
 void CosNode::update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot)
 {
     std::string key = expr();
@@ -56,3 +66,19 @@ void CosNode::update_div_mult_leaves(std::map<std::string, double>& div_mult_lea
 
     expected_abs_tot += std::abs(fact);
 }
+
+
+void CosNode::set_value(const double* params, int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    if(_selected)
+        allowed_op_funcs::cos(_n_samp, _feats[0]->value_ptr(params + 2, 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(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
+
+void CosNode::set_test_value(const double* params, int offset)
+{
+    offset = (offset == -1) ? rung() : offset;
+    allowed_op_funcs::cos(_n_test_samp, _feats[0]->test_value_ptr(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+}
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 4f4c9e8f..d52fadeb 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
@@ -60,6 +60,15 @@ public:
      */
     CosNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    CosNode(std::array<node_ptr, 1> feats, int feat_ind);
+
     // DocString: cos_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -70,7 +79,7 @@ public:
     /**
      * @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]) + ")";}
+    inline std::string expr(){return "cos(" + _feats[0]->expr() + ")";}
 
     // DocString: cos_node_set_value
     /**
@@ -78,13 +87,13 @@ public:
      *
      * @param offset(int) Key to determine which part of the temporary storage array to look into
      */
-    inline void set_value(int offset = -1)
+    virtual inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::cos(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+        allowed_op_funcs::cos(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: cos_node_set_test_value
@@ -93,10 +102,10 @@ public:
      *
      * @param offset(int) Key to determine which part of the temporary storage array to look into
      */
-    inline void set_test_value(int offset = -1)
+    virtual inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::cos(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: cos_node_rung
@@ -145,40 +154,55 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         *
+         * @param prop property to fit to get the parameters
+         */
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
+
+        /**
+         * @brief Set the values of the training data for the feature inside of the value storage arrays
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        CosNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        void set_value(const double* params, int offset = -1);
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        CosNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        void set_test_value(const double* params, int offset = -1);
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief The domain for the feature (min/max values)
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        inline Domain domain(double* params) {return Domain(-1.0, 1.0);}
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params) {return "cos(" + std::to_string(params[0]) + "*" + _feats[0]->expr( params + 2) + " + " + std::to_string(params[1]) + ")";}
     #endif
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize.cpp
deleted file mode 100644
index 6137d2df..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterize.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cos/cos.hpp>
-
-CosNode::CosNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    if((feats[0]->type() == NODE_TYPE::SIN) || (feats[0]->type() == NODE_TYPE::COS))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-CosNode::CosNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat}, feat_ind)
-{
-    if((feat->type() == NODE_TYPE::SIN) || (feat->type() == NODE_TYPE::COS))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-void CosNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double b = 1.0, c = 0.0;
-    double* val = _feats[0]->value_ptr(rung() + 2);
-    if(param_list.size() > 4)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 4)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<CosResidual_alpha_a_b_c, 1, 1, 1, 1, 1>(
-                    new CosResidual_alpha_a_b_c(val[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &b, &c
-            );
-        }
-    }
-    else if(param_list.size() == 3)
-    {
-        if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CosResidual_a_b_c, 1, 1, 1, 1>(
-                        new CosResidual_a_b_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &b, &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CosResidual_alpha_b_c, 1, 1, 1, 1>(
-                        new CosResidual_alpha_b_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &b, &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CosResidual_alpha_a_c, 1, 1, 1, 1>(
-                        new CosResidual_alpha_a_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1], &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CosResidual_alpha_a_b, 1, 1, 1, 1>(
-                        new CosResidual_alpha_a_b(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1], &b
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CosResidual_alpha_a, 1, 1, 1>(
-                        new CosResidual_alpha_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CosResidual_alpha_b, 1, 1, 1>(
-                        new CosResidual_alpha_b(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &b
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CosResidual_alpha_c, 1, 1, 1>(
-                        new CosResidual_alpha_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &c
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CosResidual_a_b, 1, 1, 1>(
-                        new CosResidual_a_b(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &b
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CosResidual_a_c, 1, 1, 1>(
-                        new CosResidual_a_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CosResidual_alpha, 1, 1>(
-                        new CosResidual_alpha(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0]
-                );
-            }
-        }
-        else if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<CosResidual_a, 1, 1>(
-                        new CosResidual_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..9da2f69a
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.cpp
@@ -0,0 +1,67 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.hpp>
+
+CosParamNode::CosParamNode()
+{}
+
+CosParamNode::CosParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    CosNode(feats, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+CosParamNode::CosParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    CosNode({feat}, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+CosParamNode::CosParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound) :
+    CosNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+CosParamNode::CosParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    CosNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void CosParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..76939c9b
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/cos/parameterized_cos.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_COS_NODE
+#define PARAM_COS_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/cos/cos.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class CosParamNode: public CosNode
+{
+    using CosNode::set_value;
+    using CosNode::set_test_value;
+    using CosNode::domain;
+    using CosNode::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<CosNode>(*this);
+        ar & _params;
+    }
+
+protected:
+    std::vector<double> _params; //!< The parameters vector
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    CosParamNode();
+
+    /**
+     * @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
+     */
+    CosParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    CosParamNode(std::array<node_ptr, 1> feats, 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 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
+     */
+    CosParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    CosParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: cos_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: cos_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: cos_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: cos_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/divide.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/divide.cpp
index 5f28d97d..5bb796d4 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/divide.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/divide.cpp
@@ -33,6 +33,14 @@ DivNode::DivNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound,
     set_test_value();
 }
 
+DivNode::DivNode(std::array<node_ptr, 2> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    check_feats();
+    set_value();
+    set_test_value();
+}
+
 void DivNode::check_feats()
 {
     if((_feats[0]->type() == NODE_TYPE::INV) || (_feats[1]->type() == NODE_TYPE::INV) || (_feats[1]->type() == NODE_TYPE::DIV))
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/divide.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/divide.hpp
index 9afea84d..77015722 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/divide.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/divide.hpp
@@ -61,6 +61,15 @@ public:
      */
     DivNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    DivNode(std::array<node_ptr, 2> feats, int feat_ind);
+
     // DocString: div_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -71,7 +80,7 @@ public:
     /**
      * @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]) + ")]";}
+    inline std::string expr(){return "[(" + _feats[0]->expr() + ") / (" + _feats[1]->expr() + ")]";}
 
     // DocString: div_node_set_value
     /**
@@ -81,11 +90,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::div(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), 1.0, 0.0, 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), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: div_node_set_test_value
@@ -97,7 +106,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::div(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _feats[1]->test_value_ptr(offset + 1), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: div_node_rung
@@ -117,7 +126,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().div(_feats[1]->domain(), _params[0], _params[1]);}
+    Domain domain(){return _feats[0]->domain().div(_feats[1]->domain());}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -152,39 +161,66 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         *
+         * @param prop property to fit to get the parameters
+         */
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
+
+        /**
+         * @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
+         * @param params pointer to the parameter values
+         */
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+
+            if(_selected)
+                allowed_op_funcs::div(_n_samp, _feats[0]->value_ptr(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->value_ptr(params + 2, 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(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->value_ptr(params + 2, offset + 1), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
+        }
+
+        /**
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        DivNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::div(_n_test_samp, _feats[0]->test_value_ptr(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->test_value_ptr(params + 2, offset + 1), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
+        }
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief The domain for the feature (min/max values)
          *
-         * @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
+         * @param params parameter values for non-linear operations
+         * @return the domain
          */
-        DivNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline Domain domain(double* params){return _feats[0]->domain(params + _feats[1]->n_params() + 2).div(_feats[1]->domain(params + 2), params[0], params[1]);}
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief The expression of the feature
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "[(" + _feats[0]->expr(params + _feats[1]->n_params() + 2) + ") / (" + std::to_string(params[0]) + "*" + _feats[1]->expr(params + 2) + " + " + std::to_string(params[1]) + ")]";}
+
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize.cpp
deleted file mode 100644
index 784f613b..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterize.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/div/divide.hpp>
-
-DivNode::DivNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    check_feats();
-
-    get_parameters(prop, param_list);
-
-    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();
-
-    set_test_value();
-}
-
-DivNode::DivNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat_1, feat_2}, feat_ind)
-{
-    check_feats();
-
-    get_parameters(prop, param_list);
-
-    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();
-
-    set_test_value();
-}
-
-void DivNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double c = 0.0;
-    double* val_1 = _feats[0]->value_ptr(rung() + 2);
-    double* val_2 = _feats[1]->value_ptr(rung() + 1);
-
-    if(param_list.size() > 3)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 3)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<DivResidual_alpha_a_c, 1, 1, 1, 1>(
-                    new DivResidual_alpha_a_c(val_1[ss], val_2[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1],  &c
-            );
-        }
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<DivResidual_a_c, 1, 1, 1>(
-                        new DivResidual_a_c(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<DivResidual_alpha_a, 1, 1, 1>(
-                        new DivResidual_alpha_a(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<DivResidual_a, 1, 1>(
-                        new DivResidual_a(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..52a30a27
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.cpp
@@ -0,0 +1,74 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.hpp>
+DivParamNode::DivParamNode()
+{}
+
+DivParamNode::DivParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    DivNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+
+    if((_params[0] * _feats[0]->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();
+
+    set_test_value();
+}
+
+DivParamNode::DivParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    DivNode({feat_1, feat_2}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+
+    if((_params[0] * _feats[0]->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();
+
+    set_test_value();
+}
+
+DivParamNode::DivParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound) :
+    DivNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+DivParamNode::DivParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound) :
+    DivNode({feat_1, feat_2}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void DivParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..db7f4bbb
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.hpp
@@ -0,0 +1,151 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_divide.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_DIV_NODE
+#define PARAM_DIV_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/div/divide.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_param_add_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class DivParamNode: public DivNode
+{
+    using DivNode::set_value;
+    using DivNode::set_test_value;
+    using DivNode::domain;
+    using DivNode::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<DivNode>(*this);
+        ar & _params;
+    }
+protected:
+    std::vector<double> _params;
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    DivParamNode();
+
+    /**
+     * @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
+     */
+    DivParamNode(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 Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    DivParamNode(std::array<node_ptr, 2> feats, 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 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
+     */
+    DivParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    DivParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: div_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: div_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: div_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: div_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/exponential.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/exponential.cpp
index 77695faf..8969e83e 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/exponential.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/exponential.cpp
@@ -35,6 +35,16 @@ ExpNode::ExpNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
     set_test_value();
 }
 
+ExpNode::ExpNode(std::array<node_ptr, 1> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    if((feats[0]->type() == NODE_TYPE::NEG_EXP) || (feats[0]->type() == NODE_TYPE::EXP) || (feats[0]->type() == NODE_TYPE::ADD) || (feats[0]->type() == NODE_TYPE::SUB) || (feats[0]->type() == NODE_TYPE::LOG))
+        throw InvalidFeatureException();
+
+    set_value();
+    set_test_value();
+}
+
 void ExpNode::update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot)
 {
     std::string key = expr();
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/exponential.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/exponential.hpp
index feadc6f2..91674b01 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/exponential.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/exponential.hpp
@@ -60,6 +60,15 @@ public:
      */
     ExpNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    ExpNode(std::array<node_ptr, 1> feats, int feat_ind);
+
     // DocString: exp_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -70,7 +79,7 @@ public:
     /**
      * @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]) + ")";}
+    inline std::string expr(){return "exp(" + _feats[0]->expr() + ")";}
 
     // DocString: exp_node_set_value
     /**
@@ -80,11 +89,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::exp(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+        allowed_op_funcs::exp(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: exp_node_set_test_value
@@ -96,7 +105,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::exp(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: exp_node_rung
@@ -116,7 +125,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().exp(_params[0], _params[1]);}
+    Domain domain(){return _feats[0]->domain().exp(1.0, 0.0);}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -145,39 +154,65 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param prop property to fit to get the parameters
          */
-        ExpNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief Set the values of the training data for the feature inside of the value storage arrays
          *
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        ExpNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            if(_selected)
+                allowed_op_funcs::exp(_n_samp, _feats[0]->value_ptr(params + 2, 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(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         */
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::exp(_n_test_samp, _feats[0]->test_value_ptr(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
+
+        /**
+         * @brief The domain for the feature (min/max values)
+         *
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        inline Domain domain(double* params){return _feats[0]->domain(params + 2).exp(params[0], params[1]);}
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "exp(" + std::to_string(params[0]) + "*" + _feats[0]->expr(params + 2) + " + " + std::to_string(params[1]) + ")";}
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize.cpp
deleted file mode 100644
index 2a211717..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterize.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/exp/exponential.hpp>
-
-ExpNode::ExpNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    if((feats[0]->type() == NODE_TYPE::NEG_EXP) || (feats[0]->type() == NODE_TYPE::EXP) || (feats[0]->type() == NODE_TYPE::ADD) || (feats[0]->type() == NODE_TYPE::SUB) || (feats[0]->type() == NODE_TYPE::LOG))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-       set_test_value();
-}
-
-ExpNode::ExpNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat}, 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))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-       set_test_value();
-}
-
-void ExpNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double b = 1.0, c = 0.0;
-    double* val = _feats[0]->value_ptr(rung() + 2);
-    if(param_list.size() > 3)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 3)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<ExpResidual_alpha_a_c, 1, 1, 1, 1>(
-                    new ExpResidual_alpha_a_c(val[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &c
-            );
-        }
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<ExpResidual_alpha_a, 1, 1, 1>(
-                        new ExpResidual_alpha_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<ExpResidual_alpha_c, 1, 1, 1>(
-                        new ExpResidual_alpha_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<ExpResidual_alpha, 1, 1>(
-                        new ExpResidual_alpha(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..76e9363c
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.cpp
@@ -0,0 +1,65 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.hpp>
+
+ExpParamNode::ExpParamNode()
+{}
+
+ExpParamNode::ExpParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    ExpNode(feats, feat_ind)
+{
+    _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();
+
+       set_test_value();
+}
+
+ExpParamNode::ExpParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    ExpNode({feat}, feat_ind)
+{
+    _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();
+
+       set_test_value();
+}
+
+ExpParamNode::ExpParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound) :
+    ExpNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+ExpParamNode::ExpParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    ExpNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void ExpParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..78da2782
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponential.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/exp/parameterized_exponetial.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_EXP_NODE
+#define PARAM_EXP_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/exp/exponential.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class ExpParamNode: public ExpNode
+{
+    using ExpNode::set_value;
+    using ExpNode::set_test_value;
+    using ExpNode::domain;
+    using ExpNode::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<ExpNode>(*this);
+        ar & _params;
+    }
+
+protected:
+    std::vector<double> _params; //!< The parameters vector
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    ExpParamNode();
+
+    /**
+     * @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
+     */
+    ExpParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    ExpParamNode(std::array<node_ptr, 1> feats, 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 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
+     */
+    ExpParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    ExpParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: exp_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: exp_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: exp_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: exp_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/inverse.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/inverse.cpp
index 47a7e3be..1773bcc3 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/inverse.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/inverse.cpp
@@ -35,6 +35,16 @@ InvNode::InvNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
     set_test_value();
 }
 
+InvNode::InvNode(std::array<node_ptr, 1> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    if((feats[0]->type() == NODE_TYPE::DIV) || (feats[0]->type() == NODE_TYPE::EXP) || (feats[0]->type() == NODE_TYPE::NEG_EXP) || (feats[0]->type() == NODE_TYPE::INV))
+        throw InvalidFeatureException();
+
+    set_value();
+    set_test_value();
+}
+
 void InvNode::update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot)
 {
     std::string key = expr();
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/inverse.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/inverse.hpp
index e14095ea..64a1c50f 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/inverse.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/inverse.hpp
@@ -38,7 +38,7 @@ public:
     InvNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
 
     /**
-     * @brief Constructor
+     * @brief Constructor without checking feature values
      * @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)
@@ -48,6 +48,15 @@ public:
      */
     InvNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    InvNode(std::array<node_ptr, 1> feats, int feat_ind);
+
     // DocString: inv_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -58,7 +67,7 @@ public:
     /**
      * @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]) + ")";}
+    inline std::string expr(){return "1.0 / (" + _feats[0]->expr() + ")";}
 
     // DocString: inv_node_set_value
     /**
@@ -68,11 +77,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::inv(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+        allowed_op_funcs::inv(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: inv_node_set_test_value
@@ -84,7 +93,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::inv(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: inv_node_rung
@@ -104,7 +113,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().inv(_params[0], _params[1]);}
+    Domain domain(){return _feats[0]->domain().inv(1.0, 0.0);}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -133,39 +142,65 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param prop property to fit to get the parameters
          */
-        InvNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief Set the values of the training data for the feature inside of the value storage arrays
          *
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        InvNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            if(_selected)
+                allowed_op_funcs::inv(_n_samp, _feats[0]->value_ptr(params + 2, 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(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         */
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::inv(_n_test_samp, _feats[0]->test_value_ptr(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
+
+        /**
+         * @brief The domain for the feature (min/max values)
+         *
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        inline Domain domain(double* params){return _feats[0]->domain(params + 2).inv(params[0], params[1]);}
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "1.0 / (" + std::to_string(params[0]) + "*" + _feats[0]->expr(params + 2) + " + " + std::to_string(params[1]) + ")";}
     #endif
 };
 #endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize.cpp
deleted file mode 100644
index 2f321821..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterize.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/inv/inverse.hpp>
-
-InvNode::InvNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    if((feats[0]->type() == NODE_TYPE::DIV) || (feats[0]->type() == NODE_TYPE::EXP) || (feats[0]->type() == NODE_TYPE::NEG_EXP) || (feats[0]->type() == NODE_TYPE::INV))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-
-    if((_params[0] * _feats[0]->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();
-
-    set_test_value();
-}
-
-InvNode::InvNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat}, feat_ind)
-{
-    if((feat->type() == NODE_TYPE::DIV) || (feat->type() == NODE_TYPE::EXP) || (feat->type() == NODE_TYPE::NEG_EXP) || (feat->type() == NODE_TYPE::INV))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-
-    if((_params[0] * _feats[0]->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();
-
-    set_test_value();
-}
-
-void InvNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double c = 0.0;
-    double* val = _feats[0]->value_ptr(rung() + 2);
-    if(param_list.size() > 3)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 3)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<InvResidual_alpha_a_c, 1, 1, 1, 1>(
-                    new InvResidual_alpha_a_c(val[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &c
-            );
-        }
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<InvResidual_alpha_a, 1, 1, 1>(
-                        new InvResidual_alpha_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<InvResidual_a_c, 1, 1, 1>(
-                        new InvResidual_a_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<InvResidual_a, 1, 1>(
-                        new InvResidual_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..b394e8ed
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.cpp
@@ -0,0 +1,75 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.hpp>
+
+InvParamNode::InvParamNode()
+{}
+
+InvParamNode::InvParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    InvNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+
+    if((_params[0] * _feats[0]->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();
+
+    set_test_value();
+}
+
+InvParamNode::InvParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    InvNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+
+    if((_params[0] * _feats[0]->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();
+
+    set_test_value();
+}
+
+InvParamNode::InvParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound) :
+    InvNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+InvParamNode::InvParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    InvNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void InvParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..ffdc23b1
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/inv/parameterized_inverse.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_INV_NODE
+#define PARAM_INV_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/inv/inverse.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class InvParamNode: public InvNode
+{
+    using InvNode::set_value;
+    using InvNode::set_test_value;
+    using InvNode::domain;
+    using InvNode::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<InvNode>(*this);
+        ar & _params;
+    }
+
+protected:
+    std::vector<double> _params; //!< The parameters vector
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    InvParamNode();
+
+    /**
+     * @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
+     */
+    InvParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    InvParamNode(std::array<node_ptr, 1> feats, 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 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
+     */
+    InvParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    InvParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: inv_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: inv_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: inv_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: inv_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/log.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/log.cpp
index ea1a8b36..19cd8fe9 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/log.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/log.cpp
@@ -20,7 +20,7 @@ LogNode::LogNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, do
     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();
 
-       set_test_value();
+    set_test_value();
 }
 
 LogNode::LogNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
@@ -39,7 +39,17 @@ LogNode::LogNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
     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();
 
-       set_test_value();
+    set_test_value();
+}
+
+LogNode::LogNode(std::array<node_ptr, 1> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    if((feats[0]->type() == NODE_TYPE::NEG_EXP) || (feats[0]->type() == NODE_TYPE::EXP) || (feats[0]->type() == NODE_TYPE::DIV) || (feats[0]->type() == NODE_TYPE::INV) || (feats[0]->type() == NODE_TYPE::MULT) || (feats[0]->type() == NODE_TYPE::LOG) || (feats[0]->type() == NODE_TYPE::SIX_POW) || (feats[0]->type() == NODE_TYPE::CB) || (feats[0]->type() == NODE_TYPE::SQ) || (feats[0]->type() == NODE_TYPE::CBRT) || (feats[0]->type() == NODE_TYPE::SQRT))
+        throw InvalidFeatureException();
+
+    set_value();
+    set_test_value();
 }
 
 void LogNode::update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot)
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/log.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/log.hpp
index 3aaf42e4..e87952a0 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/log.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/log.hpp
@@ -60,6 +60,15 @@ public:
      */
     LogNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    LogNode(std::array<node_ptr, 1> feats, int feat_ind);
+
     // DocString: log_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -70,7 +79,7 @@ public:
     /**
      * @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]) + ")";}
+    inline std::string expr(){return "log(" + _feats[0]->expr() + ")";}
 
     // DocString: log_node_set_value
     /**
@@ -80,11 +89,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::log(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+        allowed_op_funcs::log(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: log_node_set_test_value
@@ -96,7 +105,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::log(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: log_node_rung
@@ -116,7 +125,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().log(_params[0], _params[1]);}
+    Domain domain(){return _feats[0]->domain().log(1.0, 0.0);}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -145,39 +154,66 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         *
+         * @param prop property to fit to get the parameters
+         */
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
+
+        /**
+         * @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
+         * @param params pointer to the parameter values
+         */
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            if(_selected)
+                allowed_op_funcs::log(_n_samp, _feats[0]->value_ptr(params + 2, 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(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
+
+        /**
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        LogNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::log(_n_test_samp, _feats[0]->test_value_ptr(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief The domain for the feature (min/max values)
          *
-         * @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
+         * @param params parameter values for non-linear operations
+         * @return the domain
          */
-        LogNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline Domain domain(double* params){return _feats[0]->domain(params + 2).log(params[0], params[1]);}
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief The expression of the feature
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "log(" + std::to_string(params[0]) + "*" + _feats[0]->expr(params + 2) + " + " + std::to_string(params[1]) + ")";}
+
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize.cpp
deleted file mode 100644
index d1c65f0f..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterize.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/log/log.hpp>
-
-LogNode::LogNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    if((feats[0]->type() == NODE_TYPE::NEG_EXP) || (feats[0]->type() == NODE_TYPE::EXP) || (feats[0]->type() == NODE_TYPE::DIV) || (feats[0]->type() == NODE_TYPE::INV) || (feats[0]->type() == NODE_TYPE::MULT) || (feats[0]->type() == NODE_TYPE::LOG) || (feats[0]->type() == NODE_TYPE::SIX_POW) || (feats[0]->type() == NODE_TYPE::CB) || (feats[0]->type() == NODE_TYPE::SQ) || (feats[0]->type() == NODE_TYPE::CBRT) || (feats[0]->type() == NODE_TYPE::SQRT))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-
-    if(_params[0] * _feats[0]->domain() + _params[1] <= 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();
-
-       set_test_value();
-}
-
-LogNode::LogNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat}, 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))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-
-    if(_params[0] * _feats[0]->domain() + _params[1] <= 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();
-
-       set_test_value();
-}
-
-void LogNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double b = 1.0, c = 0.0;
-    double* val = _feats[0]->value_ptr(rung() + 2);
-    if(param_list.size() > 3)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 3)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<LogResidual_alpha_a_b, 1, 1, 1, 1>(
-                    new LogResidual_alpha_a_b(val[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &b
-            );
-        }
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<LogResidual_alpha_a, 1, 1, 1>(
-                        new LogResidual_alpha_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<LogResidual_a_b, 1, 1, 1>(
-                        new LogResidual_a_b(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &b
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<LogResidual_a, 1, 1>(
-                        new LogResidual_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..d0438748
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.cpp
@@ -0,0 +1,75 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.hpp>
+
+LogParamNode::LogParamNode()
+{}
+
+LogParamNode::LogParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    LogNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+
+    if(_params[0] * _feats[0]->domain() + _params[1] <= 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();
+
+       set_test_value();
+}
+
+LogParamNode::LogParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    LogNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+
+    if(_params[0] * _feats[0]->domain() + _params[1] <= 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();
+
+       set_test_value();
+}
+
+LogParamNode::LogParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound) :
+    LogNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+LogParamNode::LogParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    LogNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void LogParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-6);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..e6db88e3
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/log/parameterized_log.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_LOG_NODE
+#define PARAM_LOG_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/log/log.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class LogParamNode: public LogNode
+{
+    using LogNode::set_value;
+    using LogNode::set_test_value;
+    using LogNode::domain;
+    using LogNode::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<LogNode>(*this);
+        ar & _params;
+    }
+
+protected:
+    std::vector<double> _params; //!< The parameters vector
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    LogParamNode();
+
+    /**
+     * @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
+     */
+    LogParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    LogParamNode(std::array<node_ptr, 1> feats, 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 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
+     */
+    LogParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    LogParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: log_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: log_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: log_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: log_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/multiply.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/multiply.cpp
index 9d30eee3..f0366d93 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/multiply.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/multiply.cpp
@@ -27,6 +27,15 @@ MultNode::MultNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_boun
     set_test_value();
 }
 
+MultNode::MultNode(std::array<node_ptr, 2> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    check_feats();
+
+    set_value();
+    set_test_value();
+}
+
 void MultNode::check_feats()
 {
     if((_feats[0]->type() == NODE_TYPE::INV) || (_feats[1]->type() == NODE_TYPE::INV) || ((_feats[0]->type() == NODE_TYPE::DIV) && (_feats[1]->type() == NODE_TYPE::DIV)))
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/multiply.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/multiply.hpp
index cc00e6a9..58695c87 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/multiply.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/multiply.hpp
@@ -61,6 +61,15 @@ public:
      */
     MultNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    MultNode(std::array<node_ptr, 2> feats, int feat_ind);
+
     // DocString: mult_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -71,7 +80,7 @@ public:
     /**
      * @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() + ") * (" + _feats[1]->expr() + ")]";}
+    inline std::string expr(){return "[(" + _feats[0]->expr() + ") * (" + _feats[1]->expr() + ")]";}
 
     // DocString: mult_node_set_value
     /**
@@ -81,11 +90,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::mult(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), 1.0, 0.0, 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), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: mult_node_set_test_value
@@ -97,7 +106,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::mult(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _feats[1]->test_value_ptr(offset + 1), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: mult_node_rung
@@ -152,39 +161,65 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param prop property to fit to get the parameters
          */
-        MultNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief Set the values of the training data for the feature inside of the value storage arrays
          *
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        MultNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+
+            if(_selected)
+                allowed_op_funcs::mult(_n_samp, _feats[0]->value_ptr(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->value_ptr(params + 2, 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(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->value_ptr(params + 2, offset + 1), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
+        }
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         */
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::mult(_n_test_samp, _feats[0]->test_value_ptr(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->test_value_ptr(params + 2, offset + 1), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
+        }
+
+        /**
+         * @brief The domain for the feature (min/max values)
+         *
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        inline Domain domain(double* params){return _feats[0]->domain(params + _feats[1]->n_params() + 2).mult(_feats[1]->domain(params + 2), params[0], params[1]);}
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "[(" + _feats[0]->expr(params + _feats[1]->n_params() + 2) + ") * (" + std::to_string(params[0]) + "*" + _feats[1]->expr(params + 2) + " + " + std::to_string(params[1]) + ")]";}
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize.cpp
deleted file mode 100644
index 8888ed01..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterize.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/mult/multiply.hpp>
-
-MultNode::MultNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop):
-    OperatorNode(feats, feat_ind)
-{
-    check_feats();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-MultNode::MultNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop):
-    OperatorNode({feat_1, feat_2}, feat_ind)
-{
-    check_feats();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-void MultNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    throw std::logic_error("Too many parameters passed to the constructor.");
-}
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
new file mode 100644
index 00000000..a6b52b42
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.cpp
@@ -0,0 +1,67 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.hpp>
+
+MultParamNode::MultParamNode()
+{}
+
+MultParamNode::MultParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop):
+    MultNode(feats, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+MultParamNode::MultParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop):
+    MultNode({feat_1, feat_2}, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+MultParamNode::MultParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound):
+    MultNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+MultParamNode::MultParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound):
+    MultNode({feat_1, feat_2}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void MultParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..4bcd9e9e
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/mult/parameterized_multiply.hpp
@@ -0,0 +1,151 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/div/parameterized_multiply.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_MULT_NODE
+#define PARAM_MULT_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/mult/multiply.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_param_add_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class MultParamNode: public MultNode
+{
+    using MultNode::set_value;
+    using MultNode::set_test_value;
+    using MultNode::domain;
+    using MultNode::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<MultNode>(*this);
+        ar & _params;
+    }
+protected:
+    std::vector<double> _params;
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    MultParamNode();
+
+    /**
+     * @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
+     */
+    MultParamNode(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 Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    MultParamNode(std::array<node_ptr, 2> feats, 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 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
+     */
+    MultParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    MultParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: mult_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: mult_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: mult_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: mult_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/negative_exponential.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/negative_exponential.cpp
index 4e0b6fcc..a166ef61 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/negative_exponential.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/negative_exponential.cpp
@@ -17,7 +17,7 @@ NegExpNode::NegExpNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bou
         throw InvalidFeatureException();
 
     set_test_value();
- }
+}
 
 NegExpNode::NegExpNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
     OperatorNode({feat}, feat_ind)
@@ -33,7 +33,17 @@ NegExpNode::NegExpNode(node_ptr feat, int feat_ind, double l_bound, double u_bou
         throw InvalidFeatureException();
 
     set_test_value();
- }
+}
+
+NegExpNode::NegExpNode(std::array<node_ptr, 1> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    if((feats[0]->type() == NODE_TYPE::NEG_EXP) || (feats[0]->type() == NODE_TYPE::EXP) || (feats[0]->type() == NODE_TYPE::ADD) || (feats[0]->type() == NODE_TYPE::SUB) || (feats[0]->type() == NODE_TYPE::LOG))
+        throw InvalidFeatureException();
+
+    set_value();
+    set_test_value();
+}
 
 void NegExpNode::update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot)
 {
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/negative_exponential.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/negative_exponential.hpp
index 6c03be6b..d9af14bc 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/negative_exponential.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/negative_exponential.hpp
@@ -61,6 +61,15 @@ public:
      */
     NegExpNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    NegExpNode(std::array<node_ptr, 1> feats, int feat_ind);
+
     // DocString: neg_exp_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -71,7 +80,7 @@ public:
     /**
      * @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]) + ")]";}
+    inline std::string expr(){return "exp[-1.0*(" + _feats[0]->expr() + ")]";}
 
     // DocString: neg_exp_node_set_value
     /**
@@ -81,11 +90,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::neg_exp(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+        allowed_op_funcs::neg_exp(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     };
 
     // DocString: neg_exp_node_set_test_value
@@ -97,7 +106,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::neg_exp(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     };
 
     // DocString: neg_exp_node_rung
@@ -117,7 +126,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().exp(-1.0 * _params[0], _params[1]);}
+    Domain domain(){return _feats[0]->domain().exp(-1.0, 0.0);}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -146,39 +155,66 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         *
+         * @param prop property to fit to get the parameters
+         */
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
+
+        /**
+         * @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
+         * @param params pointer to the parameter values
+         */
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            if(_selected)
+                allowed_op_funcs::neg_exp(_n_samp, _feats[0]->value_ptr(params + 2, 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(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
+
+        /**
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        NegExpNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::neg_exp(_n_test_samp, _feats[0]->test_value_ptr(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief The domain for the feature (min/max values)
          *
-         * @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
+         * @param params parameter values for non-linear operations
+         * @return the domain
          */
-        NegExpNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline Domain domain(double* params){return _feats[0]->domain(params + 2).exp(-1.0 * params[0], params[1]);}
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief The expression of the feature
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "exp[-1.0*(" + std::to_string(params[0]) + "*" + _feats[0]->expr(params + 2) + " + " + std::to_string(params[1]) + ")]";}
+
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize.cpp
deleted file mode 100644
index 5d21fe50..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterize.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/negative_exponential.hpp>
-
-NegExpNode::NegExpNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    if((feats[0]->type() == NODE_TYPE::NEG_EXP) || (feats[0]->type() == NODE_TYPE::EXP) || (feats[0]->type() == NODE_TYPE::ADD) || (feats[0]->type() == NODE_TYPE::SUB) || (feats[0]->type() == NODE_TYPE::LOG))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-       set_test_value();
-}
-
-NegExpNode::NegExpNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat}, 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))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-       set_test_value();
-}
-
-void NegExpNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double b = 1.0, c = 0.0;
-    double* val = _feats[0]->value_ptr(rung() + 2);
-    if(param_list.size() > 3)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 3)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<NegExpResidual_alpha_a_c, 1, 1, 1, 1>(
-                    new NegExpResidual_alpha_a_c(val[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &c
-            );
-        }
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<NegExpResidual_alpha_a, 1, 1, 1>(
-                        new NegExpResidual_alpha_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<NegExpResidual_alpha_c, 1, 1, 1>(
-                        new NegExpResidual_alpha_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<NegExpResidual_alpha, 1, 1>(
-                        new NegExpResidual_alpha(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..2fac9ab1
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.cpp
@@ -0,0 +1,67 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.hpp>
+
+NegExpParamNode::NegExpParamNode()
+{}
+
+NegExpParamNode::NegExpParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    NegExpNode(feats, feat_ind)
+{
+    _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();
+
+       set_test_value();
+}
+
+NegExpParamNode::NegExpParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    NegExpNode({feat}, feat_ind)
+{
+    _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();
+
+       set_test_value();
+}
+
+NegExpParamNode::NegExpParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound) :
+    NegExpNode(feats, feat_ind)
+{
+    _params.resize(n_params(),  0.0);
+}
+
+NegExpParamNode::NegExpParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    NegExpNode({feat}, feat_ind)
+{
+    _params.resize(n_params(),  0.0);
+}
+
+void NegExpParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..75127876
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_exponential.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/parameterized_negative_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_NEG_EXP_NODE
+#define PARAM_NEG_EXP_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/neg_exp/negative_exponential.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class NegExpParamNode: public NegExpNode
+{
+    using NegExpNode::set_value;
+    using NegExpNode::set_test_value;
+    using NegExpNode::domain;
+    using NegExpNode::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<NegExpNode>(*this);
+        ar & _params;
+    }
+
+protected:
+    std::vector<double> _params; //!< The parameters vector
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    NegExpParamNode();
+
+    /**
+     * @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
+     */
+    NegExpParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    NegExpParamNode(std::array<node_ptr, 1> feats, 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 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
+     */
+    NegExpParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    NegExpParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: neg_exp_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: neg_exp_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: neg_exp_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: neg_exp_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize.cpp
deleted file mode 100644
index e5730a38..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterize.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sin/sin.hpp>
-
-SinNode::SinNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    if((feats[0]->type() == NODE_TYPE::SIN) || (feats[0]->type() == NODE_TYPE::COS))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-       set_test_value();
-}
-
-SinNode::SinNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat}, feat_ind)
-{
-    if((feat->type() == NODE_TYPE::SIN) || (feat->type() == NODE_TYPE::COS))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-       set_test_value();
-}
-
-void SinNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double b = 1.0, c = 0.0;
-    double* val = _feats[0]->value_ptr(rung() + 2);
-    if(param_list.size() > 4)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 4)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<SinResidual_alpha_a_b_c, 1, 1, 1, 1, 1>(
-                    new SinResidual_alpha_a_b_c(val[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &b, &c
-            );
-        }
-    }
-    else if(param_list.size() == 3)
-    {
-        if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SinResidual_a_b_c, 1, 1, 1, 1>(
-                        new SinResidual_a_b_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &b, &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SinResidual_alpha_b_c, 1, 1, 1, 1>(
-                        new SinResidual_alpha_b_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &b, &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SinResidual_alpha_a_c, 1, 1, 1, 1>(
-                        new SinResidual_alpha_a_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1], &c
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SinResidual_alpha_a_b, 1, 1, 1, 1>(
-                        new SinResidual_alpha_a_b(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1], &b
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SinResidual_alpha_a, 1, 1, 1>(
-                        new SinResidual_alpha_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SinResidual_alpha_b, 1, 1, 1>(
-                        new SinResidual_alpha_b(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &b
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SinResidual_alpha_c, 1, 1, 1>(
-                        new SinResidual_alpha_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &c
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SinResidual_a_b, 1, 1, 1>(
-                        new SinResidual_a_b(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &b
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SinResidual_a_c, 1, 1, 1>(
-                        new SinResidual_a_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SinResidual_alpha, 1, 1>(
-                        new SinResidual_alpha(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0]
-                );
-            }
-        }
-        else if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SinResidual_a, 1, 1>(
-                        new SinResidual_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..5b844b58
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.cpp
@@ -0,0 +1,69 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.hpp>
+
+SinParamNode::SinParamNode()
+{}
+
+SinParamNode::SinParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SinNode(feats, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+SinParamNode::SinParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SinNode({feat}, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+SinParamNode::SinParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound) :
+    SinNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+SinParamNode::SinParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    SinNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void SinParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..dd09603a
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/sin/parameterized_sin.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_SIN_NODE
+#define PARAM_SIN_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sin/sin.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class SinParamNode: public SinNode
+{
+    using SinNode::set_value;
+    using SinNode::set_test_value;
+    using SinNode::domain;
+    using SinNode::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<SinNode>(*this);
+        ar & _params;
+    }
+
+protected:
+    std::vector<double> _params; //!< The parameters vector
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    SinParamNode();
+
+    /**
+     * @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
+     */
+    SinParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    SinParamNode(std::array<node_ptr, 1> feats, 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 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
+     */
+    SinParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    SinParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: sin_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: sin_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: sin_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: sin_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/sin.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/sin.cpp
index bf0690de..0bcc6170 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/sin.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/sin.cpp
@@ -17,7 +17,7 @@ SinNode::SinNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, do
         throw InvalidFeatureException();
 
     set_test_value();
- }
+}
 
 SinNode::SinNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
     OperatorNode({feat}, feat_ind)
@@ -34,7 +34,17 @@ SinNode::SinNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
         throw InvalidFeatureException();
 
     set_test_value();
- }
+}
+
+SinNode::SinNode(std::array<node_ptr, 1> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    if((feats[0]->type() == NODE_TYPE::SIN) || (feats[0]->type() == NODE_TYPE::COS))
+        throw InvalidFeatureException();
+
+    set_value();
+    set_test_value();
+}
 
 void SinNode::update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot)
 {
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/sin.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/sin.hpp
index 8f0dbf3b..992bf8f0 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/sin.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin/sin.hpp
@@ -61,6 +61,15 @@ public:
      */
     SinNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    SinNode(std::array<node_ptr, 1> feats, int feat_ind);
+
     // DocString: sin_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -71,7 +80,7 @@ public:
     /**
      * @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]) + ")";}
+    inline std::string expr(){return "sin(" + _feats[0]->expr() + ")";}
 
     // DocString: sin_node_set_value
     /**
@@ -81,11 +90,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::sin(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+        allowed_op_funcs::sin(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: sin_node_set_test_value
@@ -97,7 +106,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::sin(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: sin_node_rung
@@ -146,39 +155,65 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param prop property to fit to get the parameters
          */
-        SinNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief Set the values of the training data for the feature inside of the value storage arrays
          *
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        SinNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            if(_selected)
+                allowed_op_funcs::sin(_n_samp, _feats[0]->value_ptr(params + 2, 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(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         */
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::sin(_n_test_samp, _feats[0]->test_value_ptr(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
+
+        /**
+         * @brief The domain for the feature (min/max values)
+         *
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        inline Domain domain(double* params){return Domain(-1.0, 1.0);}
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "sin(" + std::to_string(params[0]) + "*" + _feats[0]->expr(params + 2) + " + " + std::to_string(params[1]) + ")";}
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize.cpp
deleted file mode 100644
index 0d372c03..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterize.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/sixth_power.hpp>
-
-SixPowNode::SixPowNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    if((feats[0]->type() == NODE_TYPE::CBRT) || (feats[0]->type() == NODE_TYPE::SQRT) || (feats[0]->type() == NODE_TYPE::SQ) || (feats[0]->type() == NODE_TYPE::CB) || (feats[0]->type() == NODE_TYPE::INV))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-SixPowNode::SixPowNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat}, feat_ind)
-{
-    if((feat->type() == NODE_TYPE::CBRT) || (feat->type() == NODE_TYPE::SQRT) || (feat->type() == NODE_TYPE::SQ) || (feat->type() == NODE_TYPE::CB) || (feat->type() == NODE_TYPE::INV))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-void SixPowNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double c = 0.0;
-    double* val = _feats[0]->value_ptr(rung() + 2);
-    if(param_list.size() > 3)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 3)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<SixPowResidual_alpha_a_c, 1, 1, 1, 1>(
-                    new SixPowResidual_alpha_a_c(val[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &c
-            );
-        }
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SixPowResidual_alpha_a, 1, 1, 1>(
-                        new SixPowResidual_alpha_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SixPowResidual_a_c, 1, 1, 1>(
-                        new SixPowResidual_a_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SixPowResidual_a, 1, 1>(
-                        new SixPowResidual_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..200fa995
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.cpp
@@ -0,0 +1,69 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.hpp>
+
+SixPowParamNode::SixPowParamNode()
+{}
+
+SixPowParamNode::SixPowParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SixPowNode(feats, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+SixPowParamNode::SixPowParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SixPowNode({feat}, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+SixPowParamNode::SixPowParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound) :
+    SixPowNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+SixPowParamNode::SixPowParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    SixPowNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void SixPowParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..99e01e99
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_power.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/parameterized_sixth_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_SIX_POW_NODE
+#define PARAM_SIX_POW_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/sixth_power.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class SixPowParamNode: public SixPowNode
+{
+    using SixPowNode::set_value;
+    using SixPowNode::set_test_value;
+    using SixPowNode::domain;
+    using SixPowNode::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<SixPowNode>(*this);
+         ar & _params;
+    }
+
+protected:
+    std::vector<double> _params; //!< The parameters vector
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    SixPowParamNode();
+
+    /**
+     * @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
+     */
+    SixPowParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    SixPowParamNode(std::array<node_ptr, 1> feats, 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 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
+     */
+    SixPowParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    SixPowParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: six_pow_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: six_pow_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: six_pow_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: six_pow_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/sixth_power.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/sixth_power.cpp
index 13c7c349..afef68b0 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/sixth_power.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/sixth_power.cpp
@@ -29,6 +29,16 @@ SixPowNode::SixPowNode(node_ptr feat, int feat_ind, double l_bound, double u_bou
     set_test_value();
 }
 
+SixPowNode::SixPowNode(std::array<node_ptr, 1> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    if((feats[0]->type() == NODE_TYPE::CBRT) || (feats[0]->type() == NODE_TYPE::SQRT) || (feats[0]->type() == NODE_TYPE::SQ) || (feats[0]->type() == NODE_TYPE::CB) || (feats[0]->type() == NODE_TYPE::INV))
+        throw InvalidFeatureException();
+
+    set_value();
+    set_test_value();
+}
+
 void SixPowNode::update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot)
 {
     std::string key = expr();
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/sixth_power.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/sixth_power.hpp
index e1138cf8..8ce2ac34 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/sixth_power.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/six_pow/sixth_power.hpp
@@ -61,6 +61,15 @@ public:
      */
     SixPowNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    SixPowNode(std::array<node_ptr, 1> feats, int feat_ind);
+
     // DocString: six_pow_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -71,7 +80,7 @@ public:
     /**
      * @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";}
+    inline std::string expr(){return "(" + _feats[0]->expr() + ")^6";}
 
     // DocString: six_pow_node_set_value
     /**
@@ -81,11 +90,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::sixth_pow(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+        allowed_op_funcs::sixth_pow(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: six_pow_node_set_test_value
@@ -97,7 +106,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::sixth_pow(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: six_pow_node_rung
@@ -117,7 +126,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().pow(6.0, _params[0], _params[1]);}
+    Domain domain(){return _feats[0]->domain().pow(6.0, 1.0, 0.0);}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -146,39 +155,65 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param prop property to fit to get the parameters
          */
-        SixPowNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief Set the values of the training data for the feature inside of the value storage arrays
          *
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        SixPowNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            if(_selected)
+                allowed_op_funcs::sixth_pow(_n_samp, _feats[0]->value_ptr(params + 2, 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(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         */
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::sixth_pow(_n_test_samp, _feats[0]->test_value_ptr(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
+
+        /**
+         * @brief The domain for the feature (min/max values)
+         *
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        inline Domain domain(double* params){return _feats[0]->domain(params + 2).pow(6.0, params[0], params[1]);}
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "(" + std::to_string(params[0]) + "*" + _feats[0]->expr(params + 2) + " + " + std::to_string(params[1]) + ")^6";}
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize.cpp
deleted file mode 100644
index ceb996f9..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterize.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sq/square.hpp>
-
-SqNode::SqNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    if((feats[0]->type() == NODE_TYPE::SQRT) || (feats[0]->type() == NODE_TYPE::INV))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-SqNode::SqNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat}, feat_ind)
-{
-    if((feat->type() == NODE_TYPE::SQRT) || (feat->type() == NODE_TYPE::INV))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-void SqNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double c = 0.0;
-    double* val = _feats[0]->value_ptr(rung() + 2);
-    if(param_list.size() > 3)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 3)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<SqResidual_alpha_a_c, 1, 1, 1, 1>(
-                    new SqResidual_alpha_a_c(val[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &c
-            );
-        }
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SqResidual_alpha_a, 1, 1, 1>(
-                        new SqResidual_alpha_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SqResidual_a_c, 1, 1, 1>(
-                        new SqResidual_a_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SqResidual_a, 1, 1>(
-                        new SqResidual_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..00601cac
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.cpp
@@ -0,0 +1,69 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.hpp>
+
+SqParamNode::SqParamNode()
+{}
+
+SqParamNode::SqParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SqNode(feats, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+SqParamNode::SqParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SqNode({feat}, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+SqParamNode::SqParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound) :
+    SqNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+SqParamNode::SqParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    SqNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void SqParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..4fcdfce8
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/sq/parameterized_square.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_SQ_NODE
+#define PARAM_SQ_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sq/square.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class SqParamNode: public SqNode
+{
+    using SqNode::set_value;
+    using SqNode::set_test_value;
+    using SqNode::domain;
+    using SqNode::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<SqNode>(*this);
+         ar & _params;
+    }
+
+protected:
+    std::vector<double> _params; //!< The parameters vector
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    SqParamNode();
+
+    /**
+     * @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
+     */
+    SqParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    SqParamNode(std::array<node_ptr, 1> feats, 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 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
+     */
+    SqParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    SqParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: sq_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: sq_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: sq_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: sq_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/square.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/square.cpp
index 8dd19b2e..5d4f4435 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/square.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/square.cpp
@@ -29,6 +29,16 @@ SqNode::SqNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
     set_test_value();
 }
 
+SqNode::SqNode(std::array<node_ptr, 1> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    if((feats[0]->type() == NODE_TYPE::SQRT) || (feats[0]->type() == NODE_TYPE::INV))
+        throw InvalidFeatureException();
+
+    set_value();
+    set_test_value();
+}
+
 void SqNode::update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot)
 {
     std::string key = expr();
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/square.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/square.hpp
index 2e880468..23d874b6 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/square.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sq/square.hpp
@@ -60,6 +60,15 @@ public:
      */
     SqNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    SqNode(std::array<node_ptr, 1> feats, int feat_ind);
+
     // DocString: sq_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -70,7 +79,7 @@ public:
     /**
      * @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]) + ")^2";}
+    inline std::string expr(){return "(" + _feats[0]->expr() + ")^2";}
 
     // DocString: sq_node_set_value
     /**
@@ -80,11 +89,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::sq(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+        allowed_op_funcs::sq(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: sq_node_set_test_value
@@ -96,7 +105,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::sq(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: sq_node_rung
@@ -116,7 +125,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().pow(2.0, _params[0], _params[1]);}
+    Domain domain(){return _feats[0]->domain().pow(2.0, 1.0, 0.0);}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -145,39 +154,65 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param prop property to fit to get the parameters
          */
-        SqNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief Set the values of the training data for the feature inside of the value storage arrays
          *
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        SqNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            if(_selected)
+                allowed_op_funcs::sq(_n_samp, _feats[0]->value_ptr(params + 2, 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(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         */
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::sq(_n_test_samp, _feats[0]->test_value_ptr(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
+
+        /**
+         * @brief The domain for the feature (min/max values)
+         *
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        inline Domain domain(double* params){return _feats[0]->domain(params + 2).pow(2.0, params[0], params[1]);}
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "(" + std::to_string(params[0]) + "*" + _feats[0]->expr(params + 2) + " + " + std::to_string(params[1]) + ")^2";}
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize.cpp
deleted file mode 100644
index b38c5abf..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterize.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/square_root.hpp>
-
-SqrtNode::SqrtNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    if((feats[0]->type() == NODE_TYPE::SQ) || (feats[0]->type() == NODE_TYPE::CB) || (feats[0]->type() == NODE_TYPE::SIX_POW) || (feats[0]->type() == NODE_TYPE::CBRT) || (feats[0]->type() == NODE_TYPE::INV))
-        throw InvalidFeatureException();
-
-    if((_feats[0]->domain() < 0.0))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-
-    if(_params[0] * _feats[0]->domain() + _params[1] < 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();
-
-    set_test_value();
-}
-
-SqrtNode::SqrtNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat}, feat_ind)
-{
-    if((feat->type() == NODE_TYPE::SQ) || (feat->type() == NODE_TYPE::CB) || (feat->type() == NODE_TYPE::SIX_POW) || (feat->type() == NODE_TYPE::CBRT) || (feat->type() == NODE_TYPE::INV))
-        throw InvalidFeatureException();
-
-    if((_feats[0]->domain() < 0.0))
-        throw InvalidFeatureException();
-
-    get_parameters(prop, param_list);
-
-    if(_params[0] * _feats[0]->domain() + _params[1] < 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();
-
-    set_test_value();
-}
-
-void SqrtNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double c = 0.0;
-    double* val = _feats[0]->value_ptr(rung() + 2);
-    if(param_list.size() > 3)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 3)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<SqrtResidual_alpha_a_c, 1, 1, 1, 1>(
-                    new SqrtResidual_alpha_a_c(val[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &_params[1], &c
-            );
-        }
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("alpha") == 0) || s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SqrtResidual_alpha_a, 1, 1, 1>(
-                        new SqrtResidual_alpha_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &_params[1]
-                );
-            }
-        }
-        else if(std::all_of(param_list.begin(), param_list.end(), [](std::string s){return (s.compare("a") == 0) || s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SqrtResidual_a_c, 1, 1, 1>(
-                        new SqrtResidual_a_c(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("a") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SqrtResidual_a, 1, 1>(
-                        new SqrtResidual_a(val[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[1]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..c15266e9
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.cpp
@@ -0,0 +1,75 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.hpp>
+
+SqrtParamNode::SqrtParamNode()
+{}
+
+SqrtParamNode::SqrtParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SqrtNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+
+    if(_params[0] * _feats[0]->domain() + _params[1] < 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();
+
+    set_test_value();
+}
+
+SqrtParamNode::SqrtParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SqrtNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+
+    get_parameters(prop);
+
+    if(_params[0] * _feats[0]->domain() + _params[1] < 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();
+
+    set_test_value();
+}
+
+SqrtParamNode::SqrtParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound) :
+    SqrtNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+SqrtParamNode::SqrtParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound) :
+    SqrtNode({feat}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void SqrtParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..53db8d8c
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_root.hpp
@@ -0,0 +1,153 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/parameterized_square_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_SQRT_NODE
+#define PARAM_SQRT_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/square_root.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_abs_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class SqrtParamNode: public SqrtNode
+{
+    using SqrtNode::set_value;
+    using SqrtNode::set_test_value;
+    using SqrtNode::domain;
+    using SqrtNode::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<SqrtNode>(*this);
+         ar & _params;
+    }
+
+protected:
+    std::vector<double> _params; //!< The parameters vector
+
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    SqrtParamNode();
+
+    /**
+     * @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
+     */
+    SqrtParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    SqrtParamNode(std::array<node_ptr, 1> feats, 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 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
+     */
+    SqrtParamNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    SqrtParamNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: sqrt_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: sqrt_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: sqrt_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: sqrt_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/square_root.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/square_root.cpp
index efadeae8..6a4ee9f4 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/square_root.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/square_root.cpp
@@ -35,6 +35,16 @@ SqrtNode::SqrtNode(node_ptr feat, int feat_ind, double l_bound, double u_bound):
     set_test_value();
 }
 
+SqrtNode::SqrtNode(std::array<node_ptr, 1> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    if((feats[0]->type() == NODE_TYPE::SQ) || (feats[0]->type() == NODE_TYPE::CB) || (feats[0]->type() == NODE_TYPE::SIX_POW) || (feats[0]->type() == NODE_TYPE::CBRT) || (feats[0]->type() == NODE_TYPE::INV))
+        throw InvalidFeatureException();
+
+    set_value();
+    set_test_value();
+}
+
 void SqrtNode::update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot)
 {
     std::string key = expr();
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/square_root.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/square_root.hpp
index 00a5ea48..035d703f 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/square_root.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sqrt/square_root.hpp
@@ -61,6 +61,15 @@ public:
      */
     SqrtNode(node_ptr feat, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    SqrtNode(std::array<node_ptr, 1> feats, int feat_ind);
+
     // DocString: sqrt_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -71,7 +80,7 @@ public:
     /**
      * @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]) + ")";}
+    inline std::string expr(){return "sqrt(" + _feats[0]->expr() + ")";}
 
     // DocString: sqrt_node_set_value
     /**
@@ -81,11 +90,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::sqrt(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_d_matrix_ptr(_d_mat_ind));
+        allowed_op_funcs::sqrt(_n_samp, _feats[0]->value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: sqrt_node_set_test_value
@@ -97,7 +106,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::sqrt(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: sqrt_node_rung
@@ -117,7 +126,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().pow(0.5, _params[0], _params[1]);}
+    Domain domain(){return _feats[0]->domain().pow(0.5, 1.0, 0.0);}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -146,39 +155,65 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param prop property to fit to get the parameters
          */
-        SqrtNode(std::array<node_ptr, 1> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief Set the values of the training data for the feature inside of the value storage arrays
          *
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        SqrtNode(node_ptr feat, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            if(_selected)
+                allowed_op_funcs::sqrt(_n_samp, _feats[0]->value_ptr(params + 2, 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(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         */
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::sqrt(_n_test_samp, _feats[0]->test_value_ptr(params + 2, offset + 2), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset, false));
+        }
+
+        /**
+         * @brief The domain for the feature (min/max values)
+         *
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        inline Domain domain(double* params){return _feats[0]->domain(params + 2).pow(1.0 / 2.0, params[0], params[1]);}
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "sqrt(" + std::to_string(params[0]) + "*" + _feats[0]->expr(params + 2) + " + " + std::to_string(params[1]) + ")";}
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize.cpp
deleted file mode 100644
index 80bedaa3..00000000
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterize.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sub/subtract.hpp>
-
-SubNode::SubNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode(feats, feat_ind)
-{
-    check_feats();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-SubNode::SubNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop) :
-    OperatorNode({feat_1, feat_2}, feat_ind)
-{
-    check_feats();
-
-    get_parameters(prop, param_list);
-    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();
-
-    set_test_value();
-}
-
-void SubNode::get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list)
-{
-    if(param_list.size() == 0)
-        return;
-    Problem problem;
-    double b = 0.0, c = 0.0;
-    double* val_1 = _feats[0]->value_ptr(rung() + 2);
-    double* val_2 = _feats[1]->value_ptr(rung() + 1);
-
-    if(param_list.size() > 3)
-    {
-        throw std::logic_error("Too many parameters passed to the constructor.");
-    }
-    else if(param_list.size() == 3)
-    {
-        for (int ss = 0; ss < _n_samp; ++ss)
-        {
-            problem.AddResidualBlock(
-                new AutoDiffCostFunction<SubResidual_alpha_b_c, 1, 1, 1, 1>(
-                    new SubResidual_alpha_b_c(val_1[ss], val_2[ss], prop[ss])
-                ),
-                new CauchyLoss(0.5),
-                &_params[0], &b,  &c
-            );
-        }
-    }
-    else if(param_list.size() == 2)
-    {
-        if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("c") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SubResidual_alpha_b, 1, 1, 1>(
-                        new SubResidual_alpha_b(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &b
-                );
-            }
-        }
-        else if(std::none_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("b") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SubResidual_alpha_c, 1, 1, 1>(
-                        new SubResidual_alpha_c(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0], &c
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    else if(param_list.size() == 1)
-    {
-        if(std::any_of(param_list.begin(), param_list.end(), [](std::string s){return s.compare("alpha") == 0;}))
-        {
-            for (int ss = 0; ss < _n_samp; ++ss)
-            {
-                problem.AddResidualBlock(
-                    new AutoDiffCostFunction<SubResidual_alpha, 1, 1>(
-                        new SubResidual_alpha(val_1[ss], val_2[ss], prop[ss])
-                    ),
-                    new CauchyLoss(0.5),
-                    &_params[0]
-                );
-            }
-        }
-        else
-            throw std::logic_error("Invalid parameter in the parameter list.");
-    }
-    parameterize(problem);
-}
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
new file mode 100644
index 00000000..3b2f50fc
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.cpp
@@ -0,0 +1,67 @@
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.hpp>
+
+SubParamNode::SubParamNode()
+{}
+
+SubParamNode::SubParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SubNode(feats, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+SubParamNode::SubParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop) :
+    SubNode({feat_1, feat_2}, feat_ind)
+{
+    _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();
+
+    set_test_value();
+}
+
+SubParamNode::SubParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound) :
+    SubNode(feats, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+SubParamNode::SubParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound) :
+    SubNode({feat_1, feat_2}, feat_ind)
+{
+    _params.resize(n_params(), 0.0);
+}
+
+void SubParamNode::get_parameters(std::vector<double>& prop)
+{
+    nlopt_wrapper::feat_data d;
+    d._feat = this;
+    d._prop = prop.data();
+    double minf;
+
+    std::fill_n(_params.begin(), _params.size(), 1.0);
+    dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+
+    nlopt::opt opt(nlopt::LN_SBPLX, _params.size());
+    opt.set_min_objective(nlopt_wrapper::objective_reg, &d);
+    opt.set_maxeval(1000);
+    opt.set_xtol_rel(1e-2);
+    try
+    {
+        nlopt::result result = opt.optimize(_params, minf);
+    }
+    catch(std::exception &e)
+    {
+        std::fill_n(_params.begin(), _params.size(), 1.0);
+        dcopy_(_params.size() / 2, nlopt_wrapper::_zeros.data(), 1, &_params[1], 2);
+    }
+}
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
new file mode 100644
index 00000000..7de44c42
--- /dev/null
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.hpp
@@ -0,0 +1,151 @@
+/** @file feature_creation/node/operator_nodes/allowed_operator_nodes/sub/parameterized_subtract.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_SUB_NODE
+#define PARAM_SUB_NODE
+
+#include <feature_creation/node/operator_nodes/allowed_operator_nodes/sub/subtract.hpp>
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+// DocString: cls_param_add_node
+/**
+ * @brief Node for the absolute value operator
+ *
+ */
+class SubParamNode: public SubNode
+{
+    using SubNode::set_value;
+    using SubNode::set_test_value;
+    using SubNode::domain;
+    using SubNode::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<SubNode>(*this);
+        ar & _params;
+    }
+protected:
+    std::vector<double> _params;
+public:
+    /**
+     * @brief Base Constructor
+     * @details This is only used for serialization
+     */
+    SubParamNode();
+
+    /**
+     * @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
+     */
+    SubParamNode(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 Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    SubParamNode(std::array<node_ptr, 2> feats, 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 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
+     */
+    SubParamNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
+
+    /**
+     * @brief Constructor
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @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
+     */
+    SubParamNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound);
+
+    // DocString: sub_param_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
+     */
+    inline void set_value(int offset = -1){set_value(_params.data(), offset);}
+
+    // DocString: sub_param_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
+     */
+    inline void set_test_value(int offset = -1){set_test_value(_params.data(), offset);}
+
+    // DocString: sub_param_node_expr
+    /**
+     * @brief Get the expression for the overall feature (From root node down)
+     */
+    inline std::string expr(){return expr(_params.data());}
+
+    // DocString: sub_param_node_domain
+    /**
+     * @brief The domain for the feature (min/max values)
+     */
+    inline Domain domain(){return domain(_params.data());}
+
+    /**
+     * @brief The parameters used for introducing more non linearity in the operators
+     */
+    inline std::vector<double> parameters(){return _params;}
+
+    /**
+     * @brief Solve the non-linear optimization to set the parameters
+     * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+     *
+     * @param prop property to fit to get the parameters
+     */
+    void get_parameters(std::vector<double>& prop);
+
+    /**
+     * @brief Set the non-linear parameters
+    */
+    inline void set_parameters(std::vector<double> params)
+    {
+        if(params.size() != n_params())
+            throw std::logic_error("Wrong number of parameters passed to set_parameters.");
+        _params = params;
+    }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/subtract.cpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/subtract.cpp
index 377acd7b..3a8a6738 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/subtract.cpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/subtract.cpp
@@ -27,6 +27,15 @@ SubNode::SubNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound,
     set_test_value();
 }
 
+SubNode::SubNode(std::array<node_ptr, 2> feats, int feat_ind):
+    OperatorNode(feats, feat_ind)
+{
+    check_feats();
+
+    set_value();
+    set_test_value();
+}
+
 void SubNode::check_feats()
 {
     if(_feats[0]->unit() != _feats[1]->unit())
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/subtract.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/subtract.hpp
index 2951f79c..15e57db5 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/subtract.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sub/subtract.hpp
@@ -62,6 +62,15 @@ public:
      */
     SubNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound);
 
+    /**
+     * @brief Constructor without checking feature values
+     * @details Constructs the Node from an array of features to operate on
+     *
+     * @param feats Features to operate over (Edges of the graph)
+     * @param feat_ind Index of the new feature
+     */
+    SubNode(std::array<node_ptr, 2> feats, int feat_ind);
+
     // DocString: sub_node_unit
     /**
      * @brief Get the unit of the feature (combine the units of _feats)
@@ -72,7 +81,7 @@ public:
     /**
      * @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() + ")]";}
+    inline std::string expr(){return "[(" + _feats[0]->expr() + ") - (" + _feats[1]->expr() + ")]";}
 
     // DocString: sub_node_set_value
     /**
@@ -82,11 +91,11 @@ public:
      */
     inline void set_value(int offset = -1)
     {
-        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));
-
         offset = (offset == -1) ? rung() : offset;
-        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));
+
+        if(_selected)
+            allowed_op_funcs::sub(_n_samp, _feats[0]->value_ptr(offset + 2), _feats[1]->value_ptr(offset + 1), 1.0, 0.0, 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), 1.0, 0.0, node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: sub_node_set_test_value
@@ -98,7 +107,7 @@ public:
     inline void set_test_value(int offset = -1)
     {
         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));
+        allowed_op_funcs::sub(_n_test_samp, _feats[0]->test_value_ptr(offset + 2), _feats[1]->test_value_ptr(offset + 1), 1.0, 0.0, node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
     }
 
     // DocString: sub_node_rung
@@ -118,7 +127,7 @@ public:
     /**
      * @brief The domain for the feature (min/max values)
      */
-    Domain domain(){return _feats[0]->domain().sub(_feats[1]->domain(), _params[0], _params[1]);}
+    Domain domain(){return _feats[0]->domain().sub(_feats[1]->domain(), 1.0, 0.0);}
 
     /**
      * @brief Get the string character representation of the node for the postfix expression
@@ -153,39 +162,65 @@ public:
 
     #ifdef PARAMETERIZE
         /**
-         * @brief Constructor
-         * @details Constructs the Node from an array of features to operate on
+         * @brief The parameters used for introducing more non linearity in the operators
+         */
+        virtual std::vector<double> parameters(){return {};}
+
+        /**
+         * @brief Solve the non-linear optimization to set the parameters
+         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
          *
-         * @param feats Features to operate over (Edges of the graph)
-         * @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
+         * @param prop property to fit to get the parameters
          */
-        SubNode(std::array<node_ptr, 2> feats, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        virtual void get_parameters(std::vector<double>& prop){return;}
+
+        /**
+         * @brief Set the non-linear parameters
+        */
+        virtual void set_parameters(std::vector<double>){return;}
 
         /**
-         * @brief Constructor
-         * @details Constructs the Node from node pointer of the feature to operate on
+         * @brief Set the values of the training data for the feature inside of the value storage arrays
          *
-         * @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
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
          */
-        SubNode(node_ptr feat_1, node_ptr feat_2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop);
+        inline void set_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+
+            if(_selected)
+                allowed_op_funcs::sub(_n_samp, _feats[0]->value_ptr(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->value_ptr(params + 2, 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(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->value_ptr(params + 2, offset + 1), params[0], params[1], node_value_arrs::get_value_ptr(_arr_ind, _feat_ind, offset));
+        }
 
         /**
-         * @brief Solve the non-linear optimization to set the parameters
-         * @details Fits the data points from _feats->value_ptr and prop to get the parameters for the feature
+         * @brief Set the values of the test data for the feature inside of the value storage arrays
          *
-         * @param prop property to fit to get the parameters
-         * @param param_list List describing the parameters to fit
+         * @param offset(int) Key to determine which part of the temporary storage array to look into
+         * @param params pointer to the parameter values
+         */
+        inline void set_test_value(const double* params, int offset = -1)
+        {
+            offset = (offset == -1) ? rung() : offset;
+            allowed_op_funcs::sub(_n_test_samp, _feats[0]->test_value_ptr(params + 2 + _feats[1]->n_params(), offset + 2), _feats[1]->test_value_ptr(params + 2, offset + 1), params[0], params[1], node_value_arrs::get_test_value_ptr(_arr_ind, _feat_ind, offset));
+        }
+
+        /**
+         * @brief The domain for the feature (min/max values)
+         *
+         * @param params parameter values for non-linear operations
+         * @return the domain
+         */
+        inline Domain domain(double* params){return _feats[0]->domain(params + _feats[1]->n_params() + 2).sub(_feats[1]->domain(params + 2), params[0], params[1]);}
+
+        /**
+         * @brief The expression of the feature
+         *
+         * @param params parameter values for non-linear operations
+         * @return feature expression
          */
-        void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list);
+        inline std::string expr(double* params){return "[(" + _feats[0]->expr(params + _feats[1]->n_params() + 2) + ") - (" + std::to_string(params[0]) + "*" + _feats[1]->expr(params + 2) + " + " + std::to_string(params[1]) + ")]";}
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/allowed_ops.hpp b/src/feature_creation/node/operator_nodes/allowed_ops.hpp
index c1b0a8a2..2d404e88 100644
--- a/src/feature_creation/node/operator_nodes/allowed_ops.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_ops.hpp
@@ -25,6 +25,23 @@
 #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 <map>
 #include <iostream>
 
@@ -32,8 +49,8 @@ typedef std::function<node_ptr(node_ptr, int, double, double)> un_op_node_gen;
 typedef std::function<node_ptr(node_ptr, node_ptr, int, double, double)> bin_op_node_gen;
 
 #ifdef PARAMETERIZE
-    typedef std::function<node_ptr(node_ptr, int, double, double, std::vector<std::string>&, std::vector<double>&)> un_param_op_node_gen;
-    typedef std::function<node_ptr(node_ptr, node_ptr, int, double, double, std::vector<std::string>&, std::vector<double>&)> bin_param_op_node_gen;
+    typedef std::function<node_ptr(node_ptr, int, double, double, std::vector<double>&)> un_param_op_node_gen;
+    typedef std::function<node_ptr(node_ptr, node_ptr, int, double, double, std::vector<double>&)> bin_param_op_node_gen;
 #endif
 
 namespace allowed_op_maps
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 6d71c8b4..cc522f8a 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,22 @@ 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"] = [](node_ptr f1, node_ptr f2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<AddNode>(f1, f2, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::binary_param_operator_map["sub"] = [](node_ptr f1, node_ptr f2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<SubNode>(f1, f2, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::binary_param_operator_map["abs_diff"] = [](node_ptr f1, node_ptr f2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<AbsDiffNode>(f1, f2, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::binary_param_operator_map["mult"] = [](node_ptr f1, node_ptr f2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<MultNode>(f1, f2, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::binary_param_operator_map["div"] = [](node_ptr f1, node_ptr f2, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<DivNode>(f1, f2, feat_ind, l_bound, u_bound, param_list, prop);};
+    allowed_op_maps::binary_param_operator_map["add"] = [](node_ptr f1, node_ptr f2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<AddParamNode>(f1, f2, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::binary_param_operator_map["sub"] = [](node_ptr f1, node_ptr f2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<SubParamNode>(f1, f2, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::binary_param_operator_map["abs_diff"] = [](node_ptr f1, node_ptr f2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<AbsDiffParamNode>(f1, f2, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::binary_param_operator_map["mult"] = [](node_ptr f1, node_ptr f2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<MultParamNode>(f1, f2, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::binary_param_operator_map["div"] = [](node_ptr f1, node_ptr f2, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<DivParamNode>(f1, f2, feat_ind, l_bound, u_bound, prop);};
 
-    allowed_op_maps::unary_param_operator_map["exp"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<ExpNode>(f1, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::unary_param_operator_map["neg_exp"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<NegExpNode>(f1, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::unary_param_operator_map["inv"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<InvNode>(f1, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::unary_param_operator_map["sq"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<SqNode>(f1, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::unary_param_operator_map["cb"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<CbNode>(f1, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::unary_param_operator_map["six_pow"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<SixPowNode>(f1, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::unary_param_operator_map["sqrt"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<SqrtNode>(f1, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::unary_param_operator_map["cbrt"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<CbrtNode>(f1, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::unary_param_operator_map["log"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<LogNode>(f1, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::unary_param_operator_map["abs"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<AbsNode>(f1, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::unary_param_operator_map["sin"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<SinNode>(f1, feat_ind, l_bound, u_bound, param_list, prop);};
-    allowed_op_maps::unary_param_operator_map["cos"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<std::string>& param_list, std::vector<double>& prop){return std::make_shared<CosNode>(f1, feat_ind, l_bound, u_bound, param_list, prop);};
+    allowed_op_maps::unary_param_operator_map["exp"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<ExpParamNode>(f1, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::unary_param_operator_map["neg_exp"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<NegExpParamNode>(f1, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::unary_param_operator_map["inv"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<InvParamNode>(f1, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::unary_param_operator_map["sq"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<SqParamNode>(f1, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::unary_param_operator_map["cb"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<CbParamNode>(f1, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::unary_param_operator_map["six_pow"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<SixPowParamNode>(f1, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::unary_param_operator_map["sqrt"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<SqrtParamNode>(f1, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::unary_param_operator_map["cbrt"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<CbrtParamNode>(f1, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::unary_param_operator_map["log"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<LogParamNode>(f1, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::unary_param_operator_map["abs"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<AbsParamNode>(f1, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::unary_param_operator_map["sin"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<SinParamNode>(f1, feat_ind, l_bound, u_bound, prop);};
+    allowed_op_maps::unary_param_operator_map["cos"] = [](node_ptr f1, int feat_ind, double l_bound, double u_bound, std::vector<double>& prop){return std::make_shared<CosParamNode>(f1, feat_ind, l_bound, u_bound, prop);};
 }
diff --git a/src/feature_creation/node/value_storage/nodes_value_containers.cpp b/src/feature_creation/node/value_storage/nodes_value_containers.cpp
index a0fca7d1..f6801040 100644
--- a/src/feature_creation/node/value_storage/nodes_value_containers.cpp
+++ b/src/feature_creation/node/value_storage/nodes_value_containers.cpp
@@ -66,21 +66,21 @@ void node_value_arrs::resize_values_arr(int n_dims, int n_feat, bool use_temp)
     }
 }
 
-double* node_value_arrs::get_value_ptr(int arr_ind, int feat_ind, int offset)
+double* node_value_arrs::get_value_ptr(int arr_ind, int feat_ind, int offset, bool modify_reg)
 {
     if(arr_ind < N_STORE_FEATURES)
         return  access_value_arr(arr_ind);
 
-    temp_storage_reg(arr_ind, offset) = feat_ind;
+    temp_storage_reg(arr_ind, offset) = feat_ind * modify_reg - (!modify_reg);
     return access_temp_storage((arr_ind % N_STORE_FEATURES) + (offset % 3) * N_STORE_FEATURES);
 }
 
-double* node_value_arrs::get_test_value_ptr(int arr_ind, int feat_ind, int offset)
+double* node_value_arrs::get_test_value_ptr(int arr_ind, int feat_ind, int offset, bool modify_reg)
 {
     if(arr_ind < N_STORE_FEATURES)
         return  access_test_value_arr(arr_ind);
 
-    temp_storage_test_reg(arr_ind, offset) = feat_ind;
+    temp_storage_test_reg(arr_ind, offset) = feat_ind * modify_reg - (!modify_reg);
     return access_temp_storage_test((arr_ind % N_STORE_FEATURES) + (offset % 3) * N_STORE_FEATURES);
 }
 
diff --git a/src/feature_creation/node/value_storage/nodes_value_containers.hpp b/src/feature_creation/node/value_storage/nodes_value_containers.hpp
index 32af493c..4a7cd8f8 100644
--- a/src/feature_creation/node/value_storage/nodes_value_containers.hpp
+++ b/src/feature_creation/node/value_storage/nodes_value_containers.hpp
@@ -132,7 +132,7 @@ namespace node_value_arrs
      *
      * @return The value pointer
      */
-    double* get_value_ptr(int arr_ind, int feat_ind, int offset = 0);
+    double* get_value_ptr(int arr_ind, int feat_ind, int offset = 0, bool modify_reg = true);
 
     /**
      * @brief Get a Node's test_value_ptr
@@ -143,7 +143,7 @@ namespace node_value_arrs
      *
      * @return The value pointer
      */
-    double* get_test_value_ptr(int arr_ind, int feat_ind, int offset = 0);
+    double* get_test_value_ptr(int arr_ind, int feat_ind, int offset = 0, bool modify_reg = true);
 
     /**
      * @brief Get the pointer to a particular selected Node from sis
diff --git a/src/feature_creation/parameterization/NLOptWrapper.cpp b/src/feature_creation/parameterization/NLOptWrapper.cpp
new file mode 100644
index 00000000..f34b3614
--- /dev/null
+++ b/src/feature_creation/parameterization/NLOptWrapper.cpp
@@ -0,0 +1,22 @@
+#include <feature_creation/parameterization/NLOptWrapper.hpp>
+
+std::function<double(unsigned int n, const double* p, double* grad, void* data)> nlopt_wrapper::_objective;
+std::vector<double> nlopt_wrapper::_zeros;
+
+void nlopt_wrapper::set_objective(std::string calc_type, double* prop, std::vector<int> sizes)
+{
+    _zeros.resize(100, 0.0);
+    if(calc_type.compare("classification") == 0)
+    {
+        convex_hull::initialize_projection(sizes, prop);
+        _objective = objective_reg;
+    }
+    else if(calc_type.compare("regression") == 0)
+    {
+        _objective = objective_class;
+    }
+    else
+    {
+        throw std::logic_error("projection type can not determined");
+    }
+}
diff --git a/src/feature_creation/parameterization/NLOptWrapper.hpp b/src/feature_creation/parameterization/NLOptWrapper.hpp
new file mode 100644
index 00000000..ed75fa0a
--- /dev/null
+++ b/src/feature_creation/parameterization/NLOptWrapper.hpp
@@ -0,0 +1,49 @@
+/** @file feature_creation/parameterization/NLOptWrapper.hpp
+ *  @brief Wrapper and functions to use libnlopt to find parameterizations for features
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef NL_OPT_WRAPPER
+#define NL_OPT_WRAPPER
+
+#include <nlopt.hpp>
+#include <feature_creation/node/Node.hpp>
+#include <classification/utils.hpp>
+
+namespace nlopt_wrapper
+{
+    typedef struct
+    {
+        double *_prop; //!< pointer to the property vector
+        Node* _feat; //!< Node pointer of the feature to parameterize
+    } feat_data;
+
+    static double objective_class(unsigned int n, const double* p, double* grad, void* data)
+    {
+        feat_data* d = (feat_data*) data;
+        return convex_hull::overlap_1d(d->_feat->value_ptr(p));
+    }
+
+    static double objective_reg(unsigned int n, const double* p, double* grad, void* data)
+    {
+        feat_data* d = (feat_data*) data;
+        double score = util_funcs::r2(d->_feat->value_ptr(p), d->_prop, d->_feat->n_samp());
+        return -1.0 * std::isfinite(score) * score;
+    }
+
+    extern std::function<double(unsigned int n, const double* p, double* grad, void* data)> _objective; //!< Objective function to use
+    extern std::vector<double> _zeros; //!< array of zeros to fill parameters
+
+    /**
+     * @brief Set up the projection operator for the objective function
+     *
+     * @param calc_type [description]
+     * @param prop [description]
+     * @param N [description]
+     */
+    void set_objective(std::string calc_type, double* prop, std::vector<int> sizes);
+}
+
+
+#endif
\ No newline at end of file
diff --git a/src/inputs/InputParser.cpp b/src/inputs/InputParser.cpp
index 52f09203..541ed534 100644
--- a/src/inputs/InputParser.cpp
+++ b/src/inputs/InputParser.cpp
@@ -2,6 +2,7 @@
 
 InputParser::InputParser(pt::ptree IP, std::string fn, std::shared_ptr<MPI_Interface> comm) :
     _opset(as_vector<std::string>(IP, "opset")),
+    _param_opset(as_vector<std::string>(IP, "param_opset")),
     _filename(fn),
     _data_file(IP.get<std::string>("data_file", "data.csv")),
     _prop_key(IP.get<std::string>("property_key", "prop")),
@@ -148,24 +149,6 @@ InputParser::InputParser(pt::ptree IP, std::string fn, std::shared_ptr<MPI_Inter
         "sqrt",
         "cbrt"
     };
-    for(pt::ptree::value_type &param_op : IP.get_child("param_opset"))
-    {
-        _param_opset[param_op.first] = as_vector<std::string>(param_op.second);
-        if(_fix_intercept)
-        {
-            std::vector<std::string>::iterator it = std::find(_param_opset[param_op.first].begin(), _param_opset[param_op.first].end(), "c");
-            if(it != _param_opset[param_op.first].end())
-                _param_opset[param_op.first].erase(it);
-
-            it = std::find(fix_intercept_problem_a.begin(), fix_intercept_problem_a.end(), param_op.first);
-            if(it != fix_intercept_problem_a.end())
-            {
-                it = std::find(_param_opset[param_op.first].begin(), _param_opset[param_op.first].end(), "a");
-                if(it != _param_opset[param_op.first].end())
-                    _param_opset[param_op.first].erase(it);
-            }
-        }
-    }
 
     if((_opset.size() == 0) && (_param_opset.size() == 0))
     {
@@ -316,6 +299,12 @@ void InputParser::generate_feature_space(std::shared_ptr<MPI_Interface> comm, st
             std::array<double, 2> end_points;
             end_points[0] = *std::min_element(data[ff].begin(), data[ff].end());
             end_points[1] = *std::max_element(data[ff].begin(), data[ff].end());
+
+            double range = end_points[1] - end_points[0];
+
+            end_points[0] -= range * 0.01;
+            end_points[1] += range * 0.01;
+
             domain_list[ff] = std::make_shared<Domain>(end_points);
         }
         phi_0.push_back(std::make_shared<FeatureNode>(ff, headers[ff], data[ff], test_data[ff], *domain_list[ff], units[ff]));
diff --git a/src/inputs/InputParser.hpp b/src/inputs/InputParser.hpp
index 4b41aa29..e71265ac 100644
--- a/src/inputs/InputParser.hpp
+++ b/src/inputs/InputParser.hpp
@@ -36,7 +36,7 @@ class InputParser
 {
 public:
 
-    std::map<std::string, std::vector<std::string>> _param_opset; //!< Map of parameterization operator set (set of operators and non-linear parameters used for a non-linear least squares fit to property)
+    std::vector<std::string> _param_opset; //!< Map of parameterization operator set (set of operators and non-linear parameters used for a non-linear least squares fit to property)
     std::vector<std::string> _opset; //!< List of all operators for feature combination
     std::vector<double> _prop_train; //!< The values of the property in the training set
     std::vector<double> _prop_test; //!< The values of the property in the test set
diff --git a/src/main.cpp b/src/main.cpp
index f27916e8..41cf1fb5 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2,16 +2,16 @@
 #include <descriptor_identifier/SISSO_DI/SISSORegressor.hpp>
 #include <descriptor_identifier/SISSO_DI/SISSOClassifier.hpp>
 #include <iostream>
-#include <gflags/gflags.h>
+// #include <gflags/gflags.h>
 
 int main(int argc, char const *argv[])
 {
 
     allowed_op_maps::set_node_maps();
     #ifdef PARAMETERIZE
-        google::InitGoogleLogging("XXX");
-        google::SetCommandLineOption("GLOG_minloglevel", "3");
-        google::SetCommandLineOption("stderrthreshold", "3");
+        // google::InitGoogleLogging("XXX");
+        // google::SetCommandLineOption("GLOG_minloglevel", "3");
+        // google::SetCommandLineOption("stderrthreshold", "3");
         allowed_op_maps::set_param_node_maps();
     #endif
 
diff --git a/src/python/_sisso.cpp b/src/python/_sisso.cpp
index 86158e2b..9e25c4a9 100644
--- a/src/python/_sisso.cpp
+++ b/src/python/_sisso.cpp
@@ -6,21 +6,10 @@
 #include <mpi_interface/MPI_Interface.hpp>
 #include <Python.h>
 
-#ifdef PARAMETERIZE
-    #include <gflags/gflags.h>
-#endif
-
 static void finalize();
 
 BOOST_PYTHON_MODULE(_sisso)
 {
-
-    #ifdef PARAMETERIZE
-        google::InitGoogleLogging("XXX");
-        google::SetCommandLineOption("GLOG_minloglevel", "3");
-        google::SetCommandLineOption("stderrthreshold", "3");
-    #endif
-
     py::docstring_options local_docstring_options(true, true, true);
 
     Py_Initialize();
diff --git a/src/python/bindings.cpp b/src/python/bindings.cpp
new file mode 100644
index 00000000..ae31f904
--- /dev/null
+++ b/src/python/bindings.cpp
@@ -0,0 +1,919 @@
+#include <python/bindings.hpp>
+
+using namespace boost::python;
+
+void sisso::register_all()
+{
+    sisso::descriptor_identifier::registerModel();
+    sisso::descriptor_identifier::registerModelRegressor();
+    sisso::descriptor_identifier::registerModelClassifier();
+    sisso::descriptor_identifier::registerSISSO_DI();
+    sisso::descriptor_identifier::registerSISSORegressor();
+    sisso::descriptor_identifier::registerSISSOClassifier();
+    sisso::feature_creation::registerFeatureSpace();
+    sisso::feature_creation::registerDomain();
+    sisso::feature_creation::registerUnit();
+    sisso::feature_creation::node::registerNode();
+    sisso::feature_creation::node::registerFeatureNode();
+    sisso::feature_creation::node::registerModelNode();
+    sisso::feature_creation::node::registerOperatorNode<1>();
+    sisso::feature_creation::node::registerOperatorNode<2>();
+    sisso::feature_creation::node::registerAddNode();
+    sisso::feature_creation::node::registerSubNode();
+    sisso::feature_creation::node::registerDivNode();
+    sisso::feature_creation::node::registerMultNode();
+    sisso::feature_creation::node::registerAbsDiffNode();
+    sisso::feature_creation::node::registerAbsNode();
+    sisso::feature_creation::node::registerInvNode();
+    sisso::feature_creation::node::registerLogNode();
+    sisso::feature_creation::node::registerExpNode();
+    sisso::feature_creation::node::registerNegExpNode();
+    sisso::feature_creation::node::registerSinNode();
+    sisso::feature_creation::node::registerCosNode();
+    sisso::feature_creation::node::registerCbNode();
+    sisso::feature_creation::node::registerCbrtNode();
+    sisso::feature_creation::node::registerSqNode();
+    sisso::feature_creation::node::registerSqrtNode();
+    sisso::feature_creation::node::registerSixPowNode();
+
+    def("phi_selected_from_file", &str2node::phi_selected_from_file_py);
+    def("initialize_values_arr", &node_value_arrs::initialize_values_arr);
+    def("initialize_d_matrix_arr", &node_value_arrs::initialize_d_matrix_arr);
+}
+
+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>>())
+        .def(init<std::string, list, list, optional<std::string, int, double>>())
+        .def("sis", sis_list, "Wrapper function for SIS using a python list\nArgs:\n    prop (list): The property to perform SIS over as a python list")
+        .def("sis", sis_ndarray, "Wrapper function for SIS using a numpy array\nArgs:\n    prop (np.ndarray): The property to perform SIS over as a numpy array")
+        .def("feat_in_phi", &FeatureSpace::feat_in_phi, "Is a feature in this process' _phi?\nArgs:\n    ind: The index of the feature\n\nReturns:\n     True if feature is in this rank's _phi")
+        .def("remove_feature", &FeatureSpace::remove_feature, "Remove a feature from phi\nArgs:\n    ind: index of feature to remove")
+        .def("get_feature", &FeatureSpace::get_feature, "Return a feature at a specified index\nArgs:\n    ind: index of the feature to get\n\nReturns:\n     A ModelNode of the feature at index ind")
+        .add_property("phi_selected", &FeatureSpace::phi_selected_py, "The selected feature space (cpp definition in <python/feature_creation/FeatureSpace.cpp>)\n\nReturns:\n     _phi_selected as a python list")
+        .add_property("phi0", &FeatureSpace::phi0_py, "The initial feature space (cpp definition in <python/feature_creation/FeatureSpace.cpp>)\n\nReturns:\n     _phi0 as a python list")
+        .add_property("phi", &FeatureSpace::phi_py, "The feature space (cpp definition in <python/feature_creation/FeatureSpace.cpp>)\n\nReturns:\n     _phi as a python list")
+        .add_property("scores", &FeatureSpace::scores_py, "The vector of projection scores for SIS\n\nReturns:\n     _scores as a numpy array")
+        .add_property("task_sizes", &FeatureSpace::task_sizes_py, "The vector storing the number of samples in each task\n\nReturns:\n     _task_sizes as a python list")
+        .add_property("allowed_ops", &FeatureSpace::allowed_ops_py, "The list of allowed operator nodes\n\nReturns:\n     _allowed_ops as a python list")
+        .add_property("start_gen", &FeatureSpace::start_gen_py, "The index in _phi where each generation starts\n\nReturns:\n     _start_gen as a python list")
+        .add_property("feature_space_file", &FeatureSpace::feature_space_file, "The feature space filename")
+        .add_property("l_bound", &FeatureSpace::l_bound, "The minimum absolute value of the feature")
+        .add_property("u_bound", &FeatureSpace::u_bound, "The maximum absolute value of the feature")
+        .add_property("max_phi", &FeatureSpace::max_phi, "The maximum rung of the feature space")
+        .add_property("n_sis_select", &FeatureSpace::n_sis_select, "The number of features selected in each SIS step")
+        .add_property("n_samp", &FeatureSpace::n_samp, "The number of samples per feature")
+        .add_property("n_feat", &FeatureSpace::n_feat, "The number of features in the feature space")
+        .add_property("n_rung_store", &FeatureSpace::n_rung_store, "The number of rungs whose feature training data is stored in memory")
+        .add_property("n_rung_generate", &FeatureSpace::n_rung_generate, "The number of rungs to be generated on the fly during SIS")
+    ;
+}
+
+void sisso::feature_creation::registerUnit()
+{
+    class_<Unit>("Unit", init<>())
+        .def(init<std::map<std::string, double>>())
+        .def(init<std::string>())
+        .def(init<Unit&>())
+        .def("__str__", &Unit::toString, "Convert the unit into a string")
+        .def("__repr__", &Unit::toString, "Convert the unit into a string")
+        .def("inverse", &Unit::inverse, "Inverse operator for units\n\nReturns:\n     The inverse of this unit")
+        .def(self * self)
+        .def(self / self)
+        .def(self *= self)
+        .def(self /= self)
+        .def(self == self)
+        .def(self != self)
+        .def("__pow__", &Unit::operator^, "Exponentiation operator for units\nArgs:\n    power: power to exponentiate the unit\n\nReturns:\n     The unit raised to the power")
+    ;
+}
+
+void sisso::feature_creation::registerDomain()
+{
+    class_<Domain>("Domain", init<>())
+        .def(init<std::string>())
+        .def(init<double, double>())
+        .def(init<double, double, double, double>())
+        .def(init<Domain&>())
+        .def("__str__", &Domain::toString, "Convert the domain into a string")
+        .def("__repr__", &Domain::toString, "Convert the domain into a string")
+        .def("__pow__", &Domain::operator^, "Exponentiation operator for domain\nArgs:\n    power: power to exponentiate the domain\n\nReturns:\n     The domain raised to the power")
+        .def("inv", &Domain::inv, "The inverse of the domain\nArgs:\n    alpha: prefactor for the domain\n    a: The shift of the domain\n\nReturns:\n     The log(alpha * domain + a) domain")
+        .def("exp", &Domain::exp, "Exponential of the domain\nArgs:\n    alpha: prefactor for the domain\n    a: The shift of the domain\n\nReturns:\n     The exp(alpha * domain + a) domain")
+        .def("log", &Domain::log, "Logarithm of the domain\nArgs:\n    alpha: prefactor for the domain\n    a: The shift of the domain\n\nReturns:\n     The log(alpha * domain + a) domain")
+        .def("add", &Domain::add, "Addition operator with a scale and a shift\nArgs:\n    domain_2: the second domain to operate on\n    alpha: scale parameter\n    a: shift parameter\n\nReturns:\n     The resulting domain")
+        .def("sub", &Domain::sub, "Subtraction operator with a scale and a shift\nArgs:\n    domain_2: the second domain to operate on\n    alpha: scale parameter\n    a: shift parameter\n\nReturns:\n     The resulting domain")
+        .def("mult", &Domain::mult, "Multiplication operator with a scale and a shift\nArgs:\n    domain_2: the second domain to operate on\n    alpha: scale parameter\n    a: shift parameter\n\nReturns:\n     The resulting domain")
+        .def("div", &Domain::div, "Division operator with a scale and a shift\nArgs:\n    domain_2: the second domain to operate on\n    alpha: scale parameter\n    a: shift parameter\n\nReturns:\n     The resulting domain")
+        .def("pow", &Domain::pow, "Exponentiation operator for domain\nArgs:\n    power: power to exponentiate the domain\n\nReturns:\n     The domain raised to the power")
+        .def("abs", &Domain::abs, "Exponential of the domain\nArgs:\n    alpha: prefactor for the domain\n    a: The shift of the domain\n\nReturns:\n     The abs(alpha * domain + a) domain")
+        .def("__contains__", &Domain::contains, "Returns true if number is inside the domain\nArgs:\n    number: number to check\n\nReturns:\n     True if number is inside the domain")
+        .def(self + self)
+        .def(self - self)
+        .def(self * self)
+        .def(self / self)
+        .def(self + double())
+        .def(self - double())
+        .def(self * double())
+        .def(self / double())
+        .def(self == self)
+        .def(self != self)
+        .def(double() + self)
+        .def(double() - self)
+        .def(double() * self)
+        .def(double() / self)
+        .add_property("end_points", &Domain::end_points_py, "")
+        .add_property("excluded_points", &Domain::excluded_points_py, "")
+    ;
+}
+
+#ifdef PARAMETERIZE
+    void sisso::feature_creation::node::registerNode()
+    {
+        void (Node::*reindex_1)(int) = &Node::reindex;
+        void (Node::*reindex_2)(int, int) = &Node::reindex;
+        // void (Node::*expr_no_param)() = &Node::expr;
+
+        class_<sisso::feature_creation::node::NodeWrap, boost::noncopyable>("Node", no_init)
+            .def("reindex", reindex_1, "Reindex the feature\nre-index the feature to be continuous\nArgs:\n    ind (int): the new feature and array index")
+            .def("reindex", reindex_2, "Reindex the feature\nre-index the feature to be continuous\nArgs:\n    ind (int): the new feature index\n    arr_ind (int): the new array index")
+            // .def("__str__", expr_no_param, "Get the expression for the overall feature (From root node down)")
+            // .def("__repr__", expr_no_param, "Get the expression for the overall feature (From root node down)")
+            .add_property("n_samp", &Node::n_samp, "")
+            .add_property("n_test_samp", &Node::n_test_samp, "")
+            .add_property("feat_ind", &Node::feat_ind, "The feature index")
+            .add_property("arr_ind", &Node::arr_ind, "The feature array index")
+            .add_property("selected", &Node::selected, &Node::set_selected, "")
+            .add_property("d_mat_ind", &Node::d_mat_ind, &Node::set_d_mat_ind, "")
+            .add_property("value", &Node::value_py, "The training data of the feature\n\nReturns:\n     The training data as a numpy array")
+            .add_property("test_value", &Node::test_value_py, "The test data of the feature\n\nReturns:\n     The test data as a numpy array")
+            .add_property("primary_feat_decomp", &Node::primary_feature_decomp_py, "Get the primary feature decomposition of a feature\n\nReturns:\n     A python dict representing the primary feature comprising a feature")
+            .add_property("postfix_expr", &Node::postfix_expr, "Get the postfix expression for the feature\n\nReturns:\n     The postfix string for the expression")
+            .add_property("parameters", &Node::parameters_py, "The parameters used for non-linear operator nodes\n\nReturns:\n     The operator node parameters as a list [alpha, a]")
+            // .def("domain", pure_virtual(&Node::domain), "The domain for the feature (min/max values)")
+            // .def("expr", pure_virtual(expr_no_param), "Get the expression for the overall feature (From root node down)")
+            .def("unit", pure_virtual(&Node::unit), "Get the unit for the overall feature (From root node down)")
+            // .def("set_value", pure_virtual(&Node::set_value), "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+            // .def("set_test_value", pure_virtual(&Node::set_test_value), "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+            .def("is_nan", pure_virtual(&Node::is_nan), "Check if the feature contains NaN")
+            .def("is_const", pure_virtual(&Node::is_const), "Check if feature is constant")
+            .def("rung", pure_virtual(&Node::rung), "return the rung of the feature\nArgs:\n    cur_rung (int): The rung current rung of the feature tree (used to recursively calculate rung)")
+            .def("n_feats", pure_virtual(&Node::n_feats), "")
+            .def("feat", pure_virtual(&Node::feat), "")
+        ;
+    }
+#else
+    void sisso::feature_creation::node::registerNode()
+    {
+        void (Node::*reindex_1)(int) = &Node::reindex;
+        void (Node::*reindex_2)(int, int) = &Node::reindex;
+        class_<sisso::feature_creation::node::NodeWrap, boost::noncopyable>("Node", no_init)
+            .def("reindex", reindex_1, "Reindex the feature\nre-index the feature to be continuous\nArgs:\n    ind (int): the new feature and array index")
+            .def("reindex", reindex_2, "Reindex the feature\nre-index the feature to be continuous\nArgs:\n    ind (int): the new feature index\n    arr_ind (int): the new array index")
+            // .def("__str__", &Node::expr, "Get the expression for the overall feature (From root node down)")
+            // .def("__repr__", &Node::expr, "Get the expression for the overall feature (From root node down)")
+            .add_property("n_samp", &Node::n_samp, "")
+            .add_property("n_test_samp", &Node::n_test_samp, "")
+            .add_property("feat_ind", &Node::feat_ind, "The feature index")
+            .add_property("arr_ind", &Node::arr_ind, "The feature array index")
+            .add_property("selected", &Node::selected, &Node::set_selected, "")
+            .add_property("d_mat_ind", &Node::d_mat_ind, &Node::set_d_mat_ind, "")
+            .add_property("value", &Node::value_py, "The training data of the feature\n\nReturns:\n     The training data as a numpy array")
+            .add_property("test_value", &Node::test_value_py, "The test data of the feature\n\nReturns:\n     The test data as a numpy array")
+            .add_property("primary_feat_decomp", &Node::primary_feature_decomp_py, "Get the primary feature decomposition of a feature\n\nReturns:\n     A python dict representing the primary feature comprising a feature")
+            .add_property("postfix_expr", &Node::postfix_expr, "Get the postfix expression for the feature\n\nReturns:\n     The postfix string for the expression")
+            // .def("expr", pure_virtual(&Node::expr), "Get the expression for the overall feature (From root node down)")
+            .def("unit", pure_virtual(&Node::unit), "Get the unit for the overall feature (From root node down)")
+            // .def("set_value", pure_virtual(&Node::set_value), "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+            // .def("set_test_value", pure_virtual(&Node::set_test_value), "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+            .def("is_nan", pure_virtual(&Node::is_nan), "Check if the feature contains NaN")
+            .def("is_const", pure_virtual(&Node::is_const), "Check if feature is constant")
+            .def("rung", pure_virtual(&Node::rung), "return the rung of the feature\nArgs:\n    cur_rung (int): The rung current rung of the feature tree (used to recursively calculate rung)")
+            .def("n_feats", pure_virtual(&Node::n_feats), "")
+            .def("feat", pure_virtual(&Node::feat), "")
+        ;
+    }
+#endif
+
+void sisso::feature_creation::node::registerFeatureNode()
+{
+    std::string (FeatureNode::*expr_1)() = &FeatureNode::expr;
+    std::string (FeatureNode::*expr_const)() const = &FeatureNode::expr;
+    void (FeatureNode::*set_value_no_param)(int) = &FeatureNode::set_value;
+    void (FeatureNode::*set_test_value_no_param)(int) = &FeatureNode::set_test_value;
+    Domain (FeatureNode::*domain_no_param)() = &FeatureNode::domain;
+
+    using namespace boost::python;
+    class_<FeatureNode, bases<Node>>("FeatureNode", init<int, std::string, np::ndarray, np::ndarray, Domain, Unit>())
+        .def(init<int, std::string, py::list, py::list, Domain, Unit>())
+        .def("is_nan", &FeatureNode::is_nan, "Check if the feature contains NaN")
+        .def("is_const", &FeatureNode::is_const, "Check if feature is constant")
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_1, "Get the string expression used to represent the primary feature")
+        .add_property("expr", expr_const, "Get the string expression used to represent the primary feature")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &FeatureNode::unit, "The unit of the primary feature")
+        .add_property("rung", &FeatureNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerModelNode()
+{
+    std::string (ModelNode::*expr_1)() = &ModelNode::expr;
+    std::string (ModelNode::*expr_const)() const = &ModelNode::expr;
+
+    using namespace boost::python;
+    class_<ModelNode, bases<FeatureNode>>("ModelNode", init<int, int, std::string, std::string, std::vector<double>, std::vector<double>, Domain, Unit>())
+        .def("is_nan", &ModelNode::is_nan, "Check if the feature contains NaN")
+        .def("is_const", &ModelNode::is_const, "Check if feature is constant")
+        .def("set_value", &ModelNode::set_value, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", &ModelNode::set_test_value, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("rung", &ModelNode::rung, "return the rung of the feature\nArgs:\n    cur_rung (int): The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerAddNode()
+{
+    void (AddNode::*set_value_no_param)(int) = &AddNode::set_value;
+    void (AddNode::*set_test_value_no_param)(int) = &AddNode::set_test_value;
+    std::string (AddNode::*expr_no_param)() = &AddNode::expr;
+    Domain (AddNode::*domain_no_param)() = &AddNode::domain;
+
+    class_<AddNode, bases<OperatorNode<2>>>("AddNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the test value for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &AddNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &AddNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerSubNode()
+{
+    void (SubNode::*set_value_no_param)(int) = &SubNode::set_value;
+    void (SubNode::*set_test_value_no_param)(int) = &SubNode::set_test_value;
+    std::string (SubNode::*expr_no_param)() = &SubNode::expr;
+    Domain (SubNode::*domain_no_param)() = &SubNode::domain;
+
+    class_<SubNode, bases<OperatorNode<2>>>("SubNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &SubNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &SubNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerDivNode()
+{
+    void (DivNode::*set_value_no_param)(int) = &DivNode::set_value;
+    void (DivNode::*set_test_value_no_param)(int) = &DivNode::set_test_value;
+    std::string (DivNode::*expr_no_param)() = &DivNode::expr;
+    Domain (DivNode::*domain_no_param)() = &DivNode::domain;
+
+    class_<DivNode, bases<OperatorNode<2>>>("DivNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &DivNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &DivNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerMultNode()
+{
+    void (MultNode::*set_value_no_param)(int) = &MultNode::set_value;
+    void (MultNode::*set_test_value_no_param)(int) = &MultNode::set_test_value;
+    std::string (MultNode::*expr_no_param)() = &MultNode::expr;
+    Domain (MultNode::*domain_no_param)() = &MultNode::domain;
+
+    class_<MultNode, bases<OperatorNode<2>>>("MultNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &MultNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &MultNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerAbsDiffNode()
+{
+    void (AbsDiffNode::*set_value_no_param)(int) = &AbsDiffNode::set_value;
+    void (AbsDiffNode::*set_test_value_no_param)(int) = &AbsDiffNode::set_test_value;
+    std::string (AbsDiffNode::*expr_no_param)() = &AbsDiffNode::expr;
+    Domain (AbsDiffNode::*domain_no_param)() = &AbsDiffNode::domain;
+
+    class_<AbsDiffNode, bases<OperatorNode<2>>>("AbsDiffNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &AbsDiffNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &AbsDiffNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerAbsNode()
+{
+    void (AbsNode::*set_value_no_param)(int) = &AbsNode::set_value;
+    void (AbsNode::*set_test_value_no_param)(int) = &AbsNode::set_test_value;
+    std::string (AbsNode::*expr_no_param)() = &AbsNode::expr;
+    Domain (AbsNode::*domain_no_param)() = &AbsNode::domain;
+
+    class_<AbsNode, bases<OperatorNode<1>>>("AbsNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &AbsNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &AbsNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerInvNode()
+{
+    void (InvNode::*set_value_no_param)(int) = &InvNode::set_value;
+    void (InvNode::*set_test_value_no_param)(int) = &InvNode::set_test_value;
+    std::string (InvNode::*expr_no_param)() = &InvNode::expr;
+    Domain (InvNode::*domain_no_param)() = &InvNode::domain;
+
+    class_<InvNode, bases<OperatorNode<1>>>("InvNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &InvNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &InvNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerLogNode()
+{
+    void (LogNode::*set_value_no_param)(int) = &LogNode::set_value;
+    void (LogNode::*set_test_value_no_param)(int) = &LogNode::set_test_value;
+    std::string (LogNode::*expr_no_param)() = &LogNode::expr;
+    Domain (LogNode::*domain_no_param)() = &LogNode::domain;
+
+    class_<LogNode, bases<OperatorNode<1>>>("LogNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &LogNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &LogNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerExpNode()
+{
+    void (ExpNode::*set_value_no_param)(int) = &ExpNode::set_value;
+    void (ExpNode::*set_test_value_no_param)(int) = &ExpNode::set_test_value;
+    std::string (ExpNode::*expr_no_param)() = &ExpNode::expr;
+    Domain (ExpNode::*domain_no_param)() = &ExpNode::domain;
+
+    class_<ExpNode, bases<OperatorNode<1>>>("ExpNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &ExpNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &ExpNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerNegExpNode()
+{
+    void (NegExpNode::*set_value_no_param)(int) = &NegExpNode::set_value;
+    void (NegExpNode::*set_test_value_no_param)(int) = &NegExpNode::set_test_value;
+    std::string (NegExpNode::*expr_no_param)() = &NegExpNode::expr;
+    Domain (NegExpNode::*domain_no_param)() = &NegExpNode::domain;
+
+    class_<NegExpNode, bases<OperatorNode<1>>>("NegExpNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &NegExpNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &NegExpNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerSinNode()
+{
+    void (SinNode::*set_value_no_param)(int) = &SinNode::set_value;
+    void (SinNode::*set_test_value_no_param)(int) = &SinNode::set_test_value;
+    std::string (SinNode::*expr_no_param)() = &SinNode::expr;
+    Domain (SinNode::*domain_no_param)() = &SinNode::domain;
+
+    class_<SinNode, bases<OperatorNode<1>>>("SinNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &SinNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &SinNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerCosNode()
+{
+    void (CosNode::*set_value_no_param)(int) = &CosNode::set_value;
+    void (CosNode::*set_test_value_no_param)(int) = &CosNode::set_test_value;
+    std::string (CosNode::*expr_no_param)() = &CosNode::expr;
+    Domain (CosNode::*domain_no_param)() = &CosNode::domain;
+
+    class_<CosNode, bases<OperatorNode<1>>>("CosNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &CosNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &CosNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerCbNode()
+{
+    void (CbNode::*set_value_no_param)(int) = &CbNode::set_value;
+    void (CbNode::*set_test_value_no_param)(int) = &CbNode::set_test_value;
+    std::string (CbNode::*expr_no_param)() = &CbNode::expr;
+    Domain (CbNode::*domain_no_param)() = &CbNode::domain;
+
+    class_<CbNode, bases<OperatorNode<1>>>("CbNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &CbNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &CbNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerCbrtNode()
+{
+    void (CbrtNode::*set_value_no_param)(int) = &CbrtNode::set_value;
+    void (CbrtNode::*set_test_value_no_param)(int) = &CbrtNode::set_test_value;
+    std::string (CbrtNode::*expr_no_param)() = &CbrtNode::expr;
+    Domain (CbrtNode::*domain_no_param)() = &CbrtNode::domain;
+
+    class_<CbrtNode, bases<OperatorNode<1>>>("CbrtNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &CbrtNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &CbrtNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerSqNode()
+{
+    void (SqNode::*set_value_no_param)(int) = &SqNode::set_value;
+    void (SqNode::*set_test_value_no_param)(int) = &SqNode::set_test_value;
+    std::string (SqNode::*expr_no_param)() = &SqNode::expr;
+    Domain (SqNode::*domain_no_param)() = &SqNode::domain;
+
+    class_<SqNode, bases<OperatorNode<1>>>("SqNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &SqNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &SqNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerSqrtNode()
+{
+    void (SqrtNode::*set_value_no_param)(int) = &SqrtNode::set_value;
+    void (SqrtNode::*set_test_value_no_param)(int) = &SqrtNode::set_test_value;
+    std::string (SqrtNode::*expr_no_param)() = &SqrtNode::expr;
+    Domain (SqrtNode::*domain_no_param)() = &SqrtNode::domain;
+
+    class_<SqrtNode, bases<OperatorNode<1>>>("SqrtNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &SqrtNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &SqrtNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerSixPowNode()
+{
+    void (SixPowNode::*set_value_no_param)(int) = &SixPowNode::set_value;
+    void (SixPowNode::*set_test_value_no_param)(int) = &SixPowNode::set_test_value;
+    std::string (SixPowNode::*expr_no_param)() = &SixPowNode::expr;
+    Domain (SixPowNode::*domain_no_param)() = &SixPowNode::domain;
+
+    class_<SixPowNode, bases<OperatorNode<1>>>("SixPowNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &SixPowNode::unit, "Get the unit of the feature (combine the units of _feats)")
+        .add_property("rung", &SixPowNode::rung, "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+    ;
+}
+
+void sisso::feature_creation::node::registerAddParamNode()
+{
+    void (AddParamNode::*set_value_no_param)(int) = &AddParamNode::set_value;
+    void (AddParamNode::*set_test_value_no_param)(int) = &AddParamNode::set_test_value;
+    std::string (AddParamNode::*expr_no_param)() = &AddParamNode::expr;
+    Domain (AddParamNode::*domain_no_param)() = &AddParamNode::domain;
+
+    class_<AddParamNode, bases<AddNode>>("AddParamNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &AddParamNode::unit, "")
+        .add_property("rung", &AddParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerSubParamNode()
+{
+    void (SubParamNode::*set_value_no_param)(int) = &SubParamNode::set_value;
+    void (SubParamNode::*set_test_value_no_param)(int) = &SubParamNode::set_test_value;
+    std::string (SubParamNode::*expr_no_param)() = &SubParamNode::expr;
+    Domain (SubParamNode::*domain_no_param)() = &SubParamNode::domain;
+
+    class_<SubParamNode, bases<SubNode>>("SubNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &SubParamNode::unit, "")
+        .add_property("rung", &SubParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerDivParamNode()
+{
+    void (DivParamNode::*set_value_no_param)(int) = &DivParamNode::set_value;
+    void (DivParamNode::*set_test_value_no_param)(int) = &DivParamNode::set_test_value;
+    std::string (DivParamNode::*expr_no_param)() = &DivParamNode::expr;
+    Domain (DivParamNode::*domain_no_param)() = &DivParamNode::domain;
+
+    class_<DivParamNode, bases<DivNode>>("DivNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &DivParamNode::unit, "")
+        .add_property("rung", &DivParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerMultParamNode()
+{
+    void (MultParamNode::*set_value_no_param)(int) = &MultParamNode::set_value;
+    void (MultParamNode::*set_test_value_no_param)(int) = &MultParamNode::set_test_value;
+    std::string (MultParamNode::*expr_no_param)() = &MultParamNode::expr;
+    Domain (MultParamNode::*domain_no_param)() = &MultParamNode::domain;
+
+    class_<MultParamNode, bases<MultNode>>("MultNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &MultParamNode::unit, "")
+        .add_property("rung", &MultParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerAbsDiffParamNode()
+{
+    void (AbsDiffParamNode::*set_value_no_param)(int) = &AbsDiffParamNode::set_value;
+    void (AbsDiffParamNode::*set_test_value_no_param)(int) = &AbsDiffParamNode::set_test_value;
+    std::string (AbsDiffParamNode::*expr_no_param)() = &AbsDiffParamNode::expr;
+    Domain (AbsDiffParamNode::*domain_no_param)() = &AbsDiffParamNode::domain;
+
+    class_<AbsDiffParamNode, bases<AbsDiffNode>>("AbsDiffNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &AbsDiffParamNode::unit, "")
+        .add_property("rung", &AbsDiffParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerAbsParamNode()
+{
+    void (AbsParamNode::*set_value_no_param)(int) = &AbsParamNode::set_value;
+    void (AbsParamNode::*set_test_value_no_param)(int) = &AbsParamNode::set_test_value;
+    std::string (AbsParamNode::*expr_no_param)() = &AbsParamNode::expr;
+    Domain (AbsParamNode::*domain_no_param)() = &AbsParamNode::domain;
+
+    class_<AbsParamNode, bases<AbsNode>>("AbsNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &AbsParamNode::unit, "")
+        .add_property("rung", &AbsParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerInvParamNode()
+{
+    void (InvParamNode::*set_value_no_param)(int) = &InvParamNode::set_value;
+    void (InvParamNode::*set_test_value_no_param)(int) = &InvParamNode::set_test_value;
+    std::string (InvParamNode::*expr_no_param)() = &InvParamNode::expr;
+    Domain (InvParamNode::*domain_no_param)() = &InvParamNode::domain;
+
+    class_<InvParamNode, bases<InvNode>>("InvNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &InvParamNode::unit, "")
+        .add_property("rung", &InvParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerLogParamNode()
+{
+    void (LogParamNode::*set_value_no_param)(int) = &LogParamNode::set_value;
+    void (LogParamNode::*set_test_value_no_param)(int) = &LogParamNode::set_test_value;
+    std::string (LogParamNode::*expr_no_param)() = &LogParamNode::expr;
+    Domain (LogParamNode::*domain_no_param)() = &LogParamNode::domain;
+
+    class_<LogParamNode, bases<LogNode>>("LogNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "")
+        .def("set_test_value", set_test_value_no_param, "")
+        .add_property("expr", expr_no_param, "")
+        .add_property("domain", domain_no_param, "")
+        .add_property("unit", &LogParamNode::unit, "")
+        .add_property("rung", &LogParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerExpParamNode()
+{
+    void (ExpParamNode::*set_value_no_param)(int) = &ExpParamNode::set_value;
+    void (ExpParamNode::*set_test_value_no_param)(int) = &ExpParamNode::set_test_value;
+    std::string (ExpParamNode::*expr_no_param)() = &ExpParamNode::expr;
+    Domain (ExpParamNode::*domain_no_param)() = &ExpParamNode::domain;
+
+    class_<ExpParamNode, bases<ExpNode>>("ExpNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &ExpParamNode::unit, "")
+        .add_property("rung", &ExpParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerNegExpParamNode()
+{
+    void (NegExpParamNode::*set_value_no_param)(int) = &NegExpParamNode::set_value;
+    void (NegExpParamNode::*set_test_value_no_param)(int) = &NegExpParamNode::set_test_value;
+    std::string (NegExpParamNode::*expr_no_param)() = &NegExpParamNode::expr;
+    Domain (NegExpParamNode::*domain_no_param)() = &NegExpParamNode::domain;
+
+    class_<NegExpParamNode, bases<NegExpNode>>("NegExpNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &NegExpParamNode::unit, "")
+        .add_property("rung", &NegExpParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerSinParamNode()
+{
+    void (SinParamNode::*set_value_no_param)(int) = &SinParamNode::set_value;
+    void (SinParamNode::*set_test_value_no_param)(int) = &SinParamNode::set_test_value;
+    std::string (SinParamNode::*expr_no_param)() = &SinParamNode::expr;
+    Domain (SinParamNode::*domain_no_param)() = &SinParamNode::domain;
+
+    class_<SinParamNode, bases<SinNode>>("SinParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &SinParamNode::unit, "")
+        .add_property("rung", &SinParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerCosParamNode()
+{
+    void (CosParamNode::*set_value_no_param)(int) = &CosParamNode::set_value;
+    void (CosParamNode::*set_test_value_no_param)(int) = &CosParamNode::set_test_value;
+    std::string (CosParamNode::*expr_no_param)() = &CosParamNode::expr;
+    Domain (CosParamNode::*domain_no_param)() = &CosParamNode::domain;
+
+    class_<CosParamNode, bases<CosNode>>("CosParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &CosParamNode::unit, "")
+        .add_property("rung", &CosParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerCbParamNode()
+{
+    void (CbParamNode::*set_value_no_param)(int) = &CbParamNode::set_value;
+    void (CbParamNode::*set_test_value_no_param)(int) = &CbParamNode::set_test_value;
+    std::string (CbParamNode::*expr_no_param)() = &CbParamNode::expr;
+    Domain (CbParamNode::*domain_no_param)() = &CbParamNode::domain;
+
+    class_<CbParamNode, bases<CbNode>>("CbParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &CbParamNode::unit, "")
+        .add_property("rung", &CbParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerCbrtParamNode()
+{
+    void (CbrtParamNode::*set_value_no_param)(int) = &CbrtParamNode::set_value;
+    void (CbrtParamNode::*set_test_value_no_param)(int) = &CbrtParamNode::set_test_value;
+    std::string (CbrtParamNode::*expr_no_param)() = &CbrtParamNode::expr;
+    Domain (CbrtParamNode::*domain_no_param)() = &CbrtParamNode::domain;
+
+    class_<CbrtParamNode, bases<CbrtNode>>("CbrtParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &CbrtParamNode::unit, "")
+        .add_property("rung", &CbrtParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerSqParamNode()
+{
+    void (SqParamNode::*set_value_no_param)(int) = &SqParamNode::set_value;
+    void (SqParamNode::*set_test_value_no_param)(int) = &SqParamNode::set_test_value;
+    std::string (SqParamNode::*expr_no_param)() = &SqParamNode::expr;
+    Domain (SqParamNode::*domain_no_param)() = &SqParamNode::domain;
+
+    class_<SqParamNode, bases<SqNode>>("SqParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &SqParamNode::unit, "")
+        .add_property("rung", &SqParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerSqrtParamNode()
+{
+    void (SqrtParamNode::*set_value_no_param)(int) = &SqrtParamNode::set_value;
+    void (SqrtParamNode::*set_test_value_no_param)(int) = &SqrtParamNode::set_test_value;
+    std::string (SqrtParamNode::*expr_no_param)() = &SqrtParamNode::expr;
+    Domain (SqrtParamNode::*domain_no_param)() = &SqrtParamNode::domain;
+
+    class_<SqrtParamNode, bases<SqrtNode>>("SqrtParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &SqrtParamNode::unit, "")
+        .add_property("rung", &SqrtParamNode::rung, "")
+    ;
+}
+
+void sisso::feature_creation::node::registerSixPowParamNode()
+{
+    void (SixPowParamNode::*set_value_no_param)(int) = &SixPowParamNode::set_value;
+    void (SixPowParamNode::*set_test_value_no_param)(int) = &SixPowParamNode::set_test_value;
+    std::string (SixPowParamNode::*expr_no_param)() = &SixPowParamNode::expr;
+    Domain (SixPowParamNode::*domain_no_param)() = &SixPowParamNode::domain;
+
+    class_<SixPowParamNode, bases<SixPowNode>>("SixPowParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .def("set_test_value", set_test_value_no_param, "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+        .add_property("expr", expr_no_param, "Get the expression for the overall feature (From root node down)")
+        .add_property("domain", domain_no_param, "The domain for the feature (min/max values)")
+        .add_property("unit", &SixPowParamNode::unit, "")
+        .add_property("rung", &SixPowParamNode::rung, "")
+    ;
+}
+
+void sisso::descriptor_identifier::registerModel()
+{
+    class_<sisso::descriptor_identifier::Model_Wrap, boost::noncopyable>("Model", no_init)
+        .add_property("n_samp_train", &Model::n_samp_train, "Total number of samples being trained on")
+        .add_property("n_samp_test", &Model::n_samp_test, "Total number of samples being tested")
+        .add_property("n_dim", &Model::n_dim, "The dimensionality of the data")
+        .add_property("prop_train", &Model::prop_train, "The training data property to be learned\n\nReturns:\n     _prop_train as a numpy ndarray")
+        .add_property("prop_test", &Model::prop_test, "The test values for the property\n\nReturns:\n     _prop_test as a numpy ndarray")
+        .add_property("feats", &Model::feats, "The features of the model\n\nReturns:\n     A python list containing all of the features")
+        .add_property("coefs", &Model::coefs, "The coefficient array for the model\n\nReturns:\n     The coefficients as a python list")
+        .add_property("prop_unit", &Model::prop_unit, "The unit of the property\n\nReturns:\n     The unit of the property")
+        .add_property("task_size_train", &Model::task_sizes_train, "@DocString_model_task_sizes_train")
+        .add_property("task_size_test", &Model::task_sizes_test, "@DocString_model_task_sizes_test")
+        .add_property("fix_intercept", &Model::fix_intercept, "@DocString_model_fix_intercept")
+    ;
+}
+
+void sisso::descriptor_identifier::registerModelRegressor()
+{
+    class_<ModelRegressor, bases<Model>>("ModelRegressor", init<Unit, std::vector<double>, std::vector<double>, std::vector<model_node_ptr>, std::vector<int>, std::vector<int>, bool>())
+        .def(init<std::string>())
+        .def(init<std::string, std::string>())
+        .def("__str__", &ModelRegressor::toString, "Convert the model to a string")
+        .def("__repr__", &ModelRegressor::toString, "Convert the model to a string")
+        .add_property("fit", &ModelRegressor::prop_train_est, "The estimation of the property\n\nReturns:\n     _prop_train_est as a numpy ndarray")
+        .add_property("predict", &ModelRegressor::prop_test_est, "The estimation of the properties test values\n\nReturns:\n     _prop_test_est as a numpy ndarray")
+        .add_property("train_error", &ModelRegressor::train_error, "The training error\n\nReturns:\n     _train_error as a numpy ndarray")
+        .add_property("test_error", &ModelRegressor::test_error, "The test error\n\nReturns:\n     _test_error as a numpy ndarray")
+        .add_property("rmse", &ModelRegressor::rmse, "The training rmse of the model")
+        .add_property("test_rmse", &ModelRegressor::test_rmse, "The test rmse of the model")
+        .add_property("max_ae", &ModelRegressor::max_ae, "The max Absolute error of the training data")
+        .add_property("test_max_ae", &ModelRegressor::test_max_ae, "The max Absolute error of the testing data")
+        .add_property("mae", &ModelRegressor::mae, "The mean absolute error of the model\n\nReturns:\n     The mean absolute error of the training data")
+        .add_property("test_mae", &ModelRegressor::test_mae, "The mean absolute test error of the model\n\nReturns:\n     The mean absolute error of the test data")
+        .add_property("mape", &ModelRegressor::mape, "The mean absolute error of the model\n\nReturns:\n     The mean absolute error of the training data")
+        .add_property("test_mape", &ModelRegressor::test_mape, "The mean absolute test error of the model\n\nReturns:\n     The mean absolute error of the test data")
+        .add_property("percentile_25_ae", &ModelRegressor::percentile_25_ae, "The mean absolute error of the model\n\nReturns:\n     The mean absolute error of the training data")
+        .add_property("percentile_25_test_ae", &ModelRegressor::percentile_25_test_ae, "")
+        .add_property("percentile_50_ae", &ModelRegressor::percentile_50_ae, "The mean absolute error of the model\n\nReturns:\n     The mean absolute error of the training data")
+        .add_property("percentile_50_test_ae", &ModelRegressor::percentile_50_test_ae, "")
+        .add_property("percentile_75_ae", &ModelRegressor::percentile_75_ae, "The mean absolute error of the model\n\nReturns:\n     The mean absolute error of the training data")
+        .add_property("percentile_75_test_ae", &ModelRegressor::percentile_75_test_ae, "")
+        .add_property("percentile_95_ae", &ModelRegressor::percentile_95_ae, "The mean absolute error of the model\n\nReturns:\n     The mean absolute error of the training data")
+        .add_property("percentile_95_test_ae", &ModelRegressor::percentile_95_ae, "The mean absolute test error of the model\n\nReturns:\n     The mean absolute error of the test data")
+    ;
+}
+
+void sisso::descriptor_identifier::registerModelClassifier()
+{
+    class_<ModelClassifier, bases<Model>>("ModelClassifier", init<Unit, std::vector<double>, std::vector<double>, std::vector<model_node_ptr>, std::vector<int>, std::vector<int>, bool>())
+        .def(init<std::string>())
+        .def(init<std::string, std::string>())
+        .def(init<ModelClassifier, py::list, np::ndarray, np::ndarray>())
+        .def(init<ModelClassifier, np::ndarray, np::ndarray, np::ndarray>())
+        .def("__str__", &ModelClassifier::toString, "Convert the model to a string")
+        .def("__repr__", &ModelClassifier::toString, "Convert the model to a string")
+        .def("to_file", &ModelClassifier::to_file_py, "Convert the ModelClassifier into an output file\nArgs:\n    filename: The name of the file to output to\n    train: If true output the training data\n    test_inds: The indexes of the test set")
+        .add_property("fit", &ModelClassifier::prop_train_est, "The estimation of the property\n\nReturns:\n     _prop_train_est as a numpy ndarray")
+        .add_property("predict", &ModelClassifier::prop_test_est, "The estimation of the properties test values\n\nReturns:\n     _prop_test_est as a numpy ndarray")
+        .add_property("train_error", &ModelClassifier::train_error, "The training error\n\nReturns:\n     _train_error as a numpy ndarray")
+        .add_property("test_error", &ModelClassifier::test_error, "The test error\n\nReturns:\n     _test_error as a numpy ndarray")
+        .add_property("percent_error", &ModelClassifier::percent_train_error, "Percent of all training samples misclassified")
+        .add_property("percent_test_error", &ModelClassifier::percent_test_error, "Percent of all tset samples misclassified")
+        .add_property("n_convex_overlap_train", &ModelClassifier::n_convex_overlap_train, "")
+        .add_property("n_convex_overlap_test", &ModelClassifier::n_convex_overlap_test, "")
+        .add_property("n_svm_misclassified_train", &ModelClassifier::n_svm_misclassified_train, "")
+        .add_property("n_svm_misclassified_test", &ModelClassifier::n_svm_misclassified_test, "")
+    ;
+}
+
+void sisso::descriptor_identifier::registerSISSO_DI()
+{
+    class_<sisso::descriptor_identifier::SISSO_DI_Wrap, boost::noncopyable>("SISSO_DI", no_init)
+        .add_property("prop", &SISSO_DI::prop_py, "")
+        .add_property("prop_test", &SISSO_DI::prop_test_py, "")
+        .add_property("n_samp", &SISSO_DI::n_samp, "")
+        .add_property("n_dim", &SISSO_DI::n_dim, "")
+        .add_property("n_residual", &SISSO_DI::n_residual, "")
+        .add_property("n_models_store", &SISSO_DI::n_models_store, "")
+        .add_property("feat_space", &SISSO_DI::feat_space, "")
+        .add_property("task_sizes_train", &SISSO_DI::task_sizes_train, "")
+        .add_property("task_sizes_test", &SISSO_DI::task_sizes_test, "")
+    ;
+}
+
+void sisso::descriptor_identifier::registerSISSORegressor()
+{
+    class_<SISSORegressor, bases<SISSO_DI>>("SISSORegressor", init<std::shared_ptr<FeatureSpace>, Unit, np::ndarray, np::ndarray, py::list, py::list, py::list, int, int, int, optional<bool>>())
+        .def("fit", &SISSORegressor::fit, "Perform SISSO to generate the models\nIteratively pefrom SISSO on the Feature space and property until the model dimensionality is equal to _n_dim")
+        .def(init<std::shared_ptr<FeatureSpace>, Unit, py::list, py::list, py::list, py::list, py::list, int, int, int, optional<bool>>())
+        .add_property("models", &SISSORegressor::models_py, "The selected models (cpp definition in <python/descriptor_identifier/SISSO_DI.cpp)\n\nReturns:\n     models as a python list")
+    ;
+}
+
+void sisso::descriptor_identifier::registerSISSOClassifier()
+{
+    class_<SISSOClassifier, bases<SISSO_DI>>("SISSOClassifier", init<std::shared_ptr<FeatureSpace>, Unit, np::ndarray, np::ndarray, py::list, py::list, py::list, int, int, int>())
+        .def(init<std::shared_ptr<FeatureSpace>, Unit, py::list, py::list, py::list, py::list, py::list, int, int, int>())
+        .def("fit", &SISSOClassifier::fit, "Perform SISSO to generate the models\nIteratively pefrom SISSO on the Feature space and property until the model dimensionality is equal to _n_dim")
+        .add_property("models", &SISSOClassifier::models_py, "The selected models (cpp definition in <python/descriptor_identifier/SISSO_DI.cpp)\n\nReturns:\n     models as a python list")
+    ;
+}
diff --git a/src/python/bindings.hpp b/src/python/bindings.hpp
new file mode 100644
index 00000000..42626a88
--- /dev/null
+++ b/src/python/bindings.hpp
@@ -0,0 +1,378 @@
+/** @file python/bindings.hpp
+ *  @brief Definitions to convert C++ classes into python classes
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef PYTHON_BINDING
+#define PYTHON_BINDINGS
+
+#include <descriptor_identifier/SISSO_DI/SISSO_DI.hpp>
+#include <descriptor_identifier/SISSO_DI/SISSORegressor.hpp>
+#include <descriptor_identifier/SISSO_DI/SISSOClassifier.hpp>
+#include <feature_creation/feature_space/FeatureSpace.hpp>
+#include <python/feature_creation/node_utils.hpp>
+
+namespace py = boost::python;
+namespace np = boost::python::numpy;
+
+namespace sisso
+{
+    /**
+     * @brief Register all C++ classes as python classes
+     * @details [long description]
+     */
+    void register_all();
+
+    namespace feature_creation
+    {
+        static void registerFeatureSpace();
+        static void registerDomain();
+        static void registerUnit();
+        namespace node
+        {
+            /**
+             * @brief struct used wrap a Node object for conversion
+             */
+            struct NodeWrap :  Node, py::wrapper<Node>
+            {
+            public:
+                inline std::string expr(){return this->get_override("expr")();}
+                inline std::string expr(double*){return this->get_override("expr")();}
+                inline Unit unit(){return this->get_override("unit")();}
+                inline std::vector<double> value(){return this->get_override("value")();}
+                inline std::vector<double> test_value(){return this->get_override("test_value")();}
+                inline void set_value(int offset = -1){this->get_override("set_value")();}
+                inline void set_test_value(int offset = -1){this->get_override("set_test_value")();}
+                inline double* value_ptr(int offset = -1){return this->get_override("value_ptr")();}
+                inline double* test_value_ptr(int offset = -1){return this->get_override("test_value_ptr")();}
+                inline void set_value(double* params, int offset = -1){this->get_override("set_value")();}
+                inline void set_test_value(double* params, int offset = -1){this->get_override("set_test_value")();}
+                inline double* value_ptr(double* params, int offset = -1){return this->get_override("value_ptr")();}
+                inline double* test_value_ptr(double* params, int offset = -1){return this->get_override("test_value_ptr")();}
+                inline bool is_nan(){return this->get_override("is_nan")();}
+                inline bool is_const(){return this->get_override("is_const")();}
+                inline NODE_TYPE type(){return this->get_override("type")();}
+                inline int rung(int cur_rung = 0){return this->get_override("rung")();}
+                inline std::map<int, int> primary_feature_decomp(){return this->get_override("primary_feature_decomp")();}
+                inline void update_primary_feature_decomp(std::map<int, int>& pf_decomp){this->get_override("update_primary_feature_decomp")();}
+                inline void update_postfix(std::string& cur_expr){this->get_override("update_postfix")();}
+                inline std::string get_postfix_term(){return this->get_override("get_postfix_term")();}
+                inline void update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot){this->get_override("update_add_sub_leaves")();}
+                inline void update_div_mult_leaves(std::map<std::string, double>& div_mult_leaves, double fact, double& expected_abs_tot){this->get_override("update_div_mult_leaves")();}
+                inline std::vector<double> parameters(){return this->get_override("parameters")();}
+                inline void set_parameters(std::vector<double>){this->get_override("set_parameters")();}
+                inline Domain domain(){return this->get_override("domain")();}
+                inline Domain domain(double* params){return this->get_override("domain")();}
+                inline int n_feats(){this->get_override("n_feats");}
+                inline std::shared_ptr<Node> feat(int ind){this->get_override("feat");}
+            };
+            /**
+             * @brief struct used wrap an OperatorNode object for conversion
+             */
+            template<int N>
+            struct OperatorNodeWrap : OperatorNode<N>, py::wrapper<OperatorNode<N>>
+            {
+                inline void set_value(int offset = -1){this->get_override("set_value")();}
+                inline void set_test_value(int offset = -1){this->get_override("set_test_value")();}
+                inline void set_value(const double* params, int offset = -1){this->get_override("set_value")();}
+                inline void set_test_value(const double* params, int offset = -1){this->get_override("set_test_value")();}
+                inline NODE_TYPE type(){return this->get_override("type")();}
+                inline int rung(int cur_rung = 0){return this->get_override("rung")();}
+                inline Unit unit(){return this->get_override("unit")();}
+                inline std::string get_postfix_term(){return this->get_override("get_postfix_term")();}
+                inline Domain domain(double* params){return this->get_override("domain")();}
+                inline Domain domain(){return this->get_override("domain")();}
+                inline std::string expr(double* params){return this->get_override("expr")();}
+                inline std::string expr(){return this->get_override("expr")();}
+                inline void update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot){this->get_override("update_add_sub_leaves")();}
+                inline void update_div_mult_leaves(std::map<std::string, double>& div_mult_leaves, double fact, double& expected_abs_tot){this->get_override("update_div_mult_leaves")();}
+                inline void get_parameters(std::vector<double>& prop){this->get_override("get_parameters")();}
+                inline void set_parameters(std::vector<double>){this->get_override("set_parameters")();}
+                inline std::vector<double> parameters(){return this->get_override("parameters")();}
+            };
+
+            /**
+             * @brief Register the Node for accessing the object via python
+             */
+            static void registerNode();
+
+            /**
+             * @brief Register the FeatureNode for accessing the object via python
+             */
+            static void registerFeatureNode();
+
+            /**
+             * @brief Register the ModelNode for accessing the object via python
+             */
+            static void registerModelNode();
+
+            #ifndef PARAMETERIZE
+                /**
+                 * @brief Register the OperatorNode for accessing the object via python
+                 */
+                template<int N>
+                static void registerOperatorNode()
+                {
+                    py::class_<OperatorNodeWrap<N>, py::bases<Node>, boost::noncopyable>("OperatorNode")
+                        .def("is_nan", &OperatorNode<N>::is_nan, "Check if the feature contains NaN")
+                        .def("is_const", &OperatorNode<N>::is_const, "Check if feature is constant")
+                        .def("set_value", py::pure_virtual(&OperatorNode<N>::set_value), "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+                        .def("set_test_value", py::pure_virtual(&OperatorNode<N>::set_test_value), "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+                        .def("rung", py::pure_virtual(&OperatorNode<N>::rung), "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+                        .def("expr", py::pure_virtual(&OperatorNode<N>::expr), "Get the expression for the overall feature (From root node down)")
+                        .def("unit", py::pure_virtual(&OperatorNode<N>::unit), "Get the unit of the feature (combine the units of _feats)")
+                        .def("domain", pure_virtual(&OperatorNode<N>::domain), "The domain for the feature (min/max values)")
+                        .add_property("n_feats", &OperatorNode<N>::n_feats, "")
+                        .add_property("feat", &OperatorNode<N>::feat, "")
+                    ;
+                }
+            #else
+                /**
+                 * @brief Register the OperatorNode for accessing the object via python
+                 */
+                template<int N>
+                static void registerOperatorNode()
+                {
+                    void (OperatorNode<N>::*get_parameters_list)(py::list) = &OperatorNode<N>::get_parameters;
+                    void (OperatorNode<N>::*get_parameters_arr)(np::ndarray) = &OperatorNode<N>::get_parameters;
+                    void (OperatorNode<N>::*set_params_list)(py::list) = &OperatorNode<N>::set_parameters;
+                    void (OperatorNode<N>::*set_params_arr)(np::ndarray) = &OperatorNode<N>::set_parameters;
+                    py::class_<OperatorNodeWrap<N>, py::bases<Node>, boost::noncopyable>("OperatorNode")
+                        .def("is_nan", &OperatorNode<N>::is_nan, "Check if the feature contains NaN")
+                        .def("is_const", &OperatorNode<N>::is_const, "Check if feature is constant")
+                        // .def("set_value", py::pure_virtual(&OperatorNode<N>::set_value), "Set the values of the training data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+                        // .def("set_test_value", py::pure_virtual(&OperatorNode<N>::set_test_value), "Set the values of the test data for the feature inside of the value storage arrays\nArgs:\n    offset (int): Key to determine which part of the temporary storage array to look into")
+                        .def("rung", py::pure_virtual(&OperatorNode<N>::rung), "return the rung of the feature\nArgs:\n    cur_rung: The rung current rung of the feature tree (used to recursively calculate rung)")
+                        // .def("expr", py::pure_virtual(&OperatorNode<N>::expr), "Get the expression for the overall feature (From root node down)")
+                        .def("unit", py::pure_virtual(&OperatorNode<N>::unit), "Get the unit of the feature (combine the units of _feats)")
+                        .def("get_parameters", get_parameters_arr, "Solve the non-linear optimization to set the parameters\nFits the data points from _feats->value_ptr and prop to get the parameters for the feature\nArgs:\n    prop: property to fit to get the parameters\n    param_list: List describing the parameters to fit")
+                        .def("get_parameters", get_parameters_list, "Solve the non-linear optimization to set the parameters\nFits the data points from _feats->value_ptr and prop to get the parameters for the feature\nArgs:\n    prop: property to fit to get the parameters\n    param_list: List describing the parameters to fit")
+                        .def("set_parameters", set_params_arr, "Set the non-linear parameters")
+                        .def("set_parameters", set_params_list, "Set the non-linear parameters")
+                        .add_property("n_feats", &OperatorNode<N>::n_feats, "")
+                        .add_property("feat", &OperatorNode<N>::feat, "")
+                    ;
+                }
+            #endif
+
+            /**
+             * @brief Register the AddNode object for conversion to a python object
+             */
+            static void registerAddNode();
+
+            /**
+             * @brief Register the SubNode object for conversion to a python object
+             */
+            static void registerSubNode();
+
+            /**
+             * @brief Register the DivNode object for conversion to a python object
+             */
+            static void registerDivNode();
+
+            /**
+             * @brief Register the MultNode object for conversion to a python object
+             */
+            static void registerMultNode();
+
+            /**
+             * @brief Register the AbsDiffNode object for conversion to a python object
+             */
+            static void registerAbsDiffNode();
+
+            /**
+             * @brief Register the AbsNode object for conversion to a python object
+             */
+            static void registerAbsNode();
+
+            /**
+             * @brief Register the InvNode object for conversion to a python object
+             */
+            static void registerInvNode();
+
+            /**
+             * @brief Register the LogNode object for conversion to a python object
+             */
+            static void registerLogNode();
+
+            /**
+             * @brief Register the ExpNode object for conversion to a python object
+             */
+            static void registerExpNode();
+
+            /**
+             * @brief Register the NegExpNode object for conversion to a python object
+             */
+            static void registerNegExpNode();
+
+            /**
+             * @brief Register the SinNode object for conversion to a python object
+             */
+            static void registerSinNode();
+
+            /**
+             * @brief Register the CosNode object for conversion to a python object
+             */
+            static void registerCosNode();
+
+            /**
+             * @brief Register the CbNode object for conversion to a python object
+             */
+            static void registerCbNode();
+
+            /**
+             * @brief Register the CbrtNode object for conversion to a python object
+             */
+            static void registerCbrtNode();
+
+            /**
+             * @brief Register the SqNode object for conversion to a python object
+             */
+            static void registerSqNode();
+
+            /**
+             * @brief Register the SqrtNode object for conversion to a python object
+             */
+            static void registerSqrtNode();
+
+            /**
+             * @brief Register the SixPowNode object for conversion to a python object
+             */
+            static void registerSixPowNode();
+
+            /**
+             * @brief Register the AddParamNode object for conversion to a python object
+             */
+            static void registerAddParamNode();
+
+            /**
+             * @brief Register the SubParamNode object for conversion to a python object
+             */
+            static void registerSubParamNode();
+
+            /**
+             * @brief Register the DivParamNode object for conversion to a python object
+             */
+            static void registerDivParamNode();
+
+            /**
+             * @brief Register the MultParamNode object for conversion to a python object
+             */
+            static void registerMultParamNode();
+
+            /**
+             * @brief Register the AbsDiffParamNode object for conversion to a python object
+             */
+            static void registerAbsDiffParamNode();
+
+            /**
+             * @brief Register the AbsParamNode object for conversion to a python object
+             */
+            static void registerAbsParamNode();
+
+            /**
+             * @brief Register the InvParamNode object for conversion to a python object
+             */
+            static void registerInvParamNode();
+
+            /**
+             * @brief Register the LogParamNode object for conversion to a python object
+             */
+            static void registerLogParamNode();
+
+            /**
+             * @brief Register the ExpParamNode object for conversion to a python object
+             */
+            static void registerExpParamNode();
+
+            /**
+             * @brief Register the NegExpParamNode object for conversion to a python object
+             */
+            static void registerNegExpParamNode();
+
+            /**
+             * @brief Register the SinParamNode object for conversion to a python object
+             */
+            static void registerSinParamNode();
+
+            /**
+             * @brief Register the CosParamNode object for conversion to a python object
+             */
+            static void registerCosParamNode();
+
+            /**
+             * @brief Register the CbParamNode object for conversion to a python object
+             */
+            static void registerCbParamNode();
+
+            /**
+             * @brief Register the CbrtParamNode object for conversion to a python object
+             */
+            static void registerCbrtParamNode();
+
+            /**
+             * @brief Register the SqParamNode object for conversion to a python object
+             */
+            static void registerSqParamNode();
+
+            /**
+             * @brief Register the SqrtParamNode object for conversion to a python object
+             */
+            static void registerSqrtParamNode();
+
+            /**
+             * @brief Register the SixPowParamNode object for conversion to a python object
+             */
+            static void registerSixPowParamNode();
+
+        }
+    }
+
+    namespace descriptor_identifier
+    {
+        struct SISSO_DI_Wrap : SISSO_DI, py::wrapper<SISSO_DI>
+        {
+            inline void l0_norm(){this->get_override("l0_norm")();}
+            inline void fit(){this->get_override("fit")();}
+        };
+
+        struct Model_Wrap : Model, py::wrapper<Model>
+        {
+            inline void to_file(std::string filename, bool train = true, std::vector<int> test_inds = {}){this->get_override("to_file")();}
+        };
+
+        /**
+         * @brief Register the Model object for conversion to a python object
+         */
+        static void registerModel();
+
+        /**
+         * @brief Register the Model object for conversion to a python object
+         */
+        static void registerModelRegressor();
+
+        /**
+         * @brief Register the Model object for conversion to a python object
+         */
+        static void registerModelClassifier();
+
+        /**
+         * @brief Register the SISSORegressor object for conversion to a python object
+         */
+        static void registerSISSO_DI();
+
+        /**
+         * @brief Register the SISSORegressor object for conversion to a python object
+         */
+        static void registerSISSORegressor();
+
+
+        /**
+         * @brief Register the SISSORegressor object for conversion to a python object
+         */
+        static void registerSISSOClassifier();
+    }
+}
+
+#endif
diff --git a/src/python/bindings_docstring_keyed.cpp b/src/python/bindings_docstring_keyed.cpp
index df2e8e86..ebb02370 100644
--- a/src/python/bindings_docstring_keyed.cpp
+++ b/src/python/bindings_docstring_keyed.cpp
@@ -46,8 +46,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, dict, np::ndarray, list, optional<std::string, int, int, int, int, double, double, double>>())
-        .def(init<list, list, dict, 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>>())
+        .def(init<list, list, list, list, list, optional<std::string, int, int, int, int, double, double, double>>())
         .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@")
@@ -94,9 +94,6 @@ void sisso::feature_creation::registerUnit()
 
 void sisso::feature_creation::registerDomain()
 {
-    Domain (Domain::*abs_parameters)(double, double) = &Domain::abs;
-    Domain (Domain::*abs_bare)() = &Domain::abs;
-
     class_<Domain>("Domain", init<>())
         .def(init<std::string>())
         .def(init<double, double>())
@@ -105,7 +102,6 @@ void sisso::feature_creation::registerDomain()
         .def("__str__", &Domain::toString, "@DocString_domain_str@")
         .def("__repr__", &Domain::toString, "@DocString_domain_str@")
         .def("__pow__", &Domain::operator^, "@DocString_domain_pow@")
-        .def("__abs__", abs_bare, "@DocString_domain_abs@")
         .def("inv", &Domain::inv, "@DocString_domain_inv@")
         .def("exp", &Domain::exp, "@DocString_domain_exp@")
         .def("log", &Domain::log, "@DocString_domain_log@")
@@ -114,7 +110,7 @@ void sisso::feature_creation::registerDomain()
         .def("mult", &Domain::mult, "@DocString_domain_mult@")
         .def("div", &Domain::div, "@DocString_domain_div@")
         .def("pow", &Domain::pow, "@DocString_domain_pow@")
-        .def("abs", abs_parameters, "@DocString_domain_abs_parameters@")
+        .def("abs", &Domain::abs, "@DocString_domain_abs_parameters@")
         .def("__contains__", &Domain::contains, "@DocString_domain_contains@")
         .def(self + self)
         .def(self - self)
@@ -140,11 +136,13 @@ void sisso::feature_creation::registerDomain()
     {
         void (Node::*reindex_1)(int) = &Node::reindex;
         void (Node::*reindex_2)(int, int) = &Node::reindex;
+        // void (Node::*expr_no_param)() = &Node::expr;
+
         class_<sisso::feature_creation::node::NodeWrap, boost::noncopyable>("Node", no_init)
             .def("reindex", reindex_1, "@DocString_node_reindex_1@")
             .def("reindex", reindex_2, "@DocString_node_reindex_2@")
-            .def("__str__", &Node::expr, "@DocString_node_expr@")
-            .def("__repr__", &Node::expr, "@DocString_node_expr@")
+            // .def("__str__", expr_no_param, "@DocString_node_expr@")
+            // .def("__repr__", expr_no_param, "@DocString_node_expr@")
             .add_property("n_samp", &Node::n_samp, "@DocString_node_n_samp@")
             .add_property("n_test_samp", &Node::n_test_samp, "@DocString_node_n_test_samp@")
             .add_property("feat_ind", &Node::feat_ind, "@DocString_node_feat_ind@")
@@ -156,11 +154,11 @@ void sisso::feature_creation::registerDomain()
             .add_property("primary_feat_decomp", &Node::primary_feature_decomp_py, "@DocString_node_primary_feature_decomp@")
             .add_property("postfix_expr", &Node::postfix_expr, "@DocString_node_postfix_expr@")
             .add_property("parameters", &Node::parameters_py, "@DocString_node_parameters_py@")
-            .def("domain", pure_virtual(&Node::domain), "@DocString_node_domain@")
-            .def("expr", pure_virtual(&Node::expr), "@DocString_node_expr@")
+            // .def("domain", pure_virtual(&Node::domain), "@DocString_node_domain@")
+            // .def("expr", pure_virtual(expr_no_param), "@DocString_node_expr@")
             .def("unit", pure_virtual(&Node::unit), "@DocString_node_unit@")
-            .def("set_value", pure_virtual(&Node::set_value), "@DocString_node_set_value@")
-            .def("set_test_value", pure_virtual(&Node::set_test_value), "@DocString_node_set_test_value@")
+            // .def("set_value", pure_virtual(&Node::set_value), "@DocString_node_set_value@")
+            // .def("set_test_value", pure_virtual(&Node::set_test_value), "@DocString_node_set_test_value@")
             .def("is_nan", pure_virtual(&Node::is_nan), "@DocString_node_is_nan@")
             .def("is_const", pure_virtual(&Node::is_const), "@DocString_node_is_const@")
             .def("rung", pure_virtual(&Node::rung), "@DocString_node_rung@")
@@ -176,8 +174,8 @@ void sisso::feature_creation::registerDomain()
         class_<sisso::feature_creation::node::NodeWrap, boost::noncopyable>("Node", no_init)
             .def("reindex", reindex_1, "@DocString_node_reindex_1@")
             .def("reindex", reindex_2, "@DocString_node_reindex_2@")
-            .def("__str__", &Node::expr, "@DocString_node_expr@")
-            .def("__repr__", &Node::expr, "@DocString_node_expr@")
+            // .def("__str__", &Node::expr, "@DocString_node_expr@")
+            // .def("__repr__", &Node::expr, "@DocString_node_expr@")
             .add_property("n_samp", &Node::n_samp, "@DocString_node_n_samp@")
             .add_property("n_test_samp", &Node::n_test_samp, "@DocString_node_n_test_samp@")
             .add_property("feat_ind", &Node::feat_ind, "@DocString_node_feat_ind@")
@@ -188,10 +186,10 @@ void sisso::feature_creation::registerDomain()
             .add_property("test_value", &Node::test_value_py, "@DocString_node_test_value_py@")
             .add_property("primary_feat_decomp", &Node::primary_feature_decomp_py, "@DocString_node_primary_feature_decomp@")
             .add_property("postfix_expr", &Node::postfix_expr, "@DocString_node_postfix_expr@")
-            .def("expr", pure_virtual(&Node::expr), "@DocString_node_expr@")
+            // .def("expr", pure_virtual(&Node::expr), "@DocString_node_expr@")
             .def("unit", pure_virtual(&Node::unit), "@DocString_node_unit@")
-            .def("set_value", pure_virtual(&Node::set_value), "@DocString_node_set_value@")
-            .def("set_test_value", pure_virtual(&Node::set_test_value), "@DocString_node_set_test_value@")
+            // .def("set_value", pure_virtual(&Node::set_value), "@DocString_node_set_value@")
+            // .def("set_test_value", pure_virtual(&Node::set_test_value), "@DocString_node_set_test_value@")
             .def("is_nan", pure_virtual(&Node::is_nan), "@DocString_node_is_nan@")
             .def("is_const", pure_virtual(&Node::is_const), "@DocString_node_is_const@")
             .def("rung", pure_virtual(&Node::rung), "@DocString_node_rung@")
@@ -205,20 +203,22 @@ void sisso::feature_creation::node::registerFeatureNode()
 {
     std::string (FeatureNode::*expr_1)() = &FeatureNode::expr;
     std::string (FeatureNode::*expr_const)() const = &FeatureNode::expr;
+    void (FeatureNode::*set_value_no_param)(int) = &FeatureNode::set_value;
+    void (FeatureNode::*set_test_value_no_param)(int) = &FeatureNode::set_test_value;
+    Domain (FeatureNode::*domain_no_param)() = &FeatureNode::domain;
 
     using namespace boost::python;
     class_<FeatureNode, bases<Node>>("FeatureNode", init<int, std::string, np::ndarray, np::ndarray, Domain, Unit>())
         .def(init<int, std::string, py::list, py::list, Domain, Unit>())
         .def("is_nan", &FeatureNode::is_nan, "@DocString_feat_node_is_nan@")
         .def("is_const", &FeatureNode::is_const, "@DocString_feat_node_is_const@")
-        .def("set_value", &FeatureNode::set_value, "@DocString_feat_node_set_value@")
-        .def("set_test_value", &FeatureNode::set_test_value, "@DocString_feat_node_set_test_value@")
+        .def("set_value", set_value_no_param, "@DocString_feat_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_feat_node_set_test_value@")
         .add_property("expr", expr_1, "@DocString_feat_node_expr_1@")
         .add_property("expr", expr_const, "@DocString_feat_node_expr_const@")
-        .add_property("domain", &FeatureNode::domain, "@DocString_feat_node_domain@")
+        .add_property("domain", domain_no_param, "@DocString_feat_node_domain@")
         .add_property("unit", &FeatureNode::unit, "@DocString_feat_node_unit@")
         .add_property("rung", &FeatureNode::rung, "@DocString_feat_node_rung@")
-        .add_property("domain", &FeatureNode::domain, "@DocString_feat_node_domain@")
     ;
 }
 
@@ -239,220 +239,579 @@ void sisso::feature_creation::node::registerModelNode()
 
 void sisso::feature_creation::node::registerAddNode()
 {
+    void (AddNode::*set_value_no_param)(int) = &AddNode::set_value;
+    void (AddNode::*set_test_value_no_param)(int) = &AddNode::set_test_value;
+    std::string (AddNode::*expr_no_param)() = &AddNode::expr;
+    Domain (AddNode::*domain_no_param)() = &AddNode::domain;
+
     class_<AddNode, bases<OperatorNode<2>>>("AddNode", init<node_ptr, node_ptr, int, double, double>())
-        .def("set_value", &AddNode::set_value, "@DocString_add_node_set_value@")
-        .def("set_test_value", &AddNode::set_test_value, "@DocString_add_node_set_test_value@")
-        .add_property("expr", &AddNode::expr, "@DocString_add_node_expr@")
-        .add_property("bounds", &AddNode::domain, "@DocString_add_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_add_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_add_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_add_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_add_node_domain@")
         .add_property("unit", &AddNode::unit, "@DocString_add_node_unit@")
         .add_property("rung", &AddNode::rung, "@DocString_add_node_rung@")
-        .add_property("domain", &AddNode::domain, "@DocString_add_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerSubNode()
 {
+    void (SubNode::*set_value_no_param)(int) = &SubNode::set_value;
+    void (SubNode::*set_test_value_no_param)(int) = &SubNode::set_test_value;
+    std::string (SubNode::*expr_no_param)() = &SubNode::expr;
+    Domain (SubNode::*domain_no_param)() = &SubNode::domain;
+
     class_<SubNode, bases<OperatorNode<2>>>("SubNode", init<node_ptr, node_ptr, int, double, double>())
-        .def("set_value", &SubNode::set_value, "@DocString_sub_node_set_value@")
-        .def("set_test_value", &SubNode::set_test_value, "@DocString_sub_node_set_test_value@")
-        .add_property("expr", &SubNode::expr, "@DocString_sub_node_expr@")
-        .add_property("bounds", &SubNode::domain, "@DocString_sub_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_sub_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_sub_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_sub_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_sub_node_domain@")
         .add_property("unit", &SubNode::unit, "@DocString_sub_node_unit@")
         .add_property("rung", &SubNode::rung, "@DocString_sub_node_rung@")
-        .add_property("domain", &SubNode::domain, "@DocString_sub_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerDivNode()
 {
+    void (DivNode::*set_value_no_param)(int) = &DivNode::set_value;
+    void (DivNode::*set_test_value_no_param)(int) = &DivNode::set_test_value;
+    std::string (DivNode::*expr_no_param)() = &DivNode::expr;
+    Domain (DivNode::*domain_no_param)() = &DivNode::domain;
+
     class_<DivNode, bases<OperatorNode<2>>>("DivNode", init<node_ptr, node_ptr, int, double, double>())
-        .def("set_value", &DivNode::set_value, "@DocString_div_node_set_value@")
-        .def("set_test_value", &DivNode::set_test_value, "@DocString_div_node_set_test_value@")
-        .add_property("expr", &DivNode::expr, "@DocString_div_node_expr@")
-        .add_property("bounds", &DivNode::domain, "@DocString_div_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_div_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_div_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_div_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_div_node_domain@")
         .add_property("unit", &DivNode::unit, "@DocString_div_node_unit@")
         .add_property("rung", &DivNode::rung, "@DocString_div_node_rung@")
-        .add_property("domain", &DivNode::domain, "@DocString_div_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerMultNode()
 {
+    void (MultNode::*set_value_no_param)(int) = &MultNode::set_value;
+    void (MultNode::*set_test_value_no_param)(int) = &MultNode::set_test_value;
+    std::string (MultNode::*expr_no_param)() = &MultNode::expr;
+    Domain (MultNode::*domain_no_param)() = &MultNode::domain;
+
     class_<MultNode, bases<OperatorNode<2>>>("MultNode", init<node_ptr, node_ptr, int, double, double>())
-        .def("set_value", &MultNode::set_value, "@DocString_mult_node_set_value@")
-        .def("set_test_value", &MultNode::set_test_value, "@DocString_mult_node_set_test_value@")
-        .add_property("expr", &MultNode::expr, "@DocString_mult_node_expr@")
-        .add_property("bounds", &MultNode::domain, "@DocString_mult_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_mult_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_mult_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_mult_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_mult_node_domain@")
         .add_property("unit", &MultNode::unit, "@DocString_mult_node_unit@")
         .add_property("rung", &MultNode::rung, "@DocString_mult_node_rung@")
-        .add_property("domain", &MultNode::domain, "@DocString_mult_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerAbsDiffNode()
 {
+    void (AbsDiffNode::*set_value_no_param)(int) = &AbsDiffNode::set_value;
+    void (AbsDiffNode::*set_test_value_no_param)(int) = &AbsDiffNode::set_test_value;
+    std::string (AbsDiffNode::*expr_no_param)() = &AbsDiffNode::expr;
+    Domain (AbsDiffNode::*domain_no_param)() = &AbsDiffNode::domain;
+
     class_<AbsDiffNode, bases<OperatorNode<2>>>("AbsDiffNode", init<node_ptr, node_ptr, int, double, double>())
-        .def("set_value", &AbsDiffNode::set_value, "@DocString_abs_diff_node_set_value@")
-        .def("set_test_value", &AbsDiffNode::set_test_value, "@DocString_abs_diff_node_set_test_value@")
-        .add_property("expr", &AbsDiffNode::expr, "@DocString_abs_diff_node_expr@")
-        .add_property("bounds", &AbsDiffNode::domain, "@DocString_abs_diff_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_abs_diff_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_abs_diff_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_abs_diff_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_abs_diff_node_domain@")
         .add_property("unit", &AbsDiffNode::unit, "@DocString_abs_diff_node_unit@")
         .add_property("rung", &AbsDiffNode::rung, "@DocString_abs_diff_node_rung@")
-        .add_property("domain", &AbsDiffNode::domain, "@DocString_abs_diff_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerAbsNode()
 {
+    void (AbsNode::*set_value_no_param)(int) = &AbsNode::set_value;
+    void (AbsNode::*set_test_value_no_param)(int) = &AbsNode::set_test_value;
+    std::string (AbsNode::*expr_no_param)() = &AbsNode::expr;
+    Domain (AbsNode::*domain_no_param)() = &AbsNode::domain;
+
     class_<AbsNode, bases<OperatorNode<1>>>("AbsNode", init<node_ptr, int, double, double>())
-        .def("set_value", &AbsNode::set_value, "@DocString_abs_node_set_value@")
-        .def("set_test_value", &AbsNode::set_test_value, "@DocString_abs_node_set_test_value@")
-        .add_property("expr", &AbsNode::expr, "@DocString_abs_node_expr@")
-        .add_property("bounds", &AbsNode::domain, "@DocString_abs_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_abs_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_abs_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_abs_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_abs_node_domain@")
         .add_property("unit", &AbsNode::unit, "@DocString_abs_node_unit@")
         .add_property("rung", &AbsNode::rung, "@DocString_abs_node_rung@")
-        .add_property("domain", &AbsNode::domain, "@DocString_abs_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerInvNode()
 {
+    void (InvNode::*set_value_no_param)(int) = &InvNode::set_value;
+    void (InvNode::*set_test_value_no_param)(int) = &InvNode::set_test_value;
+    std::string (InvNode::*expr_no_param)() = &InvNode::expr;
+    Domain (InvNode::*domain_no_param)() = &InvNode::domain;
+
     class_<InvNode, bases<OperatorNode<1>>>("InvNode", init<node_ptr, int, double, double>())
-        .def("set_value", &InvNode::set_value, "@DocString_inv_node_set_value@")
-        .def("set_test_value", &InvNode::set_test_value, "@DocString_inv_node_set_test_value@")
-        .add_property("expr", &InvNode::expr, "@DocString_inv_node_expr@")
-        .add_property("bounds", &InvNode::domain, "@DocString_inv_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_inv_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_inv_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_inv_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_inv_node_domain@")
         .add_property("unit", &InvNode::unit, "@DocString_inv_node_unit@")
         .add_property("rung", &InvNode::rung, "@DocString_inv_node_rung@")
-        .add_property("domain", &InvNode::domain, "@DocString_inv_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerLogNode()
 {
+    void (LogNode::*set_value_no_param)(int) = &LogNode::set_value;
+    void (LogNode::*set_test_value_no_param)(int) = &LogNode::set_test_value;
+    std::string (LogNode::*expr_no_param)() = &LogNode::expr;
+    Domain (LogNode::*domain_no_param)() = &LogNode::domain;
+
     class_<LogNode, bases<OperatorNode<1>>>("LogNode", init<node_ptr, int, double, double>())
-        .def("set_value", &LogNode::set_value, "@DocString_log_node_set_value@")
-        .def("set_test_value", &LogNode::set_test_value, "@DocString_log_node_set_test_value@")
-        .add_property("expr", &LogNode::expr, "@DocString_log_node_expr@")
-        .add_property("bounds", &LogNode::domain, "@DocString_log_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_log_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_log_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_log_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_log_node_domain@")
         .add_property("unit", &LogNode::unit, "@DocString_log_node_unit@")
         .add_property("rung", &LogNode::rung, "@DocString_log_node_rung@")
-        .add_property("domain", &LogNode::domain, "@DocString_log_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerExpNode()
 {
+    void (ExpNode::*set_value_no_param)(int) = &ExpNode::set_value;
+    void (ExpNode::*set_test_value_no_param)(int) = &ExpNode::set_test_value;
+    std::string (ExpNode::*expr_no_param)() = &ExpNode::expr;
+    Domain (ExpNode::*domain_no_param)() = &ExpNode::domain;
+
     class_<ExpNode, bases<OperatorNode<1>>>("ExpNode", init<node_ptr, int, double, double>())
-        .def("set_value", &ExpNode::set_value, "@DocString_exp_node_set_value@")
-        .def("set_test_value", &ExpNode::set_test_value, "@DocString_exp_node_set_test_value@")
-        .add_property("expr", &ExpNode::expr, "@DocString_exp_node_expr@")
-        .add_property("bounds", &ExpNode::domain, "@DocString_exp_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_exp_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_exp_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_exp_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_exp_node_domain@")
         .add_property("unit", &ExpNode::unit, "@DocString_exp_node_unit@")
         .add_property("rung", &ExpNode::rung, "@DocString_exp_node_rung@")
-        .add_property("domain", &ExpNode::domain, "@DocString_exp_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerNegExpNode()
 {
+    void (NegExpNode::*set_value_no_param)(int) = &NegExpNode::set_value;
+    void (NegExpNode::*set_test_value_no_param)(int) = &NegExpNode::set_test_value;
+    std::string (NegExpNode::*expr_no_param)() = &NegExpNode::expr;
+    Domain (NegExpNode::*domain_no_param)() = &NegExpNode::domain;
+
     class_<NegExpNode, bases<OperatorNode<1>>>("NegExpNode", init<node_ptr, int, double, double>())
-        .def("set_value", &NegExpNode::set_value, "@DocString_neg_exp_node_set_value@")
-        .def("set_test_value", &NegExpNode::set_test_value, "@DocString_neg_exp_node_set_test_value@")
-        .add_property("expr", &NegExpNode::expr, "@DocString_neg_exp_node_expr@")
-        .add_property("bounds", &NegExpNode::domain, "@DocString_neg_exp_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_neg_exp_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_neg_exp_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_neg_exp_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_neg_exp_node_domain@")
         .add_property("unit", &NegExpNode::unit, "@DocString_neg_exp_node_unit@")
         .add_property("rung", &NegExpNode::rung, "@DocString_neg_exp_node_rung@")
-        .add_property("domain", &NegExpNode::domain, "@DocString_neg_exp_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerSinNode()
 {
+    void (SinNode::*set_value_no_param)(int) = &SinNode::set_value;
+    void (SinNode::*set_test_value_no_param)(int) = &SinNode::set_test_value;
+    std::string (SinNode::*expr_no_param)() = &SinNode::expr;
+    Domain (SinNode::*domain_no_param)() = &SinNode::domain;
+
     class_<SinNode, bases<OperatorNode<1>>>("SinNode", init<node_ptr, int, double, double>())
-        .def("set_value", &SinNode::set_value, "@DocString_sin_node_set_value@")
-        .def("set_test_value", &SinNode::set_test_value, "@DocString_sin_node_set_test_value@")
-        .add_property("expr", &SinNode::expr, "@DocString_sin_node_expr@")
-        .add_property("bounds", &SinNode::domain, "@DocString_sin_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_sin_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_sin_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_sin_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_sin_node_domain@")
         .add_property("unit", &SinNode::unit, "@DocString_sin_node_unit@")
         .add_property("rung", &SinNode::rung, "@DocString_sin_node_rung@")
-        .add_property("domain", &SinNode::domain, "@DocString_sin_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerCosNode()
 {
+    void (CosNode::*set_value_no_param)(int) = &CosNode::set_value;
+    void (CosNode::*set_test_value_no_param)(int) = &CosNode::set_test_value;
+    std::string (CosNode::*expr_no_param)() = &CosNode::expr;
+    Domain (CosNode::*domain_no_param)() = &CosNode::domain;
+
     class_<CosNode, bases<OperatorNode<1>>>("CosNode", init<node_ptr, int, double, double>())
-        .def("set_value", &CosNode::set_value, "@DocString_cos_node_set_value@")
-        .def("set_test_value", &CosNode::set_test_value, "@DocString_cos_node_set_test_value@")
-        .add_property("expr", &CosNode::expr, "@DocString_cos_node_expr@")
-        .add_property("bounds", &CosNode::domain, "@DocString_cos_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_cos_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_cos_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_cos_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_cos_node_domain@")
         .add_property("unit", &CosNode::unit, "@DocString_cos_node_unit@")
         .add_property("rung", &CosNode::rung, "@DocString_cos_node_rung@")
-        .add_property("domain", &CosNode::domain, "@DocString_cos_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerCbNode()
 {
+    void (CbNode::*set_value_no_param)(int) = &CbNode::set_value;
+    void (CbNode::*set_test_value_no_param)(int) = &CbNode::set_test_value;
+    std::string (CbNode::*expr_no_param)() = &CbNode::expr;
+    Domain (CbNode::*domain_no_param)() = &CbNode::domain;
+
     class_<CbNode, bases<OperatorNode<1>>>("CbNode", init<node_ptr, int, double, double>())
-        .def("set_value", &CbNode::set_value, "@DocString_cb_node_set_value@")
-        .def("set_test_value", &CbNode::set_test_value, "@DocString_cb_node_set_test_value@")
-        .add_property("expr", &CbNode::expr, "@DocString_cb_node_expr@")
-        .add_property("bounds", &CbNode::domain, "@DocString_cb_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_cb_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_cb_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_cb_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_cb_node_domain@")
         .add_property("unit", &CbNode::unit, "@DocString_cb_node_unit@")
-        .add_property("domain", &CbNode::domain, "@DocString_cb_node_domain@")
+        .add_property("rung", &CbNode::rung, "@DocString_cb_node_rung@")
     ;
 }
 
 void sisso::feature_creation::node::registerCbrtNode()
 {
+    void (CbrtNode::*set_value_no_param)(int) = &CbrtNode::set_value;
+    void (CbrtNode::*set_test_value_no_param)(int) = &CbrtNode::set_test_value;
+    std::string (CbrtNode::*expr_no_param)() = &CbrtNode::expr;
+    Domain (CbrtNode::*domain_no_param)() = &CbrtNode::domain;
+
     class_<CbrtNode, bases<OperatorNode<1>>>("CbrtNode", init<node_ptr, int, double, double>())
-        .def("set_value", &CbrtNode::set_value, "@DocString_cbrt_node_set_value@")
-        .def("set_test_value", &CbrtNode::set_test_value, "@DocString_cbrt_node_set_test_value@")
-        .add_property("expr", &CbrtNode::expr, "@DocString_cbrt_node_expr@")
-        .add_property("bounds", &CbrtNode::domain, "@DocString_cbrt_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_cbrt_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_cbrt_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_cbrt_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_cbrt_node_domain@")
         .add_property("unit", &CbrtNode::unit, "@DocString_cbrt_node_unit@")
-        .add_property("domain", &CbrtNode::domain, "@DocString_cbrt_node_domain@")
+        .add_property("rung", &CbrtNode::rung, "@DocString_cbrt_node_rung@")
     ;
 }
 
 void sisso::feature_creation::node::registerSqNode()
 {
+    void (SqNode::*set_value_no_param)(int) = &SqNode::set_value;
+    void (SqNode::*set_test_value_no_param)(int) = &SqNode::set_test_value;
+    std::string (SqNode::*expr_no_param)() = &SqNode::expr;
+    Domain (SqNode::*domain_no_param)() = &SqNode::domain;
+
     class_<SqNode, bases<OperatorNode<1>>>("SqNode", init<node_ptr, int, double, double>())
-        .def("set_value", &SqNode::set_value, "@DocString_sq_node_set_value@")
-        .def("set_test_value", &SqNode::set_test_value, "@DocString_sq_node_set_test_value@")
-        .add_property("expr", &SqNode::expr, "@DocString_sq_node_expr@")
-        .add_property("bounds", &SqNode::domain, "@DocString_sq_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_sq_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_sq_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_sq_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_sq_node_domain@")
         .add_property("unit", &SqNode::unit, "@DocString_sq_node_unit@")
         .add_property("rung", &SqNode::rung, "@DocString_sq_node_rung@")
-        .add_property("domain", &SqNode::domain, "@DocString_sq_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerSqrtNode()
 {
+    void (SqrtNode::*set_value_no_param)(int) = &SqrtNode::set_value;
+    void (SqrtNode::*set_test_value_no_param)(int) = &SqrtNode::set_test_value;
+    std::string (SqrtNode::*expr_no_param)() = &SqrtNode::expr;
+    Domain (SqrtNode::*domain_no_param)() = &SqrtNode::domain;
+
     class_<SqrtNode, bases<OperatorNode<1>>>("SqrtNode", init<node_ptr, int, double, double>())
-        .def("set_value", &SqrtNode::set_value, "@DocString_sqrt_node_set_value@")
-        .def("set_test_value", &SqrtNode::set_test_value, "@DocString_sqrt_node_set_test_value@")
-        .add_property("expr", &SqrtNode::expr, "@DocString_sqrt_node_expr@")
-        .add_property("bounds", &SqrtNode::domain, "@DocString_sqrt_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_sqrt_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_sqrt_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_sqrt_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_sqrt_node_domain@")
         .add_property("unit", &SqrtNode::unit, "@DocString_sqrt_node_unit@")
         .add_property("rung", &SqrtNode::rung, "@DocString_sqrt_node_rung@")
-        .add_property("domain", &SqrtNode::domain, "@DocString_sqrt_node_domain@")
     ;
 }
 
 void sisso::feature_creation::node::registerSixPowNode()
 {
+    void (SixPowNode::*set_value_no_param)(int) = &SixPowNode::set_value;
+    void (SixPowNode::*set_test_value_no_param)(int) = &SixPowNode::set_test_value;
+    std::string (SixPowNode::*expr_no_param)() = &SixPowNode::expr;
+    Domain (SixPowNode::*domain_no_param)() = &SixPowNode::domain;
+
     class_<SixPowNode, bases<OperatorNode<1>>>("SixPowNode", init<node_ptr, int, double, double>())
-        .def("set_value", &SixPowNode::set_value, "@DocString_six_pow_node_set_value@")
-        .def("set_test_value", &SixPowNode::set_test_value, "@DocString_six_pow_node_set_test_value@")
-        .add_property("expr", &SixPowNode::expr, "@DocString_six_pow_node_expr@")
-        .add_property("bounds", &SixPowNode::domain, "@DocString_six_pow_node_domain@")
+        .def("set_value", set_value_no_param, "@DocString_six_pow_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_six_pow_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_six_pow_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_six_pow_node_domain@")
         .add_property("unit", &SixPowNode::unit, "@DocString_six_pow_node_unit@")
         .add_property("rung", &SixPowNode::rung, "@DocString_six_pow_node_rung@")
-        .add_property("domain", &SixPowNode::domain, "@DocString_six_pow_node_domain@")
+    ;
+}
+
+void sisso::feature_creation::node::registerAddParamNode()
+{
+    void (AddParamNode::*set_value_no_param)(int) = &AddParamNode::set_value;
+    void (AddParamNode::*set_test_value_no_param)(int) = &AddParamNode::set_test_value;
+    std::string (AddParamNode::*expr_no_param)() = &AddParamNode::expr;
+    Domain (AddParamNode::*domain_no_param)() = &AddParamNode::domain;
+
+    class_<AddParamNode, bases<AddNode>>("AddParamNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_add_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_add_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_add_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_add_param_node_domain@")
+        .add_property("unit", &AddParamNode::unit, "@DocString_add_param_node_unit@")
+        .add_property("rung", &AddParamNode::rung, "@DocString_add_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerSubParamNode()
+{
+    void (SubParamNode::*set_value_no_param)(int) = &SubParamNode::set_value;
+    void (SubParamNode::*set_test_value_no_param)(int) = &SubParamNode::set_test_value;
+    std::string (SubParamNode::*expr_no_param)() = &SubParamNode::expr;
+    Domain (SubParamNode::*domain_no_param)() = &SubParamNode::domain;
+
+    class_<SubParamNode, bases<SubNode>>("SubNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_sub_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_sub_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_sub_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_sub_param_node_domain@")
+        .add_property("unit", &SubParamNode::unit, "@DocString_sub_param_node_unit@")
+        .add_property("rung", &SubParamNode::rung, "@DocString_sub_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerDivParamNode()
+{
+    void (DivParamNode::*set_value_no_param)(int) = &DivParamNode::set_value;
+    void (DivParamNode::*set_test_value_no_param)(int) = &DivParamNode::set_test_value;
+    std::string (DivParamNode::*expr_no_param)() = &DivParamNode::expr;
+    Domain (DivParamNode::*domain_no_param)() = &DivParamNode::domain;
+
+    class_<DivParamNode, bases<DivNode>>("DivNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_div_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_div_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_div_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_div_param_node_domain@")
+        .add_property("unit", &DivParamNode::unit, "@DocString_div_param_node_unit@")
+        .add_property("rung", &DivParamNode::rung, "@DocString_div_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerMultParamNode()
+{
+    void (MultParamNode::*set_value_no_param)(int) = &MultParamNode::set_value;
+    void (MultParamNode::*set_test_value_no_param)(int) = &MultParamNode::set_test_value;
+    std::string (MultParamNode::*expr_no_param)() = &MultParamNode::expr;
+    Domain (MultParamNode::*domain_no_param)() = &MultParamNode::domain;
+
+    class_<MultParamNode, bases<MultNode>>("MultNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_mult_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_mult_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_mult_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_mult_param_node_domain@")
+        .add_property("unit", &MultParamNode::unit, "@DocString_mult_param_node_unit@")
+        .add_property("rung", &MultParamNode::rung, "@DocString_mult_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerAbsDiffParamNode()
+{
+    void (AbsDiffParamNode::*set_value_no_param)(int) = &AbsDiffParamNode::set_value;
+    void (AbsDiffParamNode::*set_test_value_no_param)(int) = &AbsDiffParamNode::set_test_value;
+    std::string (AbsDiffParamNode::*expr_no_param)() = &AbsDiffParamNode::expr;
+    Domain (AbsDiffParamNode::*domain_no_param)() = &AbsDiffParamNode::domain;
+
+    class_<AbsDiffParamNode, bases<AbsDiffNode>>("AbsDiffNode", init<node_ptr, node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_abs_diff_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_abs_diff_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_abs_diff_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_abs_diff_param_node_domain@")
+        .add_property("unit", &AbsDiffParamNode::unit, "@DocString_abs_diff_param_node_unit@")
+        .add_property("rung", &AbsDiffParamNode::rung, "@DocString_abs_diff_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerAbsParamNode()
+{
+    void (AbsParamNode::*set_value_no_param)(int) = &AbsParamNode::set_value;
+    void (AbsParamNode::*set_test_value_no_param)(int) = &AbsParamNode::set_test_value;
+    std::string (AbsParamNode::*expr_no_param)() = &AbsParamNode::expr;
+    Domain (AbsParamNode::*domain_no_param)() = &AbsParamNode::domain;
+
+    class_<AbsParamNode, bases<AbsNode>>("AbsNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_abs_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_abs_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_abs_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_abs_param_node_domain@")
+        .add_property("unit", &AbsParamNode::unit, "@DocString_abs_param_node_unit@")
+        .add_property("rung", &AbsParamNode::rung, "@DocString_abs_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerInvParamNode()
+{
+    void (InvParamNode::*set_value_no_param)(int) = &InvParamNode::set_value;
+    void (InvParamNode::*set_test_value_no_param)(int) = &InvParamNode::set_test_value;
+    std::string (InvParamNode::*expr_no_param)() = &InvParamNode::expr;
+    Domain (InvParamNode::*domain_no_param)() = &InvParamNode::domain;
+
+    class_<InvParamNode, bases<InvNode>>("InvNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_inv_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_inv_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_inv_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_inv_param_node_domain@")
+        .add_property("unit", &InvParamNode::unit, "@DocString_inv_param_node_unit@")
+        .add_property("rung", &InvParamNode::rung, "@DocString_inv_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerLogParamNode()
+{
+    void (LogParamNode::*set_value_no_param)(int) = &LogParamNode::set_value;
+    void (LogParamNode::*set_test_value_no_param)(int) = &LogParamNode::set_test_value;
+    std::string (LogParamNode::*expr_no_param)() = &LogParamNode::expr;
+    Domain (LogParamNode::*domain_no_param)() = &LogParamNode::domain;
+
+    class_<LogParamNode, bases<LogNode>>("LogNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_log_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_log_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_log_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_log_param_node_domain@")
+        .add_property("unit", &LogParamNode::unit, "@DocString_log_param_node_unit@")
+        .add_property("rung", &LogParamNode::rung, "@DocString_log_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerExpParamNode()
+{
+    void (ExpParamNode::*set_value_no_param)(int) = &ExpParamNode::set_value;
+    void (ExpParamNode::*set_test_value_no_param)(int) = &ExpParamNode::set_test_value;
+    std::string (ExpParamNode::*expr_no_param)() = &ExpParamNode::expr;
+    Domain (ExpParamNode::*domain_no_param)() = &ExpParamNode::domain;
+
+    class_<ExpParamNode, bases<ExpNode>>("ExpNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_exp_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_exp_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_exp_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_exp_param_node_domain@")
+        .add_property("unit", &ExpParamNode::unit, "@DocString_exp_param_node_unit@")
+        .add_property("rung", &ExpParamNode::rung, "@DocString_exp_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerNegExpParamNode()
+{
+    void (NegExpParamNode::*set_value_no_param)(int) = &NegExpParamNode::set_value;
+    void (NegExpParamNode::*set_test_value_no_param)(int) = &NegExpParamNode::set_test_value;
+    std::string (NegExpParamNode::*expr_no_param)() = &NegExpParamNode::expr;
+    Domain (NegExpParamNode::*domain_no_param)() = &NegExpParamNode::domain;
+
+    class_<NegExpParamNode, bases<NegExpNode>>("NegExpNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_neg_exp_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_neg_exp_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_neg_exp_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_neg_exp_param_node_domain@")
+        .add_property("unit", &NegExpParamNode::unit, "@DocString_neg_exp_param_node_unit@")
+        .add_property("rung", &NegExpParamNode::rung, "@DocString_neg_exp_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerSinParamNode()
+{
+    void (SinParamNode::*set_value_no_param)(int) = &SinParamNode::set_value;
+    void (SinParamNode::*set_test_value_no_param)(int) = &SinParamNode::set_test_value;
+    std::string (SinParamNode::*expr_no_param)() = &SinParamNode::expr;
+    Domain (SinParamNode::*domain_no_param)() = &SinParamNode::domain;
+
+    class_<SinParamNode, bases<SinNode>>("SinParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_sin_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_sin_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_sin_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_sin_param_node_domain@")
+        .add_property("unit", &SinParamNode::unit, "@DocString_sin_param_node_unit@")
+        .add_property("rung", &SinParamNode::rung, "@DocString_sin_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerCosParamNode()
+{
+    void (CosParamNode::*set_value_no_param)(int) = &CosParamNode::set_value;
+    void (CosParamNode::*set_test_value_no_param)(int) = &CosParamNode::set_test_value;
+    std::string (CosParamNode::*expr_no_param)() = &CosParamNode::expr;
+    Domain (CosParamNode::*domain_no_param)() = &CosParamNode::domain;
+
+    class_<CosParamNode, bases<CosNode>>("CosParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_cos_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_cos_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_cos_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_cos_param_node_domain@")
+        .add_property("unit", &CosParamNode::unit, "@DocString_cos_param_node_unit@")
+        .add_property("rung", &CosParamNode::rung, "@DocString_cos_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerCbParamNode()
+{
+    void (CbParamNode::*set_value_no_param)(int) = &CbParamNode::set_value;
+    void (CbParamNode::*set_test_value_no_param)(int) = &CbParamNode::set_test_value;
+    std::string (CbParamNode::*expr_no_param)() = &CbParamNode::expr;
+    Domain (CbParamNode::*domain_no_param)() = &CbParamNode::domain;
+
+    class_<CbParamNode, bases<CbNode>>("CbParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_cb_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_cb_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_cb_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_cb_param_node_domain@")
+        .add_property("unit", &CbParamNode::unit, "@DocString_cb_param_node_unit@")
+        .add_property("rung", &CbParamNode::rung, "@DocString_cb_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerCbrtParamNode()
+{
+    void (CbrtParamNode::*set_value_no_param)(int) = &CbrtParamNode::set_value;
+    void (CbrtParamNode::*set_test_value_no_param)(int) = &CbrtParamNode::set_test_value;
+    std::string (CbrtParamNode::*expr_no_param)() = &CbrtParamNode::expr;
+    Domain (CbrtParamNode::*domain_no_param)() = &CbrtParamNode::domain;
+
+    class_<CbrtParamNode, bases<CbrtNode>>("CbrtParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_cbrt_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_cbrt_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_cbrt_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_cbrt_param_node_domain@")
+        .add_property("unit", &CbrtParamNode::unit, "@DocString_cbrt_param_node_unit@")
+        .add_property("rung", &CbrtParamNode::rung, "@DocString_cbrt_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerSqParamNode()
+{
+    void (SqParamNode::*set_value_no_param)(int) = &SqParamNode::set_value;
+    void (SqParamNode::*set_test_value_no_param)(int) = &SqParamNode::set_test_value;
+    std::string (SqParamNode::*expr_no_param)() = &SqParamNode::expr;
+    Domain (SqParamNode::*domain_no_param)() = &SqParamNode::domain;
+
+    class_<SqParamNode, bases<SqNode>>("SqParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_sq_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_sq_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_sq_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_sq_param_node_domain@")
+        .add_property("unit", &SqParamNode::unit, "@DocString_sq_param_node_unit@")
+        .add_property("rung", &SqParamNode::rung, "@DocString_sq_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerSqrtParamNode()
+{
+    void (SqrtParamNode::*set_value_no_param)(int) = &SqrtParamNode::set_value;
+    void (SqrtParamNode::*set_test_value_no_param)(int) = &SqrtParamNode::set_test_value;
+    std::string (SqrtParamNode::*expr_no_param)() = &SqrtParamNode::expr;
+    Domain (SqrtParamNode::*domain_no_param)() = &SqrtParamNode::domain;
+
+    class_<SqrtParamNode, bases<SqrtNode>>("SqrtParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_sqrt_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_sqrt_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_sqrt_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_sqrt_param_node_domain@")
+        .add_property("unit", &SqrtParamNode::unit, "@DocString_sqrt_param_node_unit@")
+        .add_property("rung", &SqrtParamNode::rung, "@DocString_sqrt_param_node_rung@")
+    ;
+}
+
+void sisso::feature_creation::node::registerSixPowParamNode()
+{
+    void (SixPowParamNode::*set_value_no_param)(int) = &SixPowParamNode::set_value;
+    void (SixPowParamNode::*set_test_value_no_param)(int) = &SixPowParamNode::set_test_value;
+    std::string (SixPowParamNode::*expr_no_param)() = &SixPowParamNode::expr;
+    Domain (SixPowParamNode::*domain_no_param)() = &SixPowParamNode::domain;
+
+    class_<SixPowParamNode, bases<SixPowNode>>("SixPowParamNode", init<node_ptr, int, double, double>())
+        .def("set_value", set_value_no_param, "@DocString_six_pow_param_node_set_value@")
+        .def("set_test_value", set_test_value_no_param, "@DocString_six_pow_param_node_set_test_value@")
+        .add_property("expr", expr_no_param, "@DocString_six_pow_param_node_expr@")
+        .add_property("domain", domain_no_param, "@DocString_six_pow_param_node_domain@")
+        .add_property("unit", &SixPowParamNode::unit, "@DocString_six_pow_param_node_unit@")
+        .add_property("rung", &SixPowParamNode::rung, "@DocString_six_pow_param_node_rung@")
     ;
 }
 
diff --git a/src/python/bindings_docstring_keyed.hpp b/src/python/bindings_docstring_keyed.hpp
index cee337fa..e80f6bb6 100644
--- a/src/python/bindings_docstring_keyed.hpp
+++ b/src/python/bindings_docstring_keyed.hpp
@@ -38,26 +38,32 @@ namespace sisso
             {
             public:
                 inline std::string expr(){return this->get_override("expr")();}
+                inline std::string expr(double*){return this->get_override("expr")();}
                 inline Unit unit(){return this->get_override("unit")();}
                 inline std::vector<double> value(){return this->get_override("value")();}
                 inline std::vector<double> test_value(){return this->get_override("test_value")();}
                 inline void set_value(int offset = -1){this->get_override("set_value")();}
-                inline double* value_ptr(int offset = -1){return this->get_override("value_ptr")();}
                 inline void set_test_value(int offset = -1){this->get_override("set_test_value")();}
+                inline double* value_ptr(int offset = -1){return this->get_override("value_ptr")();}
                 inline double* test_value_ptr(int offset = -1){return this->get_override("test_value_ptr")();}
+                inline void set_value(double* params, int offset = -1){this->get_override("set_value")();}
+                inline void set_test_value(double* params, int offset = -1){this->get_override("set_test_value")();}
+                inline double* value_ptr(double* params, int offset = -1){return this->get_override("value_ptr")();}
+                inline double* test_value_ptr(double* params, int offset = -1){return this->get_override("test_value_ptr")();}
                 inline bool is_nan(){return this->get_override("is_nan")();}
                 inline bool is_const(){return this->get_override("is_const")();}
                 inline NODE_TYPE type(){return this->get_override("type")();}
                 inline int rung(int cur_rung = 0){return this->get_override("rung")();}
                 inline std::map<int, int> primary_feature_decomp(){return this->get_override("primary_feature_decomp")();}
-                void update_primary_feature_decomp(std::map<int, int>& pf_decomp){this->get_override("update_primary_feature_decomp")();}
-                void update_postfix(std::string& cur_expr){this->get_override("update_postfix")();}
-                std::string get_postfix_term(){return this->get_override("get_postfix_term")();}
+                inline void update_primary_feature_decomp(std::map<int, int>& pf_decomp){this->get_override("update_primary_feature_decomp")();}
+                inline void update_postfix(std::string& cur_expr){this->get_override("update_postfix")();}
+                inline std::string get_postfix_term(){return this->get_override("get_postfix_term")();}
                 inline void update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot){this->get_override("update_add_sub_leaves")();}
                 inline void update_div_mult_leaves(std::map<std::string, double>& div_mult_leaves, double fact, double& expected_abs_tot){this->get_override("update_div_mult_leaves")();}
-                std::array<double, 2> parameters(){return this->get_override("parameters")();}
-                void set_parameters(std::array<double, 2>){this->get_override("set_parameters")();}
-                Domain domain(){return this->get_override("domain")();}
+                inline std::vector<double> parameters(){return this->get_override("parameters")();}
+                inline void set_parameters(std::vector<double>){this->get_override("set_parameters")();}
+                inline Domain domain(){return this->get_override("domain")();}
+                inline Domain domain(double* params){return this->get_override("domain")();}
                 inline int n_feats(){this->get_override("n_feats");}
                 inline std::shared_ptr<Node> feat(int ind){this->get_override("feat");}
             };
@@ -69,15 +75,21 @@ namespace sisso
             {
                 inline void set_value(int offset = -1){this->get_override("set_value")();}
                 inline void set_test_value(int offset = -1){this->get_override("set_test_value")();}
+                inline void set_value(const double* params, int offset = -1){this->get_override("set_value")();}
+                inline void set_test_value(const double* params, int offset = -1){this->get_override("set_test_value")();}
                 inline NODE_TYPE type(){return this->get_override("type")();}
                 inline int rung(int cur_rung = 0){return this->get_override("rung")();}
-                inline std::string expr(){return this->get_override("expr")();}
                 inline Unit unit(){return this->get_override("unit")();}
                 inline std::string get_postfix_term(){return this->get_override("get_postfix_term")();}
+                inline Domain domain(double* params){return this->get_override("domain")();}
+                inline Domain domain(){return this->get_override("domain")();}
+                inline std::string expr(double* params){return this->get_override("expr")();}
+                inline std::string expr(){return this->get_override("expr")();}
                 inline void update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot){this->get_override("update_add_sub_leaves")();}
                 inline void update_div_mult_leaves(std::map<std::string, double>& div_mult_leaves, double fact, double& expected_abs_tot){this->get_override("update_div_mult_leaves")();}
-                inline void get_parameters(std::vector<double>& prop, std::vector<std::string>& param_list){this->get_override("get_parameters")();}
-                Domain domain(){return this->get_override("domain")();}
+                inline void get_parameters(std::vector<double>& prop){this->get_override("get_parameters")();}
+                inline void set_parameters(std::vector<double>){this->get_override("set_parameters")();}
+                inline std::vector<double> parameters(){return this->get_override("parameters")();}
             };
 
             /**
@@ -122,23 +134,22 @@ namespace sisso
                 template<int N>
                 static void registerOperatorNode()
                 {
-                    void (OperatorNode<N>::*get_parameters_list)(py::list, py::list) = &OperatorNode<N>::get_parameters;
-                    void (OperatorNode<N>::*get_parameters_arr)(np::ndarray, py::list) = &OperatorNode<N>::get_parameters;
+                    void (OperatorNode<N>::*get_parameters_list)(py::list) = &OperatorNode<N>::get_parameters;
+                    void (OperatorNode<N>::*get_parameters_arr)(np::ndarray) = &OperatorNode<N>::get_parameters;
                     void (OperatorNode<N>::*set_params_list)(py::list) = &OperatorNode<N>::set_parameters;
                     void (OperatorNode<N>::*set_params_arr)(np::ndarray) = &OperatorNode<N>::set_parameters;
                     py::class_<OperatorNodeWrap<N>, py::bases<Node>, boost::noncopyable>("OperatorNode")
                         .def("is_nan", &OperatorNode<N>::is_nan, "@DocString_op_node_is_nan@")
                         .def("is_const", &OperatorNode<N>::is_const, "@DocString_op_node_is_const@")
-                        .def("set_value", py::pure_virtual(&OperatorNode<N>::set_value), "@DocString_op_node_set_value@")
-                        .def("set_test_value", py::pure_virtual(&OperatorNode<N>::set_test_value), "@DocString_op_node_set_test_value@")
+                        // .def("set_value", py::pure_virtual(&OperatorNode<N>::set_value), "@DocString_op_node_set_value@")
+                        // .def("set_test_value", py::pure_virtual(&OperatorNode<N>::set_test_value), "@DocString_op_node_set_test_value@")
                         .def("rung", py::pure_virtual(&OperatorNode<N>::rung), "@DocString_op_node_rung@")
-                        .def("expr", py::pure_virtual(&OperatorNode<N>::expr), "@DocString_op_node_expr@")
+                        // .def("expr", py::pure_virtual(&OperatorNode<N>::expr), "@DocString_op_node_expr@")
                         .def("unit", py::pure_virtual(&OperatorNode<N>::unit), "@DocString_op_node_unit@")
                         .def("get_parameters", get_parameters_arr, "@DocString_op_node_param_arr@")
                         .def("get_parameters", get_parameters_list, "@DocString_op_node_param_list@")
                         .def("set_parameters", set_params_arr, "@DocString_op_node_set_param_arr@")
                         .def("set_parameters", set_params_list, "@DocString_op_node_set_param_list@")
-                        .def("reset_parameters", &OperatorNode<N>::reset_parameters, "@DocString_op_node_reset_params@")
                         .add_property("n_feats", &OperatorNode<N>::n_feats, "@DocString_op_node_n_feats@")
                         .add_property("feat", &OperatorNode<N>::feat, "@DocString_op_node_feat@")
                     ;
@@ -230,6 +241,91 @@ namespace sisso
              */
             static void registerSixPowNode();
 
+            /**
+             * @brief Register the AddParamNode object for conversion to a python object
+             */
+            static void registerAddParamNode();
+
+            /**
+             * @brief Register the SubParamNode object for conversion to a python object
+             */
+            static void registerSubParamNode();
+
+            /**
+             * @brief Register the DivParamNode object for conversion to a python object
+             */
+            static void registerDivParamNode();
+
+            /**
+             * @brief Register the MultParamNode object for conversion to a python object
+             */
+            static void registerMultParamNode();
+
+            /**
+             * @brief Register the AbsDiffParamNode object for conversion to a python object
+             */
+            static void registerAbsDiffParamNode();
+
+            /**
+             * @brief Register the AbsParamNode object for conversion to a python object
+             */
+            static void registerAbsParamNode();
+
+            /**
+             * @brief Register the InvParamNode object for conversion to a python object
+             */
+            static void registerInvParamNode();
+
+            /**
+             * @brief Register the LogParamNode object for conversion to a python object
+             */
+            static void registerLogParamNode();
+
+            /**
+             * @brief Register the ExpParamNode object for conversion to a python object
+             */
+            static void registerExpParamNode();
+
+            /**
+             * @brief Register the NegExpParamNode object for conversion to a python object
+             */
+            static void registerNegExpParamNode();
+
+            /**
+             * @brief Register the SinParamNode object for conversion to a python object
+             */
+            static void registerSinParamNode();
+
+            /**
+             * @brief Register the CosParamNode object for conversion to a python object
+             */
+            static void registerCosParamNode();
+
+            /**
+             * @brief Register the CbParamNode object for conversion to a python object
+             */
+            static void registerCbParamNode();
+
+            /**
+             * @brief Register the CbrtParamNode object for conversion to a python object
+             */
+            static void registerCbrtParamNode();
+
+            /**
+             * @brief Register the SqParamNode object for conversion to a python object
+             */
+            static void registerSqParamNode();
+
+            /**
+             * @brief Register the SqrtParamNode object for conversion to a python object
+             */
+            static void registerSqrtParamNode();
+
+            /**
+             * @brief Register the SixPowParamNode object for conversion to a python object
+             */
+            static void registerSixPowParamNode();
+
         }
     }
 
diff --git a/src/python/feature_creation/FeatureSpace.cpp b/src/python/feature_creation/FeatureSpace.cpp
index 3c444b0e..d6105c89 100644
--- a/src/python/feature_creation/FeatureSpace.cpp
+++ b/src/python/feature_creation/FeatureSpace.cpp
@@ -3,7 +3,7 @@
 FeatureSpace::FeatureSpace(
     py::list phi_0,
     py::list allowed_ops,
-    py::dict allowed_param_ops,
+    py::list allowed_param_ops,
     py::list prop,
     py::list task_sizes,
     std::string project_type,
@@ -17,6 +17,7 @@ FeatureSpace::FeatureSpace(
 ):
     _phi(python_conv_utils::shared_ptr_vec_from_list<Node, FeatureNode>(phi_0)),
     _phi_0(_phi),
+    _allowed_param_ops(python_conv_utils::from_list<std::string>(allowed_param_ops)),
     _allowed_ops(python_conv_utils::from_list<std::string>(allowed_ops)),
     _prop(python_conv_utils::from_list<double>(prop)),
     _scores(py::len(phi_0), 0.0),
@@ -34,19 +35,14 @@ FeatureSpace::FeatureSpace(
     _n_rung_store(max_store_rung),
     _n_rung_generate(n_rung_generate)
 {
-    for(int kk = 0; kk < py::len(allowed_param_ops.keys()); ++kk)
-    {
-        std::string key = py::extract<std::string>(allowed_param_ops.keys()[kk]);
-        _allowed_param_ops[key] = python_conv_utils::from_list<std::string>(py::extract<py::list>(allowed_param_ops.values()[kk]));
-    }
     _n_samp = _phi_0[0]->n_samp();
-    initialize_fs(python_conv_utils::from_list<double>(prop), project_type);
+    initialize_fs(project_type);
 }
 
 FeatureSpace::FeatureSpace(
     py::list phi_0,
     py::list allowed_ops,
-    py::dict allowed_param_ops,
+    py::list allowed_param_ops,
     np::ndarray prop,
     py::list task_sizes,
     std::string project_type,
@@ -60,6 +56,7 @@ FeatureSpace::FeatureSpace(
 ):
     _phi(python_conv_utils::shared_ptr_vec_from_list<Node, FeatureNode>(phi_0)),
     _phi_0(_phi),
+    _allowed_param_ops(python_conv_utils::from_list<std::string>(allowed_param_ops)),
     _allowed_ops(python_conv_utils::from_list<std::string>(allowed_ops)),
     _prop(python_conv_utils::from_ndarray<double>(prop)),
     _scores(py::len(phi_0), 0.0),
@@ -77,13 +74,8 @@ FeatureSpace::FeatureSpace(
     _n_rung_store(max_store_rung),
     _n_rung_generate(n_rung_generate)
 {
-    for(int kk = 0; kk < py::len(allowed_param_ops.keys()); ++kk)
-    {
-        std::string key = py::extract<std::string>(allowed_param_ops.keys()[kk]);
-        _allowed_param_ops[key] = python_conv_utils::from_list<std::string>(py::extract<py::list>(allowed_param_ops.values()[kk]));
-    }
     _n_samp = _phi_0[0]->n_samp();
-    initialize_fs(python_conv_utils::from_ndarray<double>(prop), project_type);
+    initialize_fs(project_type);
 }
 
 FeatureSpace::FeatureSpace(
diff --git a/tests/test_classification/test_classification.py b/tests/test_classification/test_classification.py
index f2d8a910..ba1418bc 100644
--- a/tests/test_classification/test_classification.py
+++ b/tests/test_classification/test_classification.py
@@ -69,13 +69,20 @@ def test_sisso_classifier():
     test_data[3][15] = 10.0
 
     phi_0 = [
-        FeatureNode(ff, f"feat_{ff}", train_data[ff], test_data[ff], Domain("(-inf, inf)"), Unit())
+        FeatureNode(
+            ff,
+            f"feat_{ff}",
+            train_data[ff],
+            test_data[ff],
+            Domain("(-inf, inf)"),
+            Unit(),
+        )
         for ff in range(10)
     ]
 
     op_set = ["add", "sub", "mult", "sq", "cb", "sqrt", "cbrt"]
 
-    feat_space = generate_fs(phi_0, prop, [80], op_set, {}, "classification", 1, 10)
+    feat_space = generate_fs(phi_0, prop, [80], op_set, [], "classification", 1, 10)
     sisso = SISSOClassifier(
         feat_space, Unit("m"), prop, prop_test, [80], [20], list(range(10)), 2, 1, 1
     )
diff --git a/tests/test_descriptor_identifier/test_regressor.py b/tests/test_descriptor_identifier/test_regressor.py
index 17861399..57df9d33 100644
--- a/tests/test_descriptor_identifier/test_regressor.py
+++ b/tests/test_descriptor_identifier/test_regressor.py
@@ -38,7 +38,7 @@ def test_sisso_regressor():
 
     op_set = ["add", "sub", "mult", "sq", "cb", "sqrt", "cbrt"]
 
-    feat_space = generate_fs(phi_0, prop, [90], op_set, {}, "regression", 2, 10)
+    feat_space = generate_fs(phi_0, prop, [90], op_set, [], "regression", 2, 10)
 
     sisso = SISSORegressor(
         feat_space,
diff --git a/tests/test_feature_creation/test_feature_space/test_feature_space.py b/tests/test_feature_creation/test_feature_space/test_feature_space.py
index 14889b76..e1cca0af 100644
--- a/tests/test_feature_creation/test_feature_space/test_feature_space.py
+++ b/tests/test_feature_creation/test_feature_space/test_feature_space.py
@@ -27,7 +27,7 @@ def test_feature_space():
 
     op_set = ["add", "sub", "mult", "sq", "cb", "sqrt", "cbrt"]
 
-    feat_space = generate_fs(phi_0, prop, [90], op_set, {}, "regression", 2, 10)
+    feat_space = generate_fs(phi_0, prop, [90], op_set, [], "regression", 2, 10)
     feat_space.sis(prop)
 
     shutil.rmtree("feature_space/")
diff --git a/tests/test_param.py b/tests/test_param.py
index 80240375..e2726539 100644
--- a/tests/test_param.py
+++ b/tests/test_param.py
@@ -10,11 +10,7 @@ def test_param():
         df=parent / "data_param.csv",
         prop_key="Prop",
         allowed_ops="all",
-        allowed_param_ops={
-            "exp": ["alpha", "a", "c"],
-            "log": ["alpha", "a", "b"],
-            "sq": ["a"],
-        },
+        allowed_param_ops=["exp", "log", "sq"],
         cols="all",
         max_phi=2,
         n_sis_select=20,
diff --git a/tests/test_sisso.py b/tests/test_sisso.py
index 30a10c2b..2b446fdb 100644
--- a/tests/test_sisso.py
+++ b/tests/test_sisso.py
@@ -10,7 +10,7 @@ def test_sisso():
         df=str(parent / "data.csv"),
         prop_key="Prop",
         allowed_ops="all",
-        allowed_param_ops={},
+        allowed_param_ops=[],
         cols="all",
         max_phi=2,
         n_sis_select=20,
-- 
GitLab