Merge pull request #1214 from rcurtin/python-memfix

Python memory fix
This commit is contained in:
Ryan Curtin
2018-02-05 07:31:47 -08:00
committed by GitHub
92 changed files with 1708 additions and 715 deletions
+2
View File
@@ -5,7 +5,9 @@ set(SOURCES
cli_option.hpp
default_param.hpp
default_param_impl.hpp
delete_allocated_memory.hpp
end_program.hpp
get_allocated_memory.hpp
get_param.hpp
get_raw_param.hpp
get_printable_param.hpp
+4 -2
View File
@@ -89,13 +89,15 @@ void AddToPO(const util::ParamData& d,
(boost::program_options::options_description*) output;
// Generate the name to be given to boost::program_options.
const std::string mappedName = MapParameterName<T>(d.name);
const std::string mappedName =
MapParameterName<typename std::remove_pointer<T>::type>(d.name);
std::string boostName = (d.alias != '\0') ? mappedName + "," +
std::string(1, d.alias) : mappedName;
// Note that we have to add the option as type equal to the mapped type, not
// the true type of the option.
AddToPO<typename ParameterType<T>::type>(boostName, d.desc, *desc);
AddToPO<typename ParameterType<typename std::remove_pointer<T>::type>::type>(
boostName, d.desc, *desc);
}
} // namespace cli
+13 -5
View File
@@ -28,6 +28,8 @@
#include "set_param.hpp"
#include "get_printable_param_name.hpp"
#include "get_printable_param_value.hpp"
#include "get_allocated_memory.hpp"
#include "delete_allocated_memory.hpp"
namespace mlpack {
namespace bindings {
@@ -88,19 +90,21 @@ class CLIOption
data.cppType = cppName;
// Apply default value.
if (std::is_same<N, typename ParameterType<N>::type>::value)
if (std::is_same<typename std::remove_pointer<N>::type,
typename ParameterType<typename
std::remove_pointer<N>::type>::type>::value)
{
data.value = boost::any(defaultValue);
}
else
{
typename ParameterType<N>::type tmp;
data.value = boost::any(std::tuple<N, typename ParameterType<N>::type>(
defaultValue, tmp));
typename ParameterType<typename std::remove_pointer<N>::type>::type tmp;
data.value = boost::any(std::tuple<N, decltype(tmp)>(defaultValue, tmp));
}
const std::string tname = data.tname;
const std::string boostName = MapParameterName<N>(identifier);
const std::string boostName = MapParameterName<
typename std::remove_pointer<N>::type>(identifier);
std::string progOptId = (alias[0] != '\0') ? boostName + ","
+ std::string(1, alias[0]) : boostName;
@@ -152,6 +156,10 @@ class CLIOption
&GetPrintableParamName<N>;
CLI::GetSingleton().functionMap[tname]["GetPrintableParamValue"] =
&GetPrintableParamValue<N>;
CLI::GetSingleton().functionMap[tname]["GetAllocatedMemory"] =
&GetAllocatedMemory<N>;
CLI::GetSingleton().functionMap[tname]["DeleteAllocatedMemory"] =
&DeleteAllocatedMemory<N>;
}
};
+11 -2
View File
@@ -54,10 +54,19 @@ std::string DefaultParamImpl(
const util::ParamData& data,
const typename boost::enable_if_c<
arma::is_arma_type<T>::value ||
data::HasSerialize<T>::value ||
std::is_same<T, std::tuple<mlpack::data::DatasetInfo,
arma::mat>>::value>::type* /* junk */ = 0);
/**
* Return the default value of a model option (this returns the default
* filename, or '' if the default is no file).
*/
template<typename T>
std::string DefaultParamImpl(
const util::ParamData& data,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0,
const typename boost::enable_if<data::HasSerialize<T>>::type* = 0);
/**
* Return the default value of an option. This is the function that will be
* placed into the CLI functionMap.
@@ -68,7 +77,7 @@ void DefaultParam(const util::ParamData& data,
void* output)
{
std::string* outstr = (std::string*) output;
*outstr = DefaultParamImpl<T>(data);
*outstr = DefaultParamImpl<typename std::remove_pointer<T>::type>(data);
}
} // namespace cli
+18 -1
View File
@@ -70,7 +70,6 @@ std::string DefaultParamImpl(
const util::ParamData& data,
const typename boost::enable_if_c<
arma::is_arma_type<T>::value ||
data::HasSerialize<T>::value ||
std::is_same<T, std::tuple<mlpack::data::DatasetInfo,
arma::mat>>::value>::type* /* junk */)
{
@@ -81,6 +80,24 @@ std::string DefaultParamImpl(
return "'" + filename + "'";
}
/**
* Return the default value of a model option (this returns the default
* filename, or '' if the default is no file).
*/
template<typename T>
std::string DefaultParamImpl(
const util::ParamData& data,
const typename boost::disable_if<arma::is_arma_type<T>>::type* /* junk */,
const typename boost::enable_if<data::HasSerialize<T>>::type* /* junk */)
{
// Get the filename and return it, or return an empty string.
typedef std::tuple<T*, std::string> TupleType;
const TupleType& tuple = *boost::any_cast<TupleType>(&data.value);
const std::string& filename = std::get<1>(tuple);
return "'" + filename + "'";
}
} // namespace cli
} // namespace bindings
} // namespace mlpack
@@ -0,0 +1,57 @@
/**
* @file delete_allocated_memory.hpp
* @author Ryan Curtin
*
* If any memory has been allocated by the parameter, delete it.
*/
#ifndef MLPACK_BINDINGS_CLI_DELETE_ALLOCATED_MEMORY_HPP
#define MLPACK_BINDINGS_CLI_DELETE_ALLOCATED_MEMORY_HPP
#include <mlpack/core/util/param_data.hpp>
namespace mlpack {
namespace bindings {
namespace cli {
template<typename T>
void DeleteAllocatedMemoryImpl(
const util::ParamData& /* d */,
const typename boost::disable_if<data::HasSerialize<T>>::type* = 0,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0)
{
// Do nothing.
}
template<typename T>
void DeleteAllocatedMemoryImpl(
const util::ParamData& /* d */,
const typename boost::enable_if<arma::is_arma_type<T>>::type* = 0)
{
// Do nothing.
}
template<typename T>
void DeleteAllocatedMemoryImpl(
const util::ParamData& d,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0,
const typename boost::enable_if<data::HasSerialize<T>>::type* = 0)
{
// Delete the allocated memory (hopefully we actually own it).
typedef std::tuple<T*, std::string> TupleType;
delete std::get<0>(*boost::any_cast<TupleType>(&d.value));
}
template<typename T>
void DeleteAllocatedMemory(
const util::ParamData& d,
const void* /* input */,
void* /* output */)
{
DeleteAllocatedMemoryImpl<typename std::remove_pointer<T>::type>(d);
}
} // namespace cli
} // namespace bindings
} // namespace mlpack
#endif
+31
View File
@@ -67,6 +67,37 @@ inline void EndProgram()
CLI::GetSingleton().timer.PrintTimer(it2.first);
}
}
// Lastly clean up any memory. If we are holding any pointers, then we "own"
// them. But we may hold the same pointer twice, so we have to be careful to
// not delete it multiple times.
std::unordered_map<void*, const util::ParamData*> memoryAddresses;
it = parameters.begin();
while (it != parameters.end())
{
const util::ParamData& data = it->second;
void* result;
CLI::GetSingleton().functionMap[data.tname]["GetAllocatedMemory"](data,
NULL, (void*) &result);
if (result != NULL && memoryAddresses.count(result) == 0)
memoryAddresses[result] = &data;
++it;
}
// Now we have all the unique addresses that need to be deleted.
std::unordered_map<void*, const util::ParamData*>::const_iterator it2;
it2 = memoryAddresses.begin();
while (it2 != memoryAddresses.end())
{
const util::ParamData& data = *(it2->second);
CLI::GetSingleton().functionMap[data.tname]["DeleteAllocatedMemory"](data,
NULL, NULL);
++it2;
}
}
} // namespace cli
@@ -0,0 +1,59 @@
/**
* @file get_allocated_memory.hpp
* @author Ryan Curtin
*
* If the parameter has a type that may need to be deleted, return the address
* of that object. Otherwise return NULL.
*/
#ifndef MLPACK_BINDINGS_CLI_GET_ALLOCATED_MEMORY_HPP
#define MLPACK_BINDINGS_CLI_GET_ALLOCATED_MEMORY_HPP
#include <mlpack/core/util/param_data.hpp>
namespace mlpack {
namespace bindings {
namespace cli {
template<typename T>
void* GetAllocatedMemory(
const util::ParamData& /* d */,
const typename boost::disable_if<data::HasSerialize<T>>::type* = 0,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0)
{
return NULL;
}
template<typename T>
void* GetAllocatedMemory(
const util::ParamData& /* d */,
const typename boost::enable_if<arma::is_arma_type<T>>::type* = 0)
{
return NULL;
}
template<typename T>
void* GetAllocatedMemory(
const util::ParamData& d,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0,
const typename boost::enable_if<data::HasSerialize<T>>::type* = 0)
{
// Here we have a model, which is a tuple, and we need the address of the
// memory.
typedef std::tuple<T*, std::string> TupleType;
return std::get<0>(*boost::any_cast<TupleType>(&d.value));
}
template<typename T>
void GetAllocatedMemory(const util::ParamData& d,
const void* /* input */,
void* output)
{
*((void**) output) =
GetAllocatedMemory<typename std::remove_pointer<T>::type>(d);
}
} // namespace cli
} // namespace bindings
} // namespace mlpack
#endif
+8 -7
View File
@@ -95,24 +95,24 @@ T& GetParam(
* @param d ParamData object to get parameter value from.
*/
template<typename T>
T& GetParam(
T*& GetParam(
util::ParamData& d,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0,
const typename boost::enable_if<data::HasSerialize<T>>::type* = 0)
{
// If the model is an input model, we have to load it from file. 'value'
// contains the filename.
typedef std::tuple<T, std::string> TupleType;
typedef std::tuple<T*, std::string> TupleType;
TupleType* tuple = boost::any_cast<TupleType>(&d.value);
const std::string& value = std::get<1>(*tuple);
T& model = std::get<0>(*tuple);
if (d.input && !d.loaded)
{
data::Load(value, "model", model, true);
T* model = new T();
data::Load(value, "model", *model, true);
d.loaded = true;
std::get<0>(*tuple) = model;
}
return model;
return std::get<0>(*tuple);
}
/**
@@ -127,7 +127,8 @@ template<typename T>
void GetParam(const util::ParamData& d, const void* /* input */, void* output)
{
// Cast to the correct type.
*((T**) output) = &GetParam<T>(const_cast<util::ParamData&>(d));
*((T**) output) = &GetParam<typename std::remove_pointer<T>::type>(
const_cast<util::ParamData&>(d));
}
} // namespace cli
@@ -37,16 +37,24 @@ std::string GetPrintableParam(
const typename std::enable_if<util::IsStdVector<T>::value>::type* = 0);
/**
* Print a matrix option (this just prints the filename).
* Print a matrix/tuple option (this just prints the filename).
*/
template<typename T>
std::string GetPrintableParam(
const util::ParamData& data,
const typename std::enable_if<arma::is_arma_type<T>::value ||
data::HasSerialize<T>::value ||
std::is_same<T,
std::tuple<data::DatasetInfo, arma::mat>>::value>::type* = 0);
/**
* Print a model option (this just prints the filename).
*/
template<typename T>
std::string GetPrintableParam(
const util::ParamData& data,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0,
const typename boost::enable_if<data::HasSerialize<T>>::type* = 0);
/**
* Print an option into a std::string. This should print a short, one-line
* representation of the object. The string will be stored in the output
@@ -57,7 +65,8 @@ void GetPrintableParam(const util::ParamData& data,
const void* /* input */,
void* output)
{
*((std::string*) output) = GetPrintableParam<T>(data);
*((std::string*) output) =
GetPrintableParam<typename std::remove_pointer<T>::type>(data);
}
} // namespace cli
@@ -43,12 +43,11 @@ std::string GetPrintableParam(
return oss.str();
}
//! Print a matrix/model/tuple option (this just prints the filename).
//! Print a matrix/tuple option (this just prints the filename).
template<typename T>
std::string GetPrintableParam(
const util::ParamData& data,
const typename std::enable_if<arma::is_arma_type<T>::value ||
data::HasSerialize<T>::value ||
std::is_same<T,
std::tuple<data::DatasetInfo, arma::mat>>::value>::type* /* junk */)
{
@@ -61,6 +60,22 @@ std::string GetPrintableParam(
return oss.str();
}
//! Print a model option (this just prints the filename).
template<typename T>
std::string GetPrintableParam(
const util::ParamData& data,
const typename boost::disable_if<arma::is_arma_type<T>>::type* /* junk */,
const typename boost::enable_if<data::HasSerialize<T>>::type* /* junk */)
{
// Extract the string from the tuple that's being held.
typedef std::tuple<T*, typename ParameterType<T>::type> TupleType;
const TupleType* tuple = boost::any_cast<TupleType>(&data.value);
std::ostringstream oss;
oss << std::get<1>(*tuple);
return oss.str();
}
} // namespace cli
} // namespace bindings
} // namespace mlpack
@@ -64,7 +64,8 @@ void GetPrintableParamName(
const void* /* input */,
void* output)
{
*((std::string*) output) = GetPrintableParamName<T>(d);
*((std::string*) output) =
GetPrintableParamName<typename std::remove_pointer<T>::type>(d);
}
} // namespace cli
@@ -68,7 +68,8 @@ void GetPrintableParamValue(
const void* input,
void* output)
{
*((std::string*) output) = GetPrintableParamValue<T>(d,
*((std::string*) output) =
GetPrintableParamValue<typename std::remove_pointer<T>::type>(d,
*((std::string*) input));
}
+19 -4
View File
@@ -40,15 +40,29 @@ T& GetRawParam(
const typename boost::enable_if_c<
arma::is_arma_type<T>::value ||
std::is_same<T, std::tuple<mlpack::data::DatasetInfo,
arma::mat>>::value ||
data::HasSerialize<T>::value>::type* = 0)
arma::mat>>::value>::type* = 0)
{
// Don't load the matrix/model.
// Don't load the matrix.
typedef std::tuple<T, std::string> TupleType;
T& value = std::get<0>(*boost::any_cast<TupleType>(&d.value));
return value;
}
/**
* Return the name of a model parameter.
*/
template<typename T>
T*& GetRawParam(
util::ParamData& d,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0,
const typename boost::enable_if<data::HasSerialize<T>>::type* = 0)
{
// Don't load the model.
typedef std::tuple<T*, std::string> TupleType;
T*& value = std::get<0>(*boost::any_cast<TupleType>(&d.value));
return value;
}
/**
* Return a parameter casted to the given type. Type checking does not happen
* here!
@@ -63,7 +77,8 @@ void GetRawParam(const util::ParamData& d,
void* output)
{
// Cast to the correct type.
*((T**) output) = &GetRawParam<T>(const_cast<util::ParamData&>(d));
*((T**) output) = &GetRawParam<typename std::remove_pointer<T>::type>(
const_cast<util::ParamData&>(d));
}
} // namespace cli
@@ -61,7 +61,8 @@ void MapParameterName(const util::ParamData& d,
{
// Store the mapped name in the output pointer, which is actually a string
// pointer.
*((std::string*) output) = MapParameterName<T>(d.name);
*((std::string*) output) =
MapParameterName<typename std::remove_pointer<T>::type>(d.name);
}
} // namespace cli
+1 -1
View File
@@ -70,7 +70,7 @@ void OutputParam(const util::ParamData& data,
const void* /* input */,
void* /* output */)
{
OutputParamImpl<T>(data);
OutputParamImpl<typename std::remove_pointer<T>::type>(data);
}
} // namespace cli
@@ -55,10 +55,10 @@ void OutputParamImpl(
if (output.n_elem > 0 && filename != "")
{
if (arma::is_Row<T>::value || arma::is_Col<T>::value)
data::Save(filename, output, false);
else
data::Save(filename, output, false, !data.noTranspose);
if (arma::is_Row<T>::value || arma::is_Col<T>::value)
data::Save(filename, output, false);
else
data::Save(filename, output, false, !data.noTranspose);
}
}
@@ -72,14 +72,14 @@ void OutputParamImpl(
// The const cast is necessary here because Serialize() can't ever be marked
// const. In this case we can assume it though, since we will be saving and
// not loading.
typedef std::tuple<T, std::string> TupleType;
T& output = const_cast<T&>(std::get<0>(*boost::any_cast<TupleType>(
typedef std::tuple<T*, std::string> TupleType;
T*& output = const_cast<T*&>(std::get<0>(*boost::any_cast<TupleType>(
&data.value)));
const std::string& filename =
std::get<1>(*boost::any_cast<TupleType>(&data.value));
if (filename != "")
data::Save(filename, "model", output);
data::Save(filename, "model", *output);
}
//! Output a mapped dataset.
@@ -52,7 +52,7 @@ void ParseCommandLine(int argc, char** argv)
boostNameMap[boostName] = d.name;
}
// TODO: we have to mark somehow that we parsed.
// Mark that we did parsing.
CLI::GetSingleton().didParse = true;
// Parse the command line, then place the values in the right place.
+19 -2
View File
@@ -54,7 +54,6 @@ void SetParam(
util::ParamData& d,
const boost::any& value,
const typename std::enable_if<arma::is_arma_type<T>::value ||
data::HasSerialize<T>::value ||
std::is_same<T,
std::tuple<data::DatasetInfo, arma::mat>>::value>::type* = 0)
{
@@ -64,6 +63,23 @@ void SetParam(
std::get<1>(tuple) = boost::any_cast<std::string>(value);
}
/**
* Set a serializable object. This sets the filename referring to the
* parameter.
*/
template<typename T>
void SetParam(
util::ParamData& d,
const boost::any& value,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0,
const typename boost::enable_if<data::HasSerialize<T>>::type* = 0)
{
// We're setting the string filename.
typedef std::tuple<T*, typename ParameterType<T>::type> TupleType;
TupleType& tuple = *boost::any_cast<TupleType>(&d.value);
std::get<1>(tuple) = boost::any_cast<std::string>(value);
}
/**
* Return a parameter casted to the given type. Type checking does not happen
* here!
@@ -75,7 +91,8 @@ void SetParam(
template<typename T>
void SetParam(const util::ParamData& d, const void* input, void* /* output */)
{
SetParam<T>(const_cast<util::ParamData&>(d), *((boost::any*) input));
SetParam<typename std::remove_pointer<T>::type>(
const_cast<util::ParamData&>(d), *((boost::any*) input));
}
} // namespace cli
@@ -79,7 +79,6 @@ set(CYTHON_SOURCES
mlpack/arma_util.hpp
mlpack/cli.pxd
mlpack/cli_util.hpp
mlpack/move.hpp
mlpack/matrix_utils.py
mlpack/serialization.hpp
mlpack/serialization.pxd
@@ -148,7 +147,6 @@ add_custom_command(TARGET python POST_BUILD
mlpack/arma_util.hpp
mlpack/cli.pxd
mlpack/cli_util.hpp
mlpack/move.hpp
mlpack/matrix_utils.py
mlpack
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/)
@@ -113,7 +113,7 @@ inline std::string GetCythonType(
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0,
const typename boost::enable_if<data::HasSerialize<T>>::type* = 0)
{
return d.cppType;
return d.cppType + "*";
}
} // namespace python
@@ -73,7 +73,7 @@ std::string GetPrintableParam(
const typename boost::enable_if<data::HasSerialize<T>>::type* = 0)
{
std::ostringstream oss;
oss << data.cppType << " model";
oss << data.cppType << " model at " << boost::any_cast<T*>(data.value);
return oss.str();
}
@@ -110,7 +110,8 @@ void GetPrintableParam(const util::ParamData& data,
const void* /* input */,
void* output)
{
*((std::string*) output) = GetPrintableParam<T>(data);
*((std::string*) output) =
GetPrintableParam<typename std::remove_pointer<T>::type>(data);
}
} // namespace python
+1 -1
View File
@@ -79,7 +79,7 @@ void ImportDecl(const util::ParamData& d,
const void* indent,
void* /* output */)
{
ImportDecl<T>(d, *((size_t*) indent));
ImportDecl<typename std::remove_pointer<T>::type>(d, *((size_t*) indent));
}
} // namespace python
@@ -19,14 +19,15 @@ import numpy
numpy.import_array()
cimport arma
from libcpp cimport bool
"""
Convert a numpy ndarray to a matrix.
"""
cdef arma.Mat[double]* numpy_to_mat_d(numpy.ndarray[numpy.double_t, ndim=2] X) \
except +
cdef arma.Mat[size_t]* numpy_to_mat_s(numpy.ndarray[numpy.npy_intp, ndim=2] X) \
except +
cdef arma.Mat[double]* numpy_to_mat_d(numpy.ndarray[numpy.double_t, ndim=2] X, \
bool takeOwnership) except +
cdef arma.Mat[size_t]* numpy_to_mat_s(numpy.ndarray[numpy.npy_intp, ndim=2] X, \
bool takeOwnership) except +
"""
Convert an Armadillo object to a numpy ndarray of the given type.
@@ -39,10 +40,10 @@ cdef numpy.ndarray[numpy.npy_intp, ndim=2] mat_to_numpy_s(arma.Mat[size_t]& X) \
"""
Convert a numpy one-dimensional ndarray to a row of the given type.
"""
cdef arma.Row[double]* numpy_to_row_d(numpy.ndarray[numpy.double_t, ndim=1] X) \
except +
cdef arma.Row[size_t]* numpy_to_row_s(numpy.ndarray[numpy.npy_intp, ndim=1] X) \
except +
cdef arma.Row[double]* numpy_to_row_d(numpy.ndarray[numpy.double_t, ndim=1] X, \
bool takeOwnership) except +
cdef arma.Row[size_t]* numpy_to_row_s(numpy.ndarray[numpy.npy_intp, ndim=1] X, \
bool takeOwnership) except +
"""
Convert an Armadillo row vector to a one-dimensional numpy ndarray of the
@@ -56,10 +57,10 @@ cdef numpy.ndarray[numpy.npy_intp, ndim=1] row_to_numpy_s(arma.Row[size_t]& X) \
"""
Convert a numpy one-dimensional ndarray to a column vector of the given type.
"""
cdef arma.Col[double]* numpy_to_col_d(numpy.ndarray[numpy.double_t, ndim=1] X) \
except +
cdef arma.Col[size_t]* numpy_to_col_s(numpy.ndarray[numpy.npy_intp, ndim=1] X) \
except +
cdef arma.Col[double]* numpy_to_col_d(numpy.ndarray[numpy.double_t, ndim=1] X, \
bool takeOwnership) except +
cdef arma.Col[size_t]* numpy_to_col_s(numpy.ndarray[numpy.npy_intp, ndim=1] X, \
bool takeOwnership) except +
"""
Convert an Armadillo column vector to a one-dimensional numpy ndarray of the
@@ -24,6 +24,7 @@ import numpy
numpy.import_array()
cimport arma
from libcpp cimport bool
cdef extern from "numpy/arrayobject.h":
void PyArray_ENABLEFLAGS(numpy.ndarray arr, int flags)
@@ -31,6 +32,7 @@ cdef extern from "numpy/arrayobject.h":
cdef extern from "<mlpack/bindings/python/mlpack/arma_util.hpp>":
void SetMemState[T](T& m, int state)
size_t GetMemState[T](T& m)
double* GetMemory(arma.Mat[double]& m)
double* GetMemory(arma.Col[double]& m)
double* GetMemory(arma.Row[double]& m)
@@ -38,38 +40,43 @@ cdef extern from "<mlpack/bindings/python/mlpack/arma_util.hpp>":
size_t* GetMemory(arma.Col[size_t]& m)
size_t* GetMemory(arma.Row[size_t]& m)
cdef arma.Mat[double]* numpy_to_mat_d(numpy.ndarray[numpy.double_t, ndim=2] X) \
except +:
cdef arma.Mat[double]* numpy_to_mat_d(numpy.ndarray[numpy.double_t, ndim=2] X, \
bool takeOwnership) except +:
"""
Convert a numpy ndarray to a matrix.
Convert a numpy ndarray to a matrix. The memory will still be owned by numpy.
"""
if not (X.flags.c_contiguous or X.flags.owndata):
# If needed, make a copy where we own the memory.
X = X.copy(order="C")
takeOwnership = True
cdef arma.Mat[double]* m = new arma.Mat[double](<double*> X.data, X.shape[1], X.shape[0], False, True)
cdef arma.Mat[double]* m = new arma.Mat[double](<double*> X.data, X.shape[1],\
X.shape[0], False, False)
# Transfer ownership to the Armadillo matrix.
PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA)
SetMemState[arma.Mat[double]](m[0], 0)
# Take ownership of the memory, if we need to.
if takeOwnership:
PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA)
SetMemState[arma.Mat[double]](m[0], 0)
return m
cdef arma.Mat[size_t]* numpy_to_mat_s(numpy.ndarray[numpy.npy_intp, ndim=2] X) \
except +:
cdef arma.Mat[size_t]* numpy_to_mat_s(numpy.ndarray[numpy.npy_intp, ndim=2] X, \
bool takeOwnership) except +:
"""
Convert a numpy ndarray to a matrix.
Convert a numpy ndarray to a matrix. The memory will still be owned by numpy.
"""
if not (X.flags.c_contiguous or X.flags.owndata):
# If needed, make a copy where we own the memory.
X = X.copy(order="C")
takeOwnership = True
cdef arma.Mat[size_t]* m = new arma.Mat[size_t](<size_t*> X.data, X.shape[1],
X.shape[0], False, True)
X.shape[0], False, False)
# Transfer ownership to the Armadillo matrix.
PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA)
SetMemState[arma.Mat[size_t]](m[0], 0)
# Take ownership of the memory, if we need to.
if takeOwnership:
PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA)
SetMemState[arma.Mat[size_t]](m[0], 0)
return m
@@ -85,9 +92,10 @@ cdef numpy.ndarray[numpy.double_t, ndim=2] mat_to_numpy_d(arma.Mat[double]& X) \
cdef numpy.ndarray[numpy.double_t, ndim=2] output = \
numpy.PyArray_SimpleNewFromData(2, &dims[0], numpy.NPY_DOUBLE, GetMemory(X))
# Transfer memory ownership.
SetMemState[arma.Mat[double]](X, 1)
PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA)
# Transfer memory ownership, if needed.
if GetMemState[arma.Mat[double]](X) == 0:
SetMemState[arma.Mat[double]](X, 1)
PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA)
return output
@@ -103,45 +111,52 @@ cdef numpy.ndarray[numpy.npy_intp, ndim=2] mat_to_numpy_s(arma.Mat[size_t]& X) \
cdef numpy.ndarray[numpy.npy_intp, ndim=2] output = \
numpy.PyArray_SimpleNewFromData(2, &dims[0], numpy.NPY_INTP, GetMemory(X))
# Transfer memory ownership.
SetMemState[arma.Mat[size_t]](X, 1)
PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA)
# Transfer memory ownership, if needed.
if GetMemState[arma.Mat[size_t]](X) == 0:
SetMemState[arma.Mat[size_t]](X, 1)
PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA)
return output
cdef arma.Row[double]* numpy_to_row_d(numpy.ndarray[numpy.double_t, ndim=1] X) \
except +:
cdef arma.Row[double]* numpy_to_row_d(numpy.ndarray[numpy.double_t, ndim=1] X, \
bool takeOwnership) except +:
"""
Convert a numpy one-dimensional ndarray to a row.
Convert a numpy one-dimensional ndarray to a row. The memory will still be
owned by numpy.
"""
if not (X.flags.c_contiguous or X.flags.owndata):
# If needed, make a copy where we own the memory.
X = X.copy(order="C")
takeOwnership = True
cdef arma.Row[double]* m = new arma.Row[double](<double*> X.data, X.shape[0],
False, True)
False, False)
# Transfer ownership to the Armadillo matrix.
PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA)
SetMemState[arma.Row[double]](m[0], 0)
# Transfer memory ownership, if needed.
if takeOwnership:
PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA)
SetMemState[arma.Row[double]](m[0], 0)
return m
cdef arma.Row[size_t]* numpy_to_row_s(numpy.ndarray[numpy.npy_intp, ndim=1] X) \
except +:
cdef arma.Row[size_t]* numpy_to_row_s(numpy.ndarray[numpy.npy_intp, ndim=1] X, \
bool takeOwnership) except +:
"""
Convert a numpy one-dimensional ndarray to a row.
Convert a numpy one-dimensional ndarray to a row. The memory will still be
owned by numpy.
"""
if not (X.flags.c_contiguous or X.flags.owndata):
# If needed, make a copy where we own the memory.
X = X.copy(order="C")
takeOwnership = True
cdef arma.Row[size_t]* m = new arma.Row[size_t](<size_t*> X.data, X.shape[0],
False, True)
False, False)
# Transfer ownership to the Armadillo matrix.
PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA)
SetMemState[arma.Row[size_t]](m[0], 0)
# Transfer memory ownership, if needed.
if takeOwnership:
PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA)
SetMemState[arma.Row[size_t]](m[0], 0)
return m
@@ -155,9 +170,10 @@ cdef numpy.ndarray[numpy.double_t, ndim=1] row_to_numpy_d(arma.Row[double]& X) \
cdef numpy.ndarray[numpy.double_t, ndim=1] output = \
numpy.PyArray_SimpleNewFromData(1, &dim, numpy.NPY_DOUBLE, GetMemory(X))
# Transfer memory ownership.
SetMemState[arma.Row[double]](X, 1)
PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA)
# Transfer memory ownership, if needed.
if GetMemState[arma.Row[double]](X) == 0:
SetMemState[arma.Row[double]](X, 1)
PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA)
return output
@@ -171,45 +187,51 @@ cdef numpy.ndarray[numpy.npy_intp, ndim=1] row_to_numpy_s(arma.Row[size_t]& X) \
cdef numpy.ndarray[numpy.npy_intp, ndim=1] output = \
numpy.PyArray_SimpleNewFromData(1, &dim, numpy.NPY_INTP, GetMemory(X))
# Transfer memory ownership.
SetMemState[arma.Row[size_t]](X, 1)
PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA)
# Transfer memory ownership, if needed.
if GetMemState[arma.Row[size_t]](X) == 0:
SetMemState[arma.Row[size_t]](X, 1)
PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA)
return output
cdef arma.Col[double]* numpy_to_col_d(numpy.ndarray[numpy.double_t, ndim=1] X) \
except +:
cdef arma.Col[double]* numpy_to_col_d(numpy.ndarray[numpy.double_t, ndim=1] X, \
bool takeOwnership) except +:
"""
Convert a numpy one-dimensional ndarray to a column vector.
Convert a numpy one-dimensional ndarray to a column vector. The memory will
still be owned by numpy.
"""
if not (X.flags.c_contiguous or X.flags.owndata):
# If needed, make a copy where we own the memory.
X = X.copy(order="C")
takeOwnership = True
cdef arma.Col[double]* m = new arma.Col[double](<double*> X.data, X.shape[0],
False, True)
# Transfer ownership to the Armadillo matrix.
PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA)
SetMemState[arma.Col[double]](m[0], 0)
# Transfer memory ownership, if needed.
if takeOwnership:
PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA)
SetMemState[arma.Col[double]](m[0], 0)
return m
cdef arma.Col[size_t]* numpy_to_col_s(numpy.ndarray[numpy.npy_intp, ndim=1] X) \
except +:
cdef arma.Col[size_t]* numpy_to_col_s(numpy.ndarray[numpy.npy_intp, ndim=1] X, \
bool takeOwnership) except +:
"""
Convert a numpy one-dimensional ndarray to a column vector.
Convert a numpy one-dimensional ndarray to a column vector. The memory will
still be owned by numpy.
"""
if not (X.flags.c_contiguous or X.flags.owndata):
# If needed, make a copy where we own the memory.
X = X.copy(order="C")
cdef arma.Col[size_t]* m = new arma.Col[size_t](<size_t*> X.data, X.shape[0],
False, True)
False, False)
# Transfer ownership to the Armadillo matrix.
PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA)
SetMemState[arma.Col[size_t]](m[0], 0)
# Transfer memory ownership, if needed.
if takeOwnership:
PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA)
SetMemState[arma.Col[size_t]](m[0], 0)
return m
@@ -223,9 +245,10 @@ cdef numpy.ndarray[numpy.double_t, ndim=1] col_to_numpy_d(arma.Col[double]& X) \
cdef numpy.ndarray[numpy.double_t, ndim=1] output = \
numpy.PyArray_SimpleNewFromData(1, &dim, numpy.NPY_DOUBLE, GetMemory(X))
# Transfer memory ownership.
SetMemState[arma.Col[double]](X, 1)
PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA)
# Transfer memory ownership, if needed.
if GetMemState[arma.Col[double]](X) == 0:
SetMemState[arma.Col[double]](X, 1)
PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA)
return output
@@ -239,8 +262,9 @@ cdef numpy.ndarray[numpy.npy_intp, ndim=1] col_to_numpy_s(arma.Col[size_t]& X) \
cdef numpy.ndarray[numpy.npy_intp, ndim=1] output = \
numpy.PyArray_SimpleNewFromData(1, &dim, numpy.NPY_INTP, GetMemory(X))
# Transfer memory ownership.
SetMemState[arma.Col[size_t]](X, 1)
PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA)
# Transfer memory ownership, if needed.
if GetMemState[arma.Col[size_t]](X) == 0:
SetMemState[arma.Col[size_t]](X, 1)
PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA)
return output
@@ -24,6 +24,20 @@ void SetMemState(T& t, int state)
const_cast<arma::uhword&>(t.mem_state) = state;
}
/**
* Get the memory state of the given Armadillo object.
*/
template<typename T>
size_t GetMemState(T& t)
{
// Fake the memory state if we are using preallocated memory---since we will
// end up copying that memory, NumPy can own it.
if (t.mem && t.n_elem <= arma::arma_config::mat_prealloc)
return 0;
return (size_t) t.mem_state;
}
/**
* Return the matrix's allocated memory pointer, unless the matrix is using its
* internal preallocated memory, in which case we copy that and return a
+7 -7
View File
@@ -20,6 +20,9 @@ cdef extern from "<mlpack/core/util/cli.hpp>" namespace "mlpack" nogil:
@staticmethod
(T&) GetParam[T](string) nogil except +
@staticmethod
bool HasParam(string) nogil except +
@staticmethod
void SetPassed(string) nogil except +
@@ -37,16 +40,13 @@ cdef extern from "<mlpack/core/util/cli.hpp>" namespace "mlpack" nogil:
cdef extern from "<mlpack/bindings/python/mlpack/cli_util.hpp>" \
namespace "mlpack::util" nogil:
void SetParam[T](string, const T&) nogil except +
void SetParamWithInfo[T](string, const T&, const bool*) nogil except +
void SetParam[T](string, T&) nogil except +
void SetParamPtr[T](string, T*, bool) nogil except +
void SetParamWithInfo[T](string, T&, const bool*) nogil except +
(T*) GetParamPtr[T](string) nogil except +
(T&) GetParamWithInfo[T](string) nogil except +
void EnableVerbose() nogil except +
void DisableVerbose() nogil except +
void DisableBacktrace() nogil except +
void ResetTimers() nogil except +
void EnableTimers() nogil except +
cdef extern from "<mlpack/bindings/python/mlpack/move.hpp>" \
namespace "mlpack::util" nogil:
void MoveFromPtr[T](T&, T*) nogil except +
void MoveToPtr[T](T*, T&) nogil except +
+38 -8
View File
@@ -29,9 +29,27 @@ namespace util {
* @param value Value to set parameter to.
*/
template<typename T>
inline void SetParam(const std::string& identifier, const T& value)
inline void SetParam(const std::string& identifier, T& value)
{
CLI::GetParam<T>(identifier) = value;
CLI::GetParam<T>(identifier) = std::move(value);
}
/**
* Set the parameter to the given value, given that the type is a pointer.
*
* This function exists to work around both Cython's lack of support for lvalue
* references and also its seeming lack of support for template pointer types.
*
* @param identifier Name of parameter.
* @param value Value to set parameter to.
* @param copy Whether or not the object should be copied.
*/
template<typename T>
inline void SetParamPtr(const std::string& identifier,
T* value,
const bool copy)
{
CLI::GetParam<T*>(identifier) = copy ? new T(*value) : value;
}
/**
@@ -39,19 +57,20 @@ inline void SetParam(const std::string& identifier, const T& value)
*/
template<typename T>
inline void SetParamWithInfo(const std::string& identifier,
const T& matrix,
T& matrix,
const bool* dims)
{
typedef typename std::tuple<data::DatasetInfo, T> TupleType;
typedef typename T::elem_type eT;
// The true type of the parameter is std::tuple<T, DatasetInfo>.
std::get<1>(CLI::GetParam<TupleType>(identifier)) = matrix;
const size_t dimensions = matrix.n_rows;
std::get<1>(CLI::GetParam<TupleType>(identifier)) = std::move(matrix);
data::DatasetInfo& di = std::get<0>(CLI::GetParam<TupleType>(identifier));
di = data::DatasetInfo(matrix.n_rows);
di = data::DatasetInfo(dimensions);
bool hasCategoricals = false;
for (size_t i = 0; i < matrix.n_rows; ++i)
for (size_t i = 0; i < dimensions; ++i)
{
if (dims[i])
{
@@ -63,9 +82,10 @@ inline void SetParamWithInfo(const std::string& identifier,
// Do we need to find how many categories we have?
if (hasCategoricals)
{
arma::vec maxs = arma::max(matrix, 1);
arma::vec maxs = arma::max(
std::get<1>(CLI::GetParam<TupleType>(identifier)), 1);
for (size_t i = 0; i < matrix.n_rows; ++i)
for (size_t i = 0; i < dimensions; ++i)
{
if (dims[i])
{
@@ -81,6 +101,16 @@ inline void SetParamWithInfo(const std::string& identifier,
}
}
/**
* Return a pointer. This function exists to work around Cython's seeming lack
* of support for template pointer types.
*/
template<typename T>
T* GetParamPtr(const std::string& paramName)
{
return CLI::GetParam<T*>(paramName);
}
/**
* Return the matrix part of a matrix + dataset info parameter.
*/
@@ -37,7 +37,7 @@ try:
except:
buffer = memoryview
def to_matrix(x, dtype=np.double):
def to_matrix(x, dtype=np.double, copy=False):
"""
Given some array-like X, return a numpy ndarray of the same type.
"""
@@ -48,11 +48,14 @@ def to_matrix(x, dtype=np.double):
raise TypeError("given argument is not array-like")
if (isinstance(x, np.ndarray) and x.dtype == dtype and x.flags.c_contiguous):
return x
if copy: # Copy the matrix if required.
return x.copy("C"), True
else:
return x, False
else:
return np.array(x, copy=True, dtype=dtype, order='C')
return np.array(x, copy=True, dtype=dtype, order='C'), True
def to_matrix_with_info(x, dtype):
def to_matrix_with_info(x, dtype, copy=False):
"""
Given some array-like X (which should be either a numpy ndarray or a pandas
DataFrame, convert into a numpy matrix of the given dtype.
@@ -66,7 +69,12 @@ def to_matrix_with_info(x, dtype):
if isinstance(x, np.ndarray):
# It is already an ndarray, so the vector of info is all 0s (all numeric).
d = np.zeros([x.shape[1]], dtype=np.bool)
return (x, d)
# Copy the matrix if needed.
if copy:
return (x.copy(order="C"), True, d)
else:
return (x, False, d)
if isinstance(x, pd.DataFrame) or isinstance(x, pd.Series):
# It's a pandas dataframe. So we need to see if any of the dtypes are
@@ -79,8 +87,9 @@ def to_matrix_with_info(x, dtype):
not np.dtype(str) in dtype_array and \
not np.dtype(unicode) in dtype_array:
# We can just return the matrix as-is; it's all numeric.
t = to_matrix(x, dtype=dtype, copy=copy)
d = np.zeros([x.shape[1]], dtype=np.bool)
return (to_matrix(x), d)
return (t[0], t[1], d)
if np.dtype(str) in dtype_array or np.dtype(unicode) in dtype_array:
raise TypeError('cannot convert matrices with string types')
@@ -115,7 +124,10 @@ def to_matrix_with_info(x, dtype):
catColumnIndices = [y.columns.get_loc(i) for i in catColumns]
d[catColumnIndices] = 1
return (to_matrix(y.apply(pd.to_numeric)), d)
# We'll have to force the second part of the tuple (whether or not to take
# ownership) to true.
t = to_matrix(y.apply(pd.to_numeric), dtype=dtype)
return (t[0], True, d)
if isinstance(x, list):
# Get the number of dimensions.
@@ -126,7 +138,18 @@ def to_matrix_with_info(x, dtype):
dims = len(x)
d = np.zeros([dims])
return (np.array(x, dtype=dtype), d)
out = np.array(x, dtype=dtype, copy=copy) # Try to avoid copy...
# Since we don't have a great way to check if these are using the same
# memory location, we will probe manually (ugh).
oldval = x[0]
x[0] *= 2
alias = False
if out[0] == x[0]:
alias = True
x[0] = oldval
return (out, not alias, d)
# If we got here, the type is not known.
raise TypeError("given matrix is not a numpy ndarray or pandas DataFrame or "\
@@ -1,30 +0,0 @@
/**
* @file move.hpp
* @author Ryan Curtin
*
* Utility function for Cython to use std::move.
*/
#ifndef MLPACK_BINDINGS_PYTHON_CYTHON_MOVE_HPP
#define MLPACK_BINDINGS_PYTHON_CYTHON_MOVE_HPP
#include <utility>
namespace mlpack {
namespace util {
template<typename T>
void MoveToPtr(T* dest, T& src)
{
*(dest) = std::move(src);
}
template<typename T>
void MoveFromPtr(T& dest, T* src)
{
dest = std::move(*src);
}
} // namespace util
} // namespace mlpack
#endif
@@ -108,7 +108,7 @@ void PrintClassDefn(const util::ParamData& d,
const void* /* input */,
void* /* output */)
{
PrintClassDefn<T>(d);
PrintClassDefn<typename std::remove_pointer<T>::type>(d);
}
} // namespace python
+21 -1
View File
@@ -39,7 +39,27 @@ void PrintDoc(const util::ParamData& d,
oss << d.name << "_ (";
else
oss << d.name << " (";
oss << GetPythonType<T>(d) << "): " << d.desc;
oss << GetPythonType<typename std::remove_pointer<T>::type>(d) << "): "
<< d.desc;
// Print a default, if possible.
if (!d.required)
{
if (d.cppType == "std::string")
{
oss << " Default value '" << boost::any_cast<std::string>(d.value)
<< "'.";
}
else if (d.cppType == "double")
{
oss << " Default value " << boost::any_cast<double>(d.value) << ".";
}
else if (d.cppType == "int")
{
oss << " Default value " << boost::any_cast<int>(d.value) << ".";
}
}
std::cout << util::HyphenateString(oss.str(), indent + 4);
}
@@ -31,6 +31,11 @@ void PrintInputProcessing(
const typename boost::disable_if<std::is_same<T,
std::tuple<data::DatasetInfo, arma::mat>>>::type* = 0)
{
// The copy_all_inputs parameter must be handled first, and therefore is
// outside the scope of this code.
if (d.name == "copy_all_inputs")
return;
const std::string prefix(indent, ' ');
std::string def = "None";
@@ -104,7 +109,9 @@ void PrintInputProcessing(
*
* # Detect if the parameter was passed; set if so.
* if param_name is not None:
* param_name_mat = arma_numpy.numpy_to_mat_d(param_name)
* param_name_tuple = to_matrix(param_name)
* param_name_mat = arma_numpy.numpy_to_mat_d(param_name_tuple[0],
* param_name_tuple[1])
* SetParam[mat](<const string> 'param_name', dereference(param_name_mat))
* CLI.SetPassed(<const string> 'param_name')
*/
@@ -114,27 +121,33 @@ void PrintInputProcessing(
{
std::cout << prefix << "if " << d.name << " is not None:" << std::endl;
std::cout << prefix << " " << d.name << "_tuple = to_matrix(" << d.name
<< ", dtype=" << GetNumpyType<typename T::elem_type>() << ", "
<< "copy=CLI.HasParam('copy_all_inputs'))" << std::endl;
std::cout << prefix << " " << d.name << "_mat = arma_numpy.numpy_to_"
<< GetArmaType<T>() << "_" << GetNumpyTypeChar<T>() << "(to_matrix("
<< d.name << ", " << "dtype=" << GetNumpyType<typename T::elem_type>()
<< "))" << std::endl;
<< GetArmaType<T>() << "_" << GetNumpyTypeChar<T>() << "(" << d.name
<< "_tuple[0], " << d.name << "_tuple[1])" << std::endl;
std::cout << prefix << " SetParam[" << GetCythonType<T>(d) << "](<const "
<< "string> '" << d.name << "', dereference(" << d.name << "_mat))"
<< std::endl;
std::cout << prefix << " CLI.SetPassed(<const string> '" << d.name << "')"
<< std::endl;
std::cout << prefix << " del " << d.name << "_mat";
}
else
{
std::cout << prefix << d.name << "_tuple = to_matrix(" << d.name
<< ", dtype=" << GetNumpyType<typename T::elem_type>() << ", "
<< "copy=CLI.HasParam('copy_all_inputs'))" << std::endl;
std::cout << prefix << d.name << "_mat = arma_numpy.numpy_to_"
<< GetArmaType<T>() << "_" << GetNumpyTypeChar<T>() << "(to_matrix("
<< d.name << ", " << "dtype=" << GetNumpyType<typename T::elem_type>()
<< "))" << std::endl;
<< GetArmaType<T>() << "_" << GetNumpyTypeChar<T>() << "(" << d.name
<< "_tuple[0], " << d.name << "_tuple[1])" << std::endl;
std::cout << prefix << "SetParam[" << GetCythonType<T>(d) << "](<const "
<< "string> '" << d.name << "', dereference(" << d.name << "_mat))"
<< std::endl;
std::cout << prefix << "CLI.SetPassed(<const string> '" << d.name << "')"
<< std::endl;
std::cout << prefix << "del " << d.name << "_mat";
}
std::cout << std::endl;
}
@@ -161,12 +174,12 @@ void PrintInputProcessing(
* # Detect if the parameter was passed; set if so.
* if param_name is not None:
* try:
* MoveFromPtr[Model](CLI.GetParam[Model]('param_name'),
* (<ModelType?> param_name).modelptr)
* SetParamPtr[Model]('param_name', (<ModelType?> param_name).modelptr,
* CLI.HasParam('copy_all_inputs'))
* except TypeError as e:
* if type(param_name).__name__ == "ModelType":
* MoveFromPtr[Model](CLI.GetParam[Model]('param_name'),
* (<ModelType> param_name).modelptr)
* SetParamPtr[Model]('param_name', (<ModelType> param_name).modelptr,
* CLI.HasParam('copy_all_inputs'))
* else:
* raise e
* CLI.SetPassed(<const string> 'param_name')
@@ -177,15 +190,15 @@ void PrintInputProcessing(
{
std::cout << prefix << "if " << d.name << " is not None:" << std::endl;
std::cout << prefix << " try:" << std::endl;
std::cout << prefix << " MoveFromPtr[" << strippedType
<< "](CLI.GetParam[" << strippedType << "]('" << d.name << "'), (<"
<< strippedType << "Type?> " << d.name << ").modelptr)" << std::endl;
std::cout << prefix << " SetParamPtr[" << strippedType << "]('" << d.name
<< "', (<" << strippedType << "Type?> " << d.name << ").modelptr, "
<< "CLI.HasParam('copy_all_inputs'))" << std::endl;
std::cout << prefix << " except TypeError as e:" << std::endl;
std::cout << prefix << " if type(" << d.name << ").__name__ == '"
<< strippedType << "Type':" << std::endl;
std::cout << prefix << " MoveFromPtr[" << strippedType
<< "](CLI.GetParam[" << strippedType << "]('" << d.name << "'), (<"
<< strippedType << "Type> " << d.name << ").modelptr)" << std::endl;
std::cout << prefix << " SetParamPtr[" << strippedType << "]('"
<< d.name << "', (<" << strippedType << "Type> " << d.name
<< ").modelptr, CLI.HasParam('copy_all_inputs'))" << std::endl;
std::cout << prefix << " else:" << std::endl;
std::cout << prefix << " raise e" << std::endl;
std::cout << prefix << " CLI.SetPassed(<const string> '" << d.name << "')"
@@ -194,15 +207,15 @@ void PrintInputProcessing(
else
{
std::cout << prefix << "try:" << std::endl;
std::cout << prefix << " MoveFromPtr[" << strippedType << "](CLI.GetParam["
<< strippedType << "]('" << d.name << "'), (<" << strippedType
<< "Type?> " << d.name << ").modelptr)" << std::endl;
std::cout << prefix << " SetParamPtr[" << strippedType << "]('" << d.name
<< "', (<" << strippedType << "Type?> " << d.name << ").modelptr, "
<< "CLI.HasParam('copy_all_inputs'))" << std::endl;
std::cout << prefix << "except TypeError as e:" << std::endl;
std::cout << prefix << " if type(" << d.name << ").__name__ == '"
<< strippedType << "Type':" << std::endl;
std::cout << prefix << " MoveFromPtr[" << strippedType
<< "](CLI.GetParam[" << strippedType << "]('" << d.name << "'), (<"
<< strippedType << "Type> " << d.name << ").modelptr)" << std::endl;
std::cout << prefix << " SetParamPtr[" << strippedType << "]('" << d.name
<< "', (<" << strippedType << "Type> " << d.name << ").modelptr, "
<< "CLI.HasParam('copy_all_inputs'))" << std::endl;
std::cout << prefix << " else:" << std::endl;
std::cout << prefix << " raise e" << std::endl;
std::cout << prefix << "CLI.SetPassed(<const string> '" << d.name << "')"
@@ -240,30 +253,34 @@ void PrintInputProcessing(
{
std::cout << prefix << "if " << d.name << " is not None:" << std::endl;
std::cout << prefix << " " << d.name << "_tuple = to_matrix_with_info("
<< d.name << ", dtype=np.double)" << std::endl;
<< d.name << ", dtype=np.double, copy=CLI.HasParam('copy_all_inputs'))"
<< std::endl;
std::cout << prefix << " " << d.name << "_mat = arma_numpy.numpy_to_mat_d("
<< d.name << "_tuple[0])" << std::endl;
std::cout << prefix << " " << d.name << "_dims = " << d.name << "_tuple[1]"
<< d.name << "_tuple[0], " << d.name << "_tuple[1])" << std::endl;
std::cout << prefix << " " << d.name << "_dims = " << d.name << "_tuple[2]"
<< std::endl;
std::cout << prefix << " SetParamWithInfo[arma.Mat[double]](<const string>"
<< " '" << d.name << "', dereference(" << d.name << "_mat), <const "
<< "bool*> " << d.name << "_dims.data)" << std::endl;
std::cout << prefix << " CLI.SetPassed(<const string> '" << d.name << "')"
<< std::endl;
std::cout << prefix << " del " << d.name << "_mat" << std::endl;
}
else
{
std::cout << prefix << d.name << "_tuple = to_matrix_with_info(" << d.name
<< ", dtype=np.double)" << std::endl;
<< ", dtype=np.double, copy=CLI.HasParam('copy_all_inputs'))"
<< std::endl;
std::cout << prefix << d.name << "_mat = arma_numpy.numpy_to_mat_d("
<< d.name << "_tuple[0])" << std::endl;
std::cout << prefix << d.name << "_dims = " << d.name << "_tuple[1]"
<< d.name << "_tuple[0], " << d.name << "_tuple[1])" << std::endl;
std::cout << prefix << d.name << "_dims = " << d.name << "_tuple[2]"
<< std::endl;
std::cout << prefix << "SetParamWithInfo[arma.Mat[double]](<const string>"
<< " '" << d.name << "', dereference(" << d.name << "_mat), <const "
<< "bool*> " << d.name << "_dims.data)" << std::endl;
std::cout << prefix << "CLI.SetPassed(<const string> '" << d.name << "')"
<< std::endl;
std::cout << prefix << "del " << d.name << "_mat" << std::endl;
}
std::cout << std::endl;
}
@@ -284,7 +301,8 @@ void PrintInputProcessing(const util::ParamData& d,
const void* input,
void* /* output */)
{
PrintInputProcessing<T>(d, *((size_t*) input));
PrintInputProcessing<typename std::remove_pointer<T>::type>(d,
*((size_t*) input));
}
} // namespace python
@@ -180,13 +180,47 @@ void PrintOutputProcessing(
* This gives us code like:
*
* result = ModelType()
* MoveToPtr[Model]((<ModelType?> model).modelptr),
* CLI.GetParam[Model]('name'))
* (<ModelType?> result).modelptr = GetParamPtr[Model]('name')
*/
std::cout << prefix << "result = " << strippedType << "Type()" << std::endl;
std::cout << prefix << "MoveToPtr[" << strippedType << "]((<"
<< strippedType << "Type?> result).modelptr, CLI.GetParam["
<< strippedType << "]('" << d.name << "'))" << std::endl;
std::cout << prefix << "(<" << strippedType << "Type?> result).modelptr = "
<< "GetParamPtr[" << strippedType << "]('" << d.name << "')"
<< std::endl;
/**
* But we also have to check to ensure there aren't any input model
* parameters of the same type that could have the same model pointer.
* So we need to loop through all input parameters that have the same type,
* and double-check.
*/
std::map<std::string, util::ParamData>& parameters = CLI::Parameters();
for (auto it = parameters.begin(); it != parameters.end(); ++it)
{
// Is it an input parameter of the same type?
const util::ParamData& data = it->second;
if (data.input && data.cppType == d.cppType && data.required)
{
std::cout << prefix << "if (<" << strippedType
<< "Type> result).modelptr" << d.name << " == (<" << strippedType
<< "Type> " << data.name << ").modelptr:" << std::endl;
std::cout << prefix << " (<" << strippedType
<< "Type> result).modelptr = <" << strippedType << "*> 0"
<< std::endl;
std::cout << prefix << " result = " << data.name << std::endl;
}
else if (data.input && data.cppType == d.cppType)
{
std::cout << prefix << "if " << data.name << " is not None:"
<< std::endl;
std::cout << prefix << " if (<" << strippedType
<< "Type> result).modelptr" << d.name << " == (<" << strippedType
<< "Type> " << data.name << ").modelptr:" << std::endl;
std::cout << prefix << " (<" << strippedType
<< "Type> result).modelptr = <" << strippedType << "*> 0"
<< std::endl;
std::cout << prefix << " result = " << data.name << std::endl;
}
}
}
else
{
@@ -194,15 +228,50 @@ void PrintOutputProcessing(
* This gives us code like:
*
* result['name'] = ModelType()
* MoveToPtr[Model*]((<ModelType?> result['name']).modelptr),
* CLI.GetParam[Model]('name'))
* (<ModelType?> result['name']).modelptr = GetParamPtr[Model]('name'))
*/
std::cout << prefix << "result['" << d.name << "'] = " << strippedType
<< "Type()" << std::endl;
std::cout << prefix << "MoveToPtr[" << strippedType << "]((<"
<< strippedType << "Type?>" << " result['" << d.name
<< "']).modelptr, CLI.GetParam[" << strippedType << "]('"
<< d.name << "'))" << std::endl;
std::cout << prefix << "(<" << strippedType << "Type?> result['" << d.name
<< "']).modelptr = GetParamPtr[" << strippedType << "]('" << d.name
<< "')" << std::endl;
/**
* But we also have to check to ensure there aren't any input model
* parameters of the same type that could have the same model pointer.
* So we need to loop through all input parameters that have the same type,
* and double-check.
*/
std::map<std::string, util::ParamData>& parameters = CLI::Parameters();
for (auto it = parameters.begin(); it != parameters.end(); ++it)
{
// Is it an input parameter of the same type?
const util::ParamData& data = it->second;
if (data.input && data.cppType == d.cppType && data.required)
{
std::cout << prefix << "if (<" << strippedType << "Type> result['"
<< d.name << "']).modelptr == (<" << strippedType << "Type> "
<< data.name << ").modelptr:" << std::endl;
std::cout << prefix << " (<" << strippedType << "Type> result['"
<< d.name << "']).modelptr = <" << strippedType << "*> 0"
<< std::endl;
std::cout << prefix << " result['" << d.name << "'] = " << data.name
<< std::endl;
}
else if (data.input && data.cppType == d.cppType)
{
std::cout << prefix << "if " << data.name << " is not None:"
<< std::endl;
std::cout << prefix << " if (<" << strippedType << "Type> result['"
<< d.name << "']).modelptr == (<" << strippedType << "Type> "
<< data.name << ").modelptr:" << std::endl;
std::cout << prefix << " (<" << strippedType << "Type> result['"
<< d.name << "']).modelptr = <" << strippedType << "*> 0"
<< std::endl;
std::cout << prefix << " result['" << d.name << "'] = " << data.name
<< std::endl;
}
}
}
}
@@ -227,7 +296,8 @@ void PrintOutputProcessing(const util::ParamData& d,
{
std::tuple<size_t, bool>* tuple = (std::tuple<size_t, bool>*) input;
PrintOutputProcessing<T>(d, std::get<0>(*tuple), std::get<1>(*tuple));
PrintOutputProcessing<typename std::remove_pointer<T>::type>(d,
std::get<0>(*tuple), std::get<1>(*tuple));
}
} // namespace python
+10 -3
View File
@@ -71,10 +71,10 @@ void PrintPYX(const ProgramDoc& programInfo,
cout << "cimport arma" << endl;
cout << "cimport arma_numpy" << endl;
cout << "from cli cimport CLI" << endl;
cout << "from cli cimport SetParam, SetParamWithInfo" << endl;
cout << "from cli cimport SetParam, SetParamPtr, SetParamWithInfo, "
<< "GetParamPtr" << endl;
cout << "from cli cimport EnableVerbose, DisableVerbose, DisableBacktrace, "
<< "ResetTimers, EnableTimers" << endl;
cout << "from cli cimport MoveFromPtr, MoveToPtr" << endl;
cout << "from matrix_utils import to_matrix, to_matrix_with_info" << endl;
cout << "from serialization cimport SerializeIn, SerializeOut" << endl;
cout << endl;
@@ -177,7 +177,14 @@ void PrintPYX(const ProgramDoc& programInfo,
cout << " DisableVerbose()" << endl;
// Restore the parameters.
cout << " CLI.RestoreSettings(\"" << programInfo.programName << "\")";
cout << " CLI.RestoreSettings(\"" << programInfo.programName << "\")"
<< endl;
// Determine whether or not we need to copy parameters.
cout << " if copy_all_inputs:" << endl;
cout << " SetParam[bool](<const string> 'copy_all_inputs', "
<< "copy_all_inputs)" << endl;
cout << " CLI.SetPassed(<const string> 'copy_all_inputs')" << endl;
// Do any input processing.
for (size_t i = 0; i < inputOptions.size(); ++i)
+2 -2
View File
@@ -58,8 +58,8 @@ class PyOption
data.required = required;
data.input = input;
data.loaded = false;
// Only "verbose" will be persistent.
if (identifier == "verbose")
// Only "verbose" and "copy_all_inputs" will be persistent.
if (identifier == "verbose" || identifier == "copy_all_inputs")
data.persistent = true;
else
data.persistent = false;
@@ -23,7 +23,7 @@ class TestToMatrix(unittest.TestCase):
"""
d = pd.DataFrame(np.random.randn(100, 4), columns=list('abcd'))
m = to_matrix(d)
m, _ = to_matrix(d)
self.assertTrue(isinstance(m, np.ndarray))
self.assertEqual(m.shape[0], 100)
@@ -40,7 +40,7 @@ class TestToMatrix(unittest.TestCase):
"""
d = pd.DataFrame({'a': range(5)})
m = to_matrix(d)
m, _ = to_matrix(d)
self.assertTrue(isinstance(m, np.ndarray))
self.assertEqual(m.shape[0], 5)
@@ -58,7 +58,7 @@ class TestToMatrix(unittest.TestCase):
self.assertEqual(d['a'].dtype, int)
self.assertEqual(d['b'].dtype, np.dtype(np.double))
m = to_matrix(d)
m, _ = to_matrix(d)
self.assertTrue(isinstance(m, np.ndarray))
self.assertEqual(m.dtype, np.dtype(np.double))
@@ -79,7 +79,7 @@ class TestToMatrix(unittest.TestCase):
[0.07, 0.08, 0.09],
[0.10, 0.11, 0.12]]
m = to_matrix(a)
m, _ = to_matrix(a)
self.assertTrue(isinstance(m, np.ndarray))
self.assertEqual(m.dtype, np.dtype(np.double))
@@ -100,7 +100,7 @@ class TestToMatrix(unittest.TestCase):
[0.07, 0.08, 9],
[0.10, 0.11, 12]]
m = to_matrix(a)
m, _ = to_matrix(a)
self.assertTrue(isinstance(m, np.ndarray))
self.assertEqual(m.dtype, np.dtype(np.double))
@@ -116,7 +116,7 @@ class TestToMatrix(unittest.TestCase):
Make sure we can convert a numpy matrix without copying anything.
"""
m1 = np.random.randn(100, 5)
m2 = to_matrix(m1)
m2, _ = to_matrix(m1)
self.assertTrue(isinstance(m2, np.ndarray))
self.assertEqual(m2.dtype, np.dtype(np.double))
@@ -144,7 +144,7 @@ class TestToMatrixWithInfo(unittest.TestCase):
"""
d = pd.DataFrame(np.random.randn(100, 4), columns=list('abcd'))
m, dims = to_matrix_with_info(d, np.double)
m, _, dims = to_matrix_with_info(d, np.double)
self.assertTrue(isinstance(m, np.ndarray))
self.assertEqual(m.shape[0], 100)
@@ -167,7 +167,7 @@ class TestToMatrixWithInfo(unittest.TestCase):
"""
d = pd.DataFrame({'a': range(5)})
m, dims = to_matrix_with_info(d, np.double)
m, _, dims = to_matrix_with_info(d, np.double)
self.assertTrue(isinstance(m, np.ndarray))
self.assertEqual(m.shape[0], 5)
@@ -188,7 +188,7 @@ class TestToMatrixWithInfo(unittest.TestCase):
self.assertEqual(d['a'].dtype, int)
self.assertEqual(d['b'].dtype, np.dtype(np.double))
m, dims = to_matrix_with_info(d, np.double)
m, _, dims = to_matrix_with_info(d, np.double)
self.assertTrue(isinstance(m, np.ndarray))
self.assertEqual(m.dtype, np.dtype(np.double))
@@ -213,7 +213,7 @@ class TestToMatrixWithInfo(unittest.TestCase):
[0.07, 0.08, 0.09],
[0.10, 0.11, 0.12]]
m, dims = to_matrix_with_info(a, np.double)
m, _, dims = to_matrix_with_info(a, np.double)
self.assertTrue(isinstance(m, np.ndarray))
self.assertEqual(m.dtype, np.dtype(np.double))
@@ -239,7 +239,7 @@ class TestToMatrixWithInfo(unittest.TestCase):
[0.07, 0.08, 9],
[0.10, 0.11, 12]]
m, dims = to_matrix_with_info(a, np.double)
m, _, dims = to_matrix_with_info(a, np.double)
self.assertTrue(isinstance(m, np.ndarray))
self.assertEqual(m.dtype, np.dtype(np.double))
@@ -260,7 +260,7 @@ class TestToMatrixWithInfo(unittest.TestCase):
Make sure we can convert a numpy matrix without copying anything.
"""
m1 = np.random.randn(100, 5)
m2, dims = to_matrix_with_info(m1, np.double)
m2, _, dims = to_matrix_with_info(m1, np.double)
self.assertTrue(isinstance(m2, np.ndarray))
self.assertEqual(m2.dtype, np.dtype(np.double))
@@ -284,7 +284,7 @@ class TestToMatrixWithInfo(unittest.TestCase):
d = pd.DataFrame({"A": ["a", "b", "c", "a"] })
d["A"] = d["A"].astype('category') # Convert to categorical.
m, dims = to_matrix_with_info(d, np.double)
m, _, dims = to_matrix_with_info(d, np.double)
self.assertTrue(isinstance(m, np.ndarray))
self.assertEqual(m.dtype, np.dtype(np.double))
@@ -7,6 +7,7 @@ Test that passing types to Python bindings works successfully.
import unittest
import pandas as pd
import numpy as np
import copy
from mlpack.test_python_binding import test_python_binding
@@ -94,11 +95,35 @@ class TestPythonBinding(unittest.TestCase):
and the fifth forgotten.
"""
x = np.random.rand(100, 5);
z = copy.copy(x)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
matrix_in=x)
matrix_in=z)
self.assertEqual(output['matrix_out'].shape[0], 100)
self.assertEqual(output['matrix_out'].shape[1], 4)
self.assertEqual(output['matrix_out'].dtype, np.double)
for i in [0, 1, 3]:
for j in range(100):
self.assertEqual(x[j, i], output['matrix_out'][j, i])
for j in range(100):
self.assertEqual(2 * x[j, 2], output['matrix_out'][j, 2])
def testNumpyMatrixForceCopy(self):
"""
The matrix we pass in, we should get back with the third dimension doubled
and the fifth forgotten.
"""
x = np.random.rand(100, 5);
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
matrix_in=x,
copy_all_inputs=True)
self.assertEqual(output['matrix_out'].shape[0], 100)
self.assertEqual(output['matrix_out'].shape[1], 4)
@@ -139,16 +164,71 @@ class TestPythonBinding(unittest.TestCase):
self.assertEqual(output['matrix_out'][2, 2], 26)
self.assertEqual(output['matrix_out'][2, 3], 14)
def testArraylikeMatrixForceCopy(self):
"""
Test that we can pass an arraylike matrix.
"""
x = [[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15]]
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
matrix_in=x,
copy_all_inputs=True)
self.assertEqual(output['matrix_out'].shape[0], 3)
self.assertEqual(output['matrix_out'].shape[1], 4)
self.assertEqual(len(x), 3)
self.assertEqual(len(x[0]), 5)
self.assertEqual(output['matrix_out'].dtype, np.double)
self.assertEqual(output['matrix_out'][0, 0], 1)
self.assertEqual(output['matrix_out'][0, 1], 2)
self.assertEqual(output['matrix_out'][0, 2], 6)
self.assertEqual(output['matrix_out'][0, 3], 4)
self.assertEqual(output['matrix_out'][1, 0], 6)
self.assertEqual(output['matrix_out'][1, 1], 7)
self.assertEqual(output['matrix_out'][1, 2], 16)
self.assertEqual(output['matrix_out'][1, 3], 9)
self.assertEqual(output['matrix_out'][2, 0], 11)
self.assertEqual(output['matrix_out'][2, 1], 12)
self.assertEqual(output['matrix_out'][2, 2], 26)
self.assertEqual(output['matrix_out'][2, 3], 14)
def testNumpyUmatrix(self):
"""
Same as testNumpyMatrix() but with an unsigned matrix.
"""
x = np.random.randint(0, high=500, size=[100, 5])
z = copy.copy(x)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
umatrix_in=z)
self.assertEqual(output['umatrix_out'].shape[0], 100)
self.assertEqual(output['umatrix_out'].shape[1], 4)
self.assertEqual(output['umatrix_out'].dtype, np.long)
for i in [0, 1, 3]:
for j in range(100):
self.assertEqual(x[j, i], output['umatrix_out'][j, i])
for j in range(100):
self.assertEqual(2 * x[j, 2], output['umatrix_out'][j, 2])
def testNumpyUmatrixForceCopy(self):
"""
Same as testNumpyMatrix() but with an unsigned matrix.
"""
x = np.random.randint(0, high=500, size=[100, 5])
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
umatrix_in=x)
umatrix_in=x,
copy_all_inputs=True)
self.assertEqual(output['umatrix_out'].shape[0], 100)
self.assertEqual(output['umatrix_out'].shape[1], 4)
@@ -189,16 +269,67 @@ class TestPythonBinding(unittest.TestCase):
self.assertEqual(output['umatrix_out'][2, 2], 26)
self.assertEqual(output['umatrix_out'][2, 3], 14)
def testArraylikeUmatrixForceCopy(self):
"""
Test that we can pass an arraylike unsigned matrix.
"""
x = [[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15]]
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
umatrix_in=x,
copy_all_inputs=True)
self.assertEqual(output['umatrix_out'].shape[0], 3)
self.assertEqual(output['umatrix_out'].shape[1], 4)
self.assertEqual(len(x), 3)
self.assertEqual(len(x[0]), 5)
self.assertEqual(output['umatrix_out'].dtype, np.long)
self.assertEqual(output['umatrix_out'][0, 0], 1)
self.assertEqual(output['umatrix_out'][0, 1], 2)
self.assertEqual(output['umatrix_out'][0, 2], 6)
self.assertEqual(output['umatrix_out'][0, 3], 4)
self.assertEqual(output['umatrix_out'][1, 0], 6)
self.assertEqual(output['umatrix_out'][1, 1], 7)
self.assertEqual(output['umatrix_out'][1, 2], 16)
self.assertEqual(output['umatrix_out'][1, 3], 9)
self.assertEqual(output['umatrix_out'][2, 0], 11)
self.assertEqual(output['umatrix_out'][2, 1], 12)
self.assertEqual(output['umatrix_out'][2, 2], 26)
self.assertEqual(output['umatrix_out'][2, 3], 14)
def testCol(self):
"""
Test a column vector input parameter.
"""
x = np.random.rand(100)
z = copy.copy(x)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
col_in=z)
self.assertEqual(output['col_out'].shape[0], 100)
self.assertEqual(output['col_out'].dtype, np.double)
for i in range(100):
self.assertEqual(output['col_out'][i], x[i] * 2)
def testColForceCopy(self):
"""
Test a column vector input parameter.
"""
x = np.random.rand(100)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
col_in=x)
col_in=x,
copy_all_inputs=True)
self.assertEqual(output['col_out'].shape[0], 100)
self.assertEqual(output['col_out'].dtype, np.double)
@@ -211,11 +342,29 @@ class TestPythonBinding(unittest.TestCase):
Test an unsigned column vector input parameter.
"""
x = np.random.randint(0, high=500, size=100)
z = copy.copy(x)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
ucol_in=x)
ucol_in=z)
self.assertEqual(output['ucol_out'].shape[0], 100)
self.assertEqual(output['ucol_out'].dtype, np.long)
for i in range(100):
self.assertEqual(output['ucol_out'][i], x[i] * 2)
def testUcolForceCopy(self):
"""
Test an unsigned column vector input parameter.
"""
x = np.random.randint(0, high=500, size=100)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
ucol_in=x,
copy_all_inputs=True)
self.assertEqual(output['ucol_out'].shape[0], 100)
self.assertEqual(output['ucol_out'].dtype, np.long)
@@ -227,11 +376,30 @@ class TestPythonBinding(unittest.TestCase):
Test a row vector input parameter.
"""
x = np.random.rand(100)
z = copy.copy(x)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
row_in=x)
row_in=z)
self.assertEqual(output['row_out'].shape[0], 100)
self.assertEqual(output['row_out'].dtype, np.double)
for i in range(100):
self.assertEqual(output['row_out'][i], x[i] * 2)
def testRowForceCopy(self):
"""
Test a row vector input parameter.
"""
x = np.random.rand(100)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
row_in=x,
copy_all_inputs=True)
self.assertEqual(output['row_out'].shape[0], 100)
self.assertEqual(output['row_out'].dtype, np.double)
@@ -244,11 +412,30 @@ class TestPythonBinding(unittest.TestCase):
Test an unsigned row vector input parameter.
"""
x = np.random.randint(0, high=500, size=100)
z = copy.copy(x)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
urow_in=x)
urow_in=z)
self.assertEqual(output['urow_out'].shape[0], 100)
self.assertEqual(output['urow_out'].dtype, np.long)
for i in range(100):
self.assertEqual(output['urow_out'][i], x[i] * 2)
def testUrowForceCopy(self):
"""
Test an unsigned row vector input parameter.
"""
x = np.random.randint(0, high=500, size=100)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
urow_in=x,
copy_all_inputs=True)
self.assertEqual(output['urow_out'].shape[0], 100)
self.assertEqual(output['urow_out'].dtype, np.long)
@@ -261,11 +448,31 @@ class TestPythonBinding(unittest.TestCase):
Test that we can pass a matrix with all numeric features.
"""
x = np.random.rand(100, 10)
z = copy.copy(x)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
matrix_and_info_in=x)
matrix_and_info_in=z)
self.assertEqual(output['matrix_and_info_out'].shape[0], 100)
self.assertEqual(output['matrix_and_info_out'].shape[1], 10)
for i in range(10):
for j in range(100):
self.assertEqual(output['matrix_and_info_out'][j, i], x[j, i] * 2.0)
def testMatrixAndInfoNumpyForceCopy(self):
"""
Test that we can pass a matrix with all numeric features.
"""
x = np.random.rand(100, 10)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
matrix_and_info_in=x,
copy_all_inputs=True)
self.assertEqual(output['matrix_and_info_out'].shape[0], 100)
self.assertEqual(output['matrix_and_info_out'].shape[1], 10)
@@ -281,11 +488,38 @@ class TestPythonBinding(unittest.TestCase):
x = pd.DataFrame(np.random.rand(10, 4), columns=list('abcd'))
x['e'] = pd.Series(['a', 'b', 'c', 'd', 'a', 'b', 'e', 'c', 'a', 'b'],
dtype='category')
z = copy.copy(x)
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
matrix_and_info_in=x)
matrix_and_info_in=z)
self.assertEqual(output['matrix_and_info_out'].shape[0], 10)
self.assertEqual(output['matrix_and_info_out'].shape[1], 5)
cols = list('abcde')
for i in range(4):
for j in range(10):
self.assertEqual(output['matrix_and_info_out'][j, i], z[cols[i]][j] * 2)
for j in range(10):
self.assertEqual(output['matrix_and_info_out'][j, 4], z[cols[4]][j])
def testMatrixAndInfoPandasForceCopy(self):
"""
Test that we can pass a matrix with some categorical features.
"""
x = pd.DataFrame(np.random.rand(10, 4), columns=list('abcd'))
x['e'] = pd.Series(['a', 'b', 'c', 'd', 'a', 'b', 'e', 'c', 'a', 'b'],
dtype='category')
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
matrix_and_info_in=x,
copy_all_inputs=True)
self.assertEqual(output['matrix_and_info_out'].shape[0], 10)
self.assertEqual(output['matrix_and_info_out'].shape[1], 5)
@@ -345,5 +579,29 @@ class TestPythonBinding(unittest.TestCase):
self.assertEqual(output2['model_bw_out'], 20.0)
def testModelForceCopy(self):
"""
First create a GaussianKernel object, then send it back and make sure we get
the right double value.
"""
output = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
build_model=True)
output2 = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
model_in=output['model_out'],
copy_all_inputs=True)
output3 = test_python_binding(string_in='hello',
int_in=12,
double_in=4.0,
model_in=output['model_out'])
self.assertEqual(output2['model_bw_out'], 20.0)
self.assertEqual(output3['model_bw_out'], 20.0)
if __name__ == '__main__':
unittest.main()
@@ -172,13 +172,13 @@ static void mlpackMain()
// If we got a request to build a model, then build it.
if (CLI::HasParam("build_model"))
{
CLI::GetParam<GaussianKernel>("model_out") = GaussianKernel(10.0);
CLI::GetParam<GaussianKernel*>("model_out") = new GaussianKernel(10.0);
}
// If we got an input model, double the bandwidth and output that.
if (CLI::HasParam("model_in"))
{
CLI::GetParam<double>("model_bw_out") =
CLI::GetParam<GaussianKernel>("model_in").Bandwidth() * 2.0;
CLI::GetParam<GaussianKernel*>("model_in")->Bandwidth() * 2.0;
}
}
+4
View File
@@ -1,8 +1,12 @@
# Define the files we need to compile.
# Anything not in this list will not be compiled into mlpack.
set(SOURCES
clean_memory.hpp
clean_memory.cpp
test_option.hpp
ignore_check.hpp
delete_allocated_memory.hpp
get_allocated_memory.hpp
get_param.hpp
get_printable_param.hpp
get_printable_param_impl.hpp
@@ -0,0 +1,54 @@
/**
* @file clean_memory.cpp
* @author Ryan Curtin
*
* Delete any pointers held by the CLI object.
*/
#include "clean_memory.hpp"
#include <mlpack/core.hpp>
namespace mlpack {
namespace bindings {
namespace tests {
/**
* Delete any pointers held by the CLI object.
*/
void CleanMemory()
{
// If we are holding any pointers, then we "own" them. But we may hold the
// same pointer twice, so we have to be careful to not delete it multiple
// times.
std::unordered_map<void*, const util::ParamData*> memoryAddresses;
auto it = CLI::Parameters().begin();
while (it != CLI::Parameters().end())
{
const util::ParamData& data = it->second;
void* result;
CLI::GetSingleton().functionMap[data.tname]["GetAllocatedMemory"](data,
NULL, (void*) &result);
if (result != NULL && memoryAddresses.count(result) == 0)
memoryAddresses[result] = &data;
++it;
}
// Now we have all the unique addresses that need to be deleted.
std::unordered_map<void*, const util::ParamData*>::const_iterator it2;
it2 = memoryAddresses.begin();
while (it2 != memoryAddresses.end())
{
const util::ParamData& data = *(it2->second);
CLI::GetSingleton().functionMap[data.tname]["DeleteAllocatedMemory"](data,
NULL, NULL);
++it2;
}
}
} // namespace tests
} // namespace bindings
} // namespace mlpack
@@ -0,0 +1,24 @@
/**
* @file clean_memory.hpp
* @author Ryan Curtin
*
* Delete any unique pointers that are held by the CLI object. This is similar
* to the code in end_program.hpp.
*/
#ifndef MLPACK_BINDINGS_TESTS_CLEAN_MEMORY_HPP
#define MLPACK_BINDINGS_TESTS_CLEAN_MEMORY_HPP
namespace mlpack {
namespace bindings {
namespace tests {
/**
* Delete any unique pointers that are held by the CLI object.
*/
void CleanMemory();
} // namespace tests
} // namespace bindings
} // namespace mlpack
#endif
@@ -0,0 +1,56 @@
/**
* @file delete_allocated_memory.hpp
* @author Ryan Curtin
*
* If any memory has been allocated by the parameter, delete it.
*/
#ifndef MLPACK_BINDINGS_CLI_DELETE_ALLOCATED_MEMORY_HPP
#define MLPACK_BINDINGS_CLI_DELETE_ALLOCATED_MEMORY_HPP
#include <mlpack/core/util/param_data.hpp>
namespace mlpack {
namespace bindings {
namespace tests {
template<typename T>
void DeleteAllocatedMemoryImpl(
const util::ParamData& /* d */,
const typename boost::disable_if<data::HasSerialize<T>>::type* = 0,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0)
{
// Do nothing.
}
template<typename T>
void DeleteAllocatedMemoryImpl(
const util::ParamData& /* d */,
const typename boost::enable_if<arma::is_arma_type<T>>::type* = 0)
{
// Do nothing.
}
template<typename T>
void DeleteAllocatedMemoryImpl(
const util::ParamData& d,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0,
const typename boost::enable_if<data::HasSerialize<T>>::type* = 0)
{
// Delete the allocated memory (hopefully we actually own it).
delete *boost::any_cast<T*>(&d.value);
}
template<typename T>
void DeleteAllocatedMemory(
const util::ParamData& d,
const void* /* input */,
void* /* output */)
{
DeleteAllocatedMemoryImpl<typename std::remove_pointer<T>::type>(d);
}
} // namespace tests
} // namespace bindings
} // namespace mlpack
#endif
@@ -0,0 +1,57 @@
/**
* @file get_allocated_memory.hpp
* @author Ryan Curtin
*
* If the parameter has a type that may need to be deleted, return the address
* of that object. Otherwise return NULL.
*/
#ifndef MLPACK_BINDINGS_CLI_GET_ALLOCATED_MEMORY_HPP
#define MLPACK_BINDINGS_CLI_GET_ALLOCATED_MEMORY_HPP
#include <mlpack/core/util/param_data.hpp>
namespace mlpack {
namespace bindings {
namespace tests {
template<typename T>
void* GetAllocatedMemory(
const util::ParamData& /* d */,
const typename boost::disable_if<data::HasSerialize<T>>::type* = 0,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0)
{
return NULL;
}
template<typename T>
void* GetAllocatedMemory(
const util::ParamData& /* d */,
const typename boost::enable_if<arma::is_arma_type<T>>::type* = 0)
{
return NULL;
}
template<typename T>
void* GetAllocatedMemory(
const util::ParamData& d,
const typename boost::disable_if<arma::is_arma_type<T>>::type* = 0,
const typename boost::enable_if<data::HasSerialize<T>>::type* = 0)
{
// Here we have a model; return its memory location.
return *boost::any_cast<T*>(&d.value);
}
template<typename T>
void GetAllocatedMemory(const util::ParamData& d,
const void* /* input */,
void* output)
{
*((void**) output) =
GetAllocatedMemory<typename std::remove_pointer<T>::type>(d);
}
} // namespace tests
} // namespace bindings
} // namespace mlpack
#endif
@@ -72,7 +72,8 @@ void GetPrintableParam(const util::ParamData& data,
const void* /* input */,
void* output)
{
*((std::string*) output) = GetPrintableParam<T>(data);
*((std::string*) output) =
GetPrintableParam<typename std::remove_pointer<T>::type>(data);
}
} // namespace tests
@@ -18,6 +18,8 @@
#include <mlpack/core/util/cli.hpp>
#include "get_printable_param.hpp"
#include "get_param.hpp"
#include "get_allocated_memory.hpp"
#include "delete_allocated_memory.hpp"
namespace mlpack {
namespace bindings {
@@ -90,6 +92,10 @@ class TestOption
CLI::GetSingleton().functionMap[tname]["GetPrintableParam"] =
&GetPrintableParam<N>;
CLI::GetSingleton().functionMap[tname]["GetParam"] = &GetParam<N>;
CLI::GetSingleton().functionMap[tname]["GetAllocatedMemory"] =
&GetAllocatedMemory<N>;
CLI::GetSingleton().functionMap[tname]["DeleteAllocatedMemory"] =
&DeleteAllocatedMemory<N>;
CLI::Add(std::move(data));
+5
View File
@@ -74,6 +74,7 @@ int main(int argc, char** argv)
#include <mlpack/bindings/tests/test_option.hpp>
#include <mlpack/bindings/tests/ignore_check.hpp>
#include <mlpack/bindings/tests/clean_memory.hpp>
// These functions will do nothing.
#define PRINT_PARAM_STRING(A) std::string(" ")
@@ -138,6 +139,10 @@ static const std::string testName = "";
PARAM_FLAG("verbose", "Display informational messages and the full list of "
"parameters and timers at the end of execution.", "v");
PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep"
" copied before the method is run. This is useful for debugging problems "
"where the input parameters are being modified by the algorithm, but can "
"slow down the code.", "");
// Nothing else needs to be defined---the binding will use mlpackMain() as-is.
+4 -4
View File
@@ -1058,9 +1058,9 @@ using DatasetInfo = DatasetMapper<IncrementPolicy, std::string>;
// There are no uses of required models, so that is not an option to this
// macro (it would be easy to add).
#define PARAM_MODEL(TYPE, ID, DESC, ALIAS, REQ, IN) \
static mlpack::util::Option<TYPE> \
static mlpack::util::Option<TYPE*> \
JOIN(cli_option_dummy_model_, __COUNTER__) \
(TYPE(), ID, DESC, ALIAS, #TYPE, REQ, IN, false, testName);
(nullptr, ID, DESC, ALIAS, #TYPE, REQ, IN, false, testName);
#else
// We have to do some really bizarre stuff since __COUNTER__ isn't defined. I
// don't think we can absolutely guarantee success, but it should be "good
@@ -1113,9 +1113,9 @@ using DatasetInfo = DatasetMapper<IncrementPolicy, std::string>;
!TRANS, testName);
#define PARAM_MODEL(TYPE, ID, DESC, ALIAS, REQ, IN) \
static mlpack::util::Option<TYPE> \
static mlpack::util::Option<TYPE*> \
JOIN(JOIN(cli_option_dummy_object_model_, __LINE__), opt) \
(TYPE(), ID, DESC, ALIAS, #TYPE, REQ, IN, false, \
(nullptr, ID, DESC, ALIAS, #TYPE, REQ, IN, false, \
testName);
#endif
+14 -16
View File
@@ -145,10 +145,11 @@ static void mlpackMain()
ReportIgnoredParam({{ "test", false }}, "output");
AdaBoostModel m;
AdaBoostModel* m;
if (CLI::HasParam("training"))
{
mat trainingData = std::move(CLI::GetParam<arma::mat>("training"));
m = new AdaBoostModel();
// Load labels.
arma::Row<size_t> labelsIn;
@@ -172,28 +173,28 @@ static void mlpackMain()
Row<size_t> labels;
// Normalize the labels.
data::NormalizeLabels(labelsIn, labels, m.Mappings());
data::NormalizeLabels(labelsIn, labels, m->Mappings());
// Get other training parameters.
const double tolerance = CLI::GetParam<double>("tolerance");
const size_t iterations = (size_t) CLI::GetParam<int>("iterations");
const string weakLearner = CLI::GetParam<string>("weak_learner");
if (weakLearner == "decision_stump")
m.WeakLearnerType() = AdaBoostModel::WeakLearnerTypes::DECISION_STUMP;
m->WeakLearnerType() = AdaBoostModel::WeakLearnerTypes::DECISION_STUMP;
else if (weakLearner == "perceptron")
m.WeakLearnerType() = AdaBoostModel::WeakLearnerTypes::PERCEPTRON;
m->WeakLearnerType() = AdaBoostModel::WeakLearnerTypes::PERCEPTRON;
const size_t numClasses = m.Mappings().n_elem;
const size_t numClasses = m->Mappings().n_elem;
Log::Info << numClasses << " classes in dataset." << endl;
Timer::Start("adaboost_training");
m.Train(trainingData, labels, numClasses, iterations, tolerance);
m->Train(trainingData, labels, numClasses, iterations, tolerance);
Timer::Stop("adaboost_training");
}
else
{
// We have a specified input model.
m = std::move(CLI::GetParam<AdaBoostModel>("input_model"));
m = CLI::GetParam<AdaBoostModel*>("input_model");
}
// Perform classification, if desired.
@@ -201,24 +202,21 @@ static void mlpackMain()
{
mat testingData = std::move(CLI::GetParam<arma::mat>("test"));
if (testingData.n_rows != m.Dimensionality())
if (testingData.n_rows != m->Dimensionality())
Log::Fatal << "Test data dimensionality (" << testingData.n_rows << ") "
<< "must be the same as the model dimensionality ("
<< m.Dimensionality() << ")!" << endl;
<< m->Dimensionality() << ")!" << endl;
Row<size_t> predictedLabels(testingData.n_cols);
Timer::Start("adaboost_classification");
m.Classify(testingData, predictedLabels);
m->Classify(testingData, predictedLabels);
Timer::Stop("adaboost_classification");
Row<size_t> results;
data::RevertLabels(predictedLabels, m.Mappings(), results);
data::RevertLabels(predictedLabels, m->Mappings(), results);
if (CLI::HasParam("output"))
CLI::GetParam<arma::Row<size_t>>("output") = std::move(results);
CLI::GetParam<arma::Row<size_t>>("output") = std::move(results);
}
// Should we save the model, too?
if (CLI::HasParam("output_model"))
CLI::GetParam<AdaBoostModel>("output_model") = std::move(m);
CLI::GetParam<AdaBoostModel*>("output_model") = m;
}
@@ -190,11 +190,12 @@ static void mlpackMain()
}
// Do the building of a model, if necessary.
ApproxKFNModel m;
ApproxKFNModel* m;
arma::mat referenceSet; // This may be used at query time.
if (CLI::HasParam("reference"))
{
referenceSet = std::move(CLI::GetParam<arma::mat>("reference"));
m = new ApproxKFNModel();
const size_t numTables = (size_t) CLI::GetParam<int>("num_tables");
const size_t numProjections =
@@ -205,16 +206,16 @@ static void mlpackMain()
{
Timer::Start("drusilla_select_construct");
Log::Info << "Building DrusillaSelect model..." << endl;
m.type = 0;
m.ds = DrusillaSelect<>(referenceSet, numTables, numProjections);
m->type = 0;
m->ds = DrusillaSelect<>(referenceSet, numTables, numProjections);
Timer::Stop("drusilla_select_construct");
}
else
{
Timer::Start("qdafn_construct");
Log::Info << "Building QDAFN model..." << endl;
m.type = 1;
m.qdafn = QDAFN<>(referenceSet, numTables, numProjections);
m->type = 1;
m->qdafn = QDAFN<>(referenceSet, numTables, numProjections);
Timer::Stop("qdafn_construct");
}
Log::Info << "Model built." << endl;
@@ -222,7 +223,7 @@ static void mlpackMain()
else
{
// We must load the model from what was passed.
m = std::move(CLI::GetParam<ApproxKFNModel>("input_model"));
m = CLI::GetParam<ApproxKFNModel*>("input_model");
}
// Now, do we need to do any queries?
@@ -238,12 +239,12 @@ static void mlpackMain()
if (CLI::HasParam("query"))
querySet = std::move(CLI::GetParam<arma::mat>("query"));
if (m.type == 0)
if (m->type == 0)
{
Timer::Start("drusilla_select_search");
Log::Info << "Searching for " << k << " furthest neighbors with "
<< "DrusillaSelect..." << endl;
m.ds.Search(set, k, neighbors, distances);
m->ds.Search(set, k, neighbors, distances);
Timer::Stop("drusilla_select_search");
}
else
@@ -251,7 +252,7 @@ static void mlpackMain()
Timer::Start("qdafn_search");
Log::Info << "Searching for " << k << " furthest neighbors with "
<< "QDAFN..." << endl;
m.qdafn.Search(set, k, neighbors, distances);
m->qdafn.Search(set, k, neighbors, distances);
Timer::Stop("qdafn_search");
}
Log::Info << "Search complete." << endl;
@@ -288,13 +289,9 @@ static void mlpackMain()
}
// Save results, if desired.
if (CLI::HasParam("neighbors"))
CLI::GetParam<arma::Mat<size_t>>("neighbors") = std::move(neighbors);
if (CLI::HasParam("distances"))
CLI::GetParam<arma::mat>("distances") = std::move(distances);
CLI::GetParam<arma::Mat<size_t>>("neighbors") = std::move(neighbors);
CLI::GetParam<arma::mat>("distances") = std::move(distances);
}
// Should we save the model?
if (CLI::HasParam("output_model"))
CLI::GetParam<ApproxKFNModel>("output_model") = std::move(m);
CLI::GetParam<ApproxKFNModel*>("output_model") = m;
}
+10 -12
View File
@@ -115,7 +115,7 @@ PARAM_INT_IN("recommendations", "Number of recommendations to generate for each"
PARAM_INT_IN("seed", "Set the random seed (0 uses std::time(NULL)).", "s", 0);
void ComputeRecommendations(CF& cf,
void ComputeRecommendations(CF* cf,
const size_t numRecs,
arma::Mat<size_t>& recommendations)
{
@@ -132,16 +132,16 @@ void ComputeRecommendations(CF& cf,
Log::Info << "Generating recommendations for " << users.n_elem << " users."
<< endl;
cf.GetRecommendations(numRecs, recommendations, users.row(0).t());
cf->GetRecommendations(numRecs, recommendations, users.row(0).t());
}
else
{
Log::Info << "Generating recommendations for all users." << endl;
cf.GetRecommendations(numRecs, recommendations);
cf->GetRecommendations(numRecs, recommendations);
}
}
void ComputeRMSE(CF& cf)
void ComputeRMSE(CF* cf)
{
// Now, compute each test point.
arma::mat testData = std::move(CLI::GetParam<arma::mat>("test"));
@@ -156,7 +156,7 @@ void ComputeRMSE(CF& cf)
// Now compute the RMSE.
arma::vec predictions;
cf.Predict(combinations, predictions);
cf->Predict(combinations, predictions);
// Compute the root of the sum of the squared errors, divide by the number of
// points to get the RMSE. It turns out this is just the L2-norm divided by
@@ -168,7 +168,7 @@ void ComputeRMSE(CF& cf)
Log::Info << "RMSE is " << rmse << "." << endl;
}
void PerformAction(CF& c)
void PerformAction(CF* c)
{
if (CLI::HasParam("query") || CLI::HasParam("all_user_recommendations"))
{
@@ -180,15 +180,13 @@ void PerformAction(CF& c)
ComputeRecommendations(c, numRecs, recommendations);
// Save the output.
if (CLI::HasParam("output"))
CLI::GetParam<arma::Mat<size_t>>("output") = recommendations;
CLI::GetParam<arma::Mat<size_t>>("output") = recommendations;
}
if (CLI::HasParam("test"))
ComputeRMSE(c);
if (CLI::HasParam("output_model"))
CLI::GetParam<CF>("output_model") = std::move(c);
CLI::GetParam<CF*>("output_model") = c;
}
template<typename Factorizer>
@@ -198,7 +196,7 @@ void PerformAction(Factorizer&& factorizer,
{
// Parameters for generating the CF object.
const size_t neighborhood = (size_t) CLI::GetParam<int>("neighborhood");
CF c(dataset, factorizer, neighborhood, rank);
CF* c = new CF(dataset, factorizer, neighborhood, rank);
PerformAction(c);
}
@@ -323,7 +321,7 @@ static void mlpackMain()
else
{
// Load an input model.
CF c = std::move(CLI::GetParam<CF>("input_model"));
CF* c = std::move(CLI::GetParam<CF*>("input_model"));
PerformAction(c);
}
@@ -116,9 +116,10 @@ static void mlpackMain()
ReportIgnoredParam({{ "test", false }}, "predictions");
// We must either load a model, or train a new stump.
DSModel model;
DSModel* model;
if (CLI::HasParam("training"))
{
model = new DSModel();
mat trainingData = std::move(CLI::GetParam<mat>("training"));
// Load labels, if necessary.
@@ -140,18 +141,18 @@ static void mlpackMain()
// Normalize the labels.
Row<size_t> labels;
data::NormalizeLabels(labelsIn, labels, model.mappings);
data::NormalizeLabels(labelsIn, labels, model->mappings);
const size_t bucketSize = CLI::GetParam<int>("bucket_size");
const size_t classes = labels.max() + 1;
Timer::Start("training");
model.stump.Train(trainingData, labels, classes, bucketSize);
model->stump.Train(trainingData, labels, classes, bucketSize);
Timer::Stop("training");
}
else
{
model = std::move(CLI::GetParam<DSModel>("input_model"));
model = CLI::GetParam<DSModel*>("input_model");
}
// Now, do we need to do any testing?
@@ -160,21 +161,21 @@ static void mlpackMain()
// Load the test file.
mat testingData = std::move(CLI::GetParam<arma::mat>("test"));
if (testingData.n_rows <= model.stump.SplitDimension())
if (testingData.n_rows <= model->stump.SplitDimension())
Log::Fatal << "Test data dimensionality (" << testingData.n_rows << ") "
<< "is too low; the trained stump requires at least "
<< model.stump.SplitDimension() << " dimensions!" << endl;
<< model->stump.SplitDimension() << " dimensions!" << endl;
Row<size_t> predictedLabels(testingData.n_cols);
Timer::Start("testing");
model.stump.Classify(testingData, predictedLabels);
model->stump.Classify(testingData, predictedLabels);
Timer::Stop("testing");
// Denormalize predicted labels, if we want to save them.
if (CLI::HasParam("predictions"))
{
Row<size_t> actualLabels;
data::RevertLabels(predictedLabels, model.mappings, actualLabels);
data::RevertLabels(predictedLabels, model->mappings, actualLabels);
// Save the predicted labels as output.
CLI::GetParam<Row<size_t>>("predictions") = std::move(actualLabels);
@@ -182,6 +183,5 @@ static void mlpackMain()
}
// Save the model, if desired.
if (CLI::HasParam("output_model"))
CLI::GetParam<DSModel>("output_model") = std::move(model);
CLI::GetParam<DSModel*>("output_model") = model;
}
@@ -137,13 +137,14 @@ static void mlpackMain()
"leaf size must be positive");
// Load the model or build the tree.
DecisionTreeModel model;
DecisionTreeModel* model;
arma::mat trainingSet;
arma::Row<size_t> labels;
if (CLI::HasParam("training"))
{
model.info = std::move(std::get<0>(CLI::GetParam<TupleType>("training")));
model = new DecisionTreeModel();
model->info = std::move(std::get<0>(CLI::GetParam<TupleType>("training")));
trainingSet = std::move(std::get<1>(CLI::GetParam<TupleType>("training")));
if (CLI::HasParam("labels"))
{
@@ -169,12 +170,12 @@ static void mlpackMain()
{
arma::Row<double> weights =
std::move(CLI::GetParam<arma::Mat<double>>("weights"));
model.tree = DecisionTree<>(trainingSet, model.info, labels,
model->tree = DecisionTree<>(trainingSet, model->info, labels,
numClasses, weights, minLeafSize);
}
else
{
model.tree = DecisionTree<>(trainingSet, model.info, labels,
model->tree = DecisionTree<>(trainingSet, model->info, labels,
numClasses, minLeafSize);
}
@@ -184,7 +185,7 @@ static void mlpackMain()
arma::Row<size_t> predictions;
arma::mat probabilities;
model.tree.Classify(trainingSet, predictions, probabilities);
model->tree.Classify(trainingSet, predictions, probabilities);
size_t correct = 0;
for (size_t i = 0; i < trainingSet.n_cols; ++i)
@@ -199,19 +200,19 @@ static void mlpackMain()
}
else
{
model = std::move(CLI::GetParam<DecisionTreeModel>("input_model"));
model = CLI::GetParam<DecisionTreeModel*>("input_model");
}
// Do we need to get predictions?
if (CLI::HasParam("test"))
{
std::get<0>(CLI::GetRawParam<TupleType>("test")) = model.info;
std::get<0>(CLI::GetRawParam<TupleType>("test")) = model->info;
arma::mat testPoints = std::get<1>(CLI::GetParam<TupleType>("test"));
arma::Row<size_t> predictions;
arma::mat probabilities;
model.tree.Classify(testPoints, predictions, probabilities);
model->tree.Classify(testPoints, predictions, probabilities);
// Do we need to calculate accuracy?
if (CLI::HasParam("test_labels"))
@@ -231,13 +232,10 @@ static void mlpackMain()
}
// Do we need to save outputs?
if (CLI::HasParam("predictions"))
CLI::GetParam<arma::Row<size_t>>("predictions") = std::move(predictions);
if (CLI::HasParam("probabilities"))
CLI::GetParam<arma::mat>("probabilities") = std::move(probabilities);
CLI::GetParam<arma::Row<size_t>>("predictions") = predictions;
CLI::GetParam<arma::mat>("probabilities") = probabilities;
}
// Do we need to save the model?
if (CLI::HasParam("output_model"))
CLI::GetParam<DecisionTreeModel>("output_model") = std::move(model);
CLI::GetParam<DecisionTreeModel*>("output_model") = model;
}
+4 -10
View File
@@ -168,7 +168,7 @@ static void mlpackMain()
}
else
{
tree = &CLI::GetParam<DTree<arma::mat>>("input_model");
tree = CLI::GetParam<DTree<arma::mat>*>("input_model");
}
// Compute the density at the provided test points and output the density in
@@ -213,8 +213,7 @@ static void mlpackMain()
if (!ofs.is_open())
{
Log::Warn << "Unable to open file '" << tagFile
<< "' to save tag membership info."
<< std::endl;
<< "' to save tag membership info." << std::endl;
}
else if (CLI::HasParam("path_format"))
{
@@ -231,7 +230,7 @@ static void mlpackMain()
else
{
Log::Warn << "Unknown path format specified: '" << pathFormat
<< "'. Valid are: lr | lr-id | id-lr. Defaults to 'lr'." << endl;
<< "'. Valid are: lr | lr-id | id-lr. Defaults to 'lr'." << endl;
theFormat = PathCacher::FormatLR;
}
@@ -284,10 +283,5 @@ static void mlpackMain()
}
// Save the model, if desired.
if (CLI::HasParam("output_model"))
CLI::GetParam<DTree<arma::mat>>("output_model") = std::move(*tree);
// Clean up memory, if we need to.
if (!CLI::HasParam("input_model") && !CLI::HasParam("output_model"))
delete tree;
CLI::GetParam<DTree<arma::mat>*>("output_model") = tree;
}
+26 -29
View File
@@ -111,10 +111,11 @@ static void mlpackMain()
// Naive mode overrides single mode.
ReportIgnoredParam({{ "naive", true }}, "single");
FastMKSModel model;
FastMKSModel* model;
arma::mat referenceData;
if (CLI::HasParam("reference"))
{
model = new FastMKSModel();
referenceData = std::move(CLI::GetParam<arma::mat>("reference"));
Log::Info << "Loaded reference data (" << referenceData.n_rows << " x "
@@ -137,55 +138,55 @@ static void mlpackMain()
if (kernelType == "linear")
{
LinearKernel lk;
model.KernelType() = FastMKSModel::LINEAR_KERNEL;
model.BuildModel(referenceData, lk, single, naive, base);
model->KernelType() = FastMKSModel::LINEAR_KERNEL;
model->BuildModel(referenceData, lk, single, naive, base);
}
else if (kernelType == "polynomial")
{
PolynomialKernel pk(degree, offset);
model.KernelType() = FastMKSModel::POLYNOMIAL_KERNEL;
model.BuildModel(referenceData, pk, single, naive, base);
model->KernelType() = FastMKSModel::POLYNOMIAL_KERNEL;
model->BuildModel(referenceData, pk, single, naive, base);
}
else if (kernelType == "cosine")
{
CosineDistance cd;
model.KernelType() = FastMKSModel::COSINE_DISTANCE;
model.BuildModel(referenceData, cd, single, naive, base);
model->KernelType() = FastMKSModel::COSINE_DISTANCE;
model->BuildModel(referenceData, cd, single, naive, base);
}
else if (kernelType == "gaussian")
{
GaussianKernel gk(bandwidth);
model.KernelType() = FastMKSModel::GAUSSIAN_KERNEL;
model.BuildModel(referenceData, gk, single, naive, base);
model->KernelType() = FastMKSModel::GAUSSIAN_KERNEL;
model->BuildModel(referenceData, gk, single, naive, base);
}
else if (kernelType == "epanechnikov")
{
EpanechnikovKernel ek(bandwidth);
model.KernelType() = FastMKSModel::EPANECHNIKOV_KERNEL;
model.BuildModel(referenceData, ek, single, naive, base);
model->KernelType() = FastMKSModel::EPANECHNIKOV_KERNEL;
model->BuildModel(referenceData, ek, single, naive, base);
}
else if (kernelType == "triangular")
{
TriangularKernel tk(bandwidth);
model.KernelType() = FastMKSModel::TRIANGULAR_KERNEL;
model.BuildModel(referenceData, tk, single, naive, base);
model->KernelType() = FastMKSModel::TRIANGULAR_KERNEL;
model->BuildModel(referenceData, tk, single, naive, base);
}
else if (kernelType == "hyptan")
{
HyperbolicTangentKernel htk(scale, offset);
model.KernelType() = FastMKSModel::HYPTAN_KERNEL;
model.BuildModel(referenceData, htk, single, naive, base);
model->KernelType() = FastMKSModel::HYPTAN_KERNEL;
model->BuildModel(referenceData, htk, single, naive, base);
}
}
else
{
// Load model from file, then do whatever is necessary.
model = std::move(CLI::GetParam<FastMKSModel>("input_model"));
model = CLI::GetParam<FastMKSModel*>("input_model");
}
// Set search preferences.
model.Naive() = CLI::HasParam("naive");
model.SingleMode() = CLI::HasParam("single");
model->Naive() = CLI::HasParam("naive");
model->SingleMode() = CLI::HasParam("single");
// Should we do search?
if (CLI::HasParam("k"))
@@ -202,23 +203,19 @@ static void mlpackMain()
Log::Info << "Loaded query data (" << queryData.n_rows << " x "
<< queryData.n_cols << ")." << endl;
model.Search(queryData, (size_t) CLI::GetParam<int>("k"), indices,
model->Search(queryData, (size_t) CLI::GetParam<int>("k"), indices,
kernels, base);
}
else
{
model.Search((size_t) CLI::GetParam<int>("k"), indices, kernels);
model->Search((size_t) CLI::GetParam<int>("k"), indices, kernels);
}
// Save output, if we were asked to.
if (CLI::HasParam("kernels"))
CLI::GetParam<arma::mat>("kernels") = std::move(kernels);
if (CLI::HasParam("indices"))
CLI::GetParam<arma::Mat<size_t>>("indices") = std::move(indices);
// Save output.
CLI::GetParam<arma::mat>("kernels") = std::move(kernels);
CLI::GetParam<arma::Mat<size_t>>("indices") = std::move(indices);
}
// Save the model, if requested.
if (CLI::HasParam("output_model"))
CLI::GetParam<FastMKSModel>("output_model") = std::move(model);
// Save the model.
CLI::GetParam<FastMKSModel*>("output_model") = model;
}
+4 -5
View File
@@ -55,15 +55,14 @@ static void mlpackMain()
RequireParamValue<int>("samples", [](int x) { return x > 0; }, true,
"number of samples must be greater than 0");
GMM gmm = std::move(CLI::GetParam<GMM>("input_model"));
GMM* gmm = CLI::GetParam<GMM*>("input_model");
size_t length = (size_t) CLI::GetParam<int>("samples");
Log::Info << "Generating " << length << " samples..." << endl;
arma::mat samples(gmm.Dimensionality(), length);
arma::mat samples(gmm->Dimensionality(), length);
for (size_t i = 0; i < length; ++i)
samples.col(i) = gmm.Random();
samples.col(i) = gmm->Random();
// Save, if the user asked for it.
if (CLI::HasParam("output"))
CLI::GetParam<arma::mat>("output") = std::move(samples);
CLI::GetParam<arma::mat>("output") = std::move(samples);
}
@@ -46,16 +46,15 @@ static void mlpackMain()
RequireAtLeastOnePassed({ "output" }, false, "no results will be saved");
// Get the GMM and the points.
GMM gmm = std::move(CLI::GetParam<GMM>("input_model"));
GMM* gmm = CLI::GetParam<GMM*>("input_model");
arma::mat dataset = std::move(CLI::GetParam<arma::mat>("input"));
// Now calculate the probabilities.
arma::rowvec probabilities(dataset.n_cols);
for (size_t i = 0; i < dataset.n_cols; ++i)
probabilities[i] = gmm.Probability(dataset.unsafe_col(i));
probabilities[i] = gmm->Probability(dataset.unsafe_col(i));
// And save the result.
if (CLI::HasParam("output"))
CLI::GetParam<arma::mat>("output") = std::move(probabilities);
CLI::GetParam<arma::mat>("output") = std::move(probabilities);
}
+15 -12
View File
@@ -153,17 +153,21 @@ static void mlpackMain()
}
// Initialize GMM.
GMM gmm(size_t(gaussians), dataPoints.n_rows);
GMM* gmm;
if (CLI::HasParam("input_model"))
{
gmm = std::move(CLI::GetParam<GMM>("input_model"));
gmm = CLI::GetParam<GMM*>("input_model");
if (gmm.Dimensionality() != dataPoints.n_rows)
if (gmm->Dimensionality() != dataPoints.n_rows)
Log::Fatal << "Given input data (with " << PRINT_PARAM_STRING("input")
<< ") has dimensionality " << dataPoints.n_rows << ", but the initial"
<< " model (given with " << PRINT_PARAM_STRING("input_model")
<< " has dimensionality " << gmm.Dimensionality() << "!" << endl;
<< " has dimensionality " << gmm->Dimensionality() << "!" << endl;
}
else
{
gmm = new GMM(size_t(gaussians), dataPoints.n_rows);
}
// Gather parameters for EMFit object.
@@ -199,7 +203,7 @@ static void mlpackMain()
// Compute the parameters of the model using the EM algorithm.
Timer::Start("em");
EMFit<KMeansType, DiagonalConstraint> em(maxIterations, tolerance, k);
likelihood = gmm.Train(dataPoints, CLI::GetParam<int>("trials"), false,
likelihood = gmm->Train(dataPoints, CLI::GetParam<int>("trials"), false,
em);
Timer::Stop("em");
}
@@ -208,7 +212,7 @@ static void mlpackMain()
// Compute the parameters of the model using the EM algorithm.
Timer::Start("em");
EMFit<KMeansType> em(maxIterations, tolerance, k);
likelihood = gmm.Train(dataPoints, CLI::GetParam<int>("trials"), false,
likelihood = gmm->Train(dataPoints, CLI::GetParam<int>("trials"), false,
em);
Timer::Stop("em");
}
@@ -217,7 +221,7 @@ static void mlpackMain()
// Compute the parameters of the model using the EM algorithm.
Timer::Start("em");
EMFit<KMeansType, NoConstraint> em(maxIterations, tolerance, k);
likelihood = gmm.Train(dataPoints, CLI::GetParam<int>("trials"), false,
likelihood = gmm->Train(dataPoints, CLI::GetParam<int>("trials"), false,
em);
Timer::Stop("em");
}
@@ -231,7 +235,7 @@ static void mlpackMain()
// Compute the parameters of the model using the EM algorithm.
Timer::Start("em");
EMFit<kmeans::KMeans<>, DiagonalConstraint> em(maxIterations, tolerance);
likelihood = gmm.Train(dataPoints, CLI::GetParam<int>("trials"), false,
likelihood = gmm->Train(dataPoints, CLI::GetParam<int>("trials"), false,
em);
Timer::Stop("em");
}
@@ -240,7 +244,7 @@ static void mlpackMain()
// Compute the parameters of the model using the EM algorithm.
Timer::Start("em");
EMFit<> em(maxIterations, tolerance);
likelihood = gmm.Train(dataPoints, CLI::GetParam<int>("trials"), false,
likelihood = gmm->Train(dataPoints, CLI::GetParam<int>("trials"), false,
em);
Timer::Stop("em");
}
@@ -249,7 +253,7 @@ static void mlpackMain()
// Compute the parameters of the model using the EM algorithm.
Timer::Start("em");
EMFit<KMeans<>, NoConstraint> em(maxIterations, tolerance);
likelihood = gmm.Train(dataPoints, CLI::GetParam<int>("trials"), false,
likelihood = gmm->Train(dataPoints, CLI::GetParam<int>("trials"), false,
em);
Timer::Stop("em");
}
@@ -257,6 +261,5 @@ static void mlpackMain()
Log::Info << "Log-likelihood of estimate: " << likelihood << "." << endl;
if (CLI::HasParam("output_model"))
CLI::GetParam<GMM>("output_model") = std::move(gmm);
CLI::GetParam<GMM*>("output_model") = gmm;
}
+1 -1
View File
@@ -79,5 +79,5 @@ struct Loglik
static void mlpackMain()
{
// Load model, and calculate the log-likelihood of the sequence.
CLI::GetParam<HMMModel>("input_model").PerformAction<Loglik>((void*) NULL);
CLI::GetParam<HMMModel*>("input_model")->PerformAction<Loglik>((void*) NULL);
}
+6 -6
View File
@@ -436,21 +436,21 @@ static void mlpackMain()
typeId = HMMType::GaussianMixtureModelHMM;
// If we have a model file, we can autodetect the type.
HMMModel hmm(typeId);
HMMModel* hmm;
if (CLI::HasParam("input_model"))
{
hmm = std::move(CLI::GetParam<HMMModel>("input_model"));
hmm = CLI::GetParam<HMMModel*>("input_model");
}
else
{
// We need to initialize the model.
hmm.PerformAction<Init, vector<mat>>(&trainSeq);
hmm = new HMMModel(typeId);
hmm->PerformAction<Init, vector<mat>>(&trainSeq);
}
// Train the model.
hmm.PerformAction<Train, vector<mat>>(&trainSeq);
hmm->PerformAction<Train, vector<mat>>(&trainSeq);
// If necessary, save the output.
if (CLI::HasParam("output_model"))
CLI::GetParam<HMMModel>("output_model") = std::move(hmm);
CLI::GetParam<HMMModel*>("output_model") = hmm;
}
+2 -3
View File
@@ -77,8 +77,7 @@ struct Viterbi
hmm.Predict(dataSeq, sequence);
// Save output.
if (CLI::HasParam("output"))
CLI::GetParam<arma::Mat<size_t>>("output") = std::move(sequence);
CLI::GetParam<arma::Mat<size_t>>("output") = std::move(sequence);
}
};
@@ -86,5 +85,5 @@ static void mlpackMain()
{
RequireAtLeastOnePassed({ "output" }, false, "no results will be saved");
CLI::GetParam<HMMModel>("input_model").PerformAction<Viterbi>((void*) NULL);
CLI::GetParam<HMMModel*>("input_model")->PerformAction<Viterbi>((void*) NULL);
}
@@ -140,25 +140,25 @@ static void mlpackMain()
true, "unrecognized numeric split strategy");
// Do we need to load a model or do we already have one?
HoeffdingTreeModel model;
HoeffdingTreeModel* model;
DatasetInfo datasetInfo;
arma::mat trainingSet;
arma::Row<size_t> labels;
if (CLI::HasParam("input_model"))
{
model = std::move(CLI::GetParam<HoeffdingTreeModel>("input_model"));
model = CLI::GetParam<HoeffdingTreeModel*>("input_model");
}
else
{
// Initialize a model.
if (!CLI::HasParam("info_gain") && (numericSplitStrategy == "domingos"))
model = HoeffdingTreeModel(HoeffdingTreeModel::GINI_HOEFFDING);
model = new HoeffdingTreeModel(HoeffdingTreeModel::GINI_HOEFFDING);
else if (!CLI::HasParam("info_gain") && (numericSplitStrategy == "binary"))
model = HoeffdingTreeModel(HoeffdingTreeModel::GINI_BINARY);
model = new HoeffdingTreeModel(HoeffdingTreeModel::GINI_BINARY);
else if (CLI::HasParam("info_gain") && (numericSplitStrategy == "domingos"))
model = HoeffdingTreeModel(HoeffdingTreeModel::INFO_HOEFFDING);
model = new HoeffdingTreeModel(HoeffdingTreeModel::INFO_HOEFFDING);
else if (CLI::HasParam("info_gain") && (numericSplitStrategy == "binary"))
model = HoeffdingTreeModel(HoeffdingTreeModel::INFO_BINARY);
model = new HoeffdingTreeModel(HoeffdingTreeModel::INFO_BINARY);
}
// Now, do we need to train?
@@ -207,7 +207,7 @@ static void mlpackMain()
if (!CLI::HasParam("input_model"))
{
// Build the model.
model.BuildModel(trainingSet, datasetInfo, labels,
model->BuildModel(trainingSet, datasetInfo, labels,
arma::max(labels) + 1, batchTraining, confidence, maxSamples,
100, minSamples, bins, observationsBeforeBinning);
--passes; // This model-building takes one pass.
@@ -219,12 +219,12 @@ static void mlpackMain()
// We only need to do batch training if we've not already called
// BuildModel.
if (CLI::HasParam("input_model"))
model.Train(trainingSet, labels, true);
model->Train(trainingSet, labels, true);
}
else
{
for (size_t p = 0; p < passes; ++p)
model.Train(trainingSet, labels, false);
model->Train(trainingSet, labels, false);
}
Timer::Stop("tree_training");
@@ -235,7 +235,7 @@ static void mlpackMain()
{
// Get training error.
arma::Row<size_t> predictions;
model.Classify(trainingSet, predictions);
model->Classify(trainingSet, predictions);
size_t correct = 0;
for (size_t i = 0; i < labels.n_elem; ++i)
@@ -248,7 +248,7 @@ static void mlpackMain()
}
// Get the number of nodes in the tree.
Log::Info << model.NumNodes() << " nodes in the tree." << endl;
Log::Info << model->NumNodes() << " nodes in the tree." << endl;
// The tree is trained or loaded. Now do any testing if we need.
if (CLI::HasParam("test"))
@@ -262,7 +262,7 @@ static void mlpackMain()
arma::rowvec probabilities;
Timer::Start("tree_testing");
model.Classify(testSet, predictions, probabilities);
model->Classify(testSet, predictions, probabilities);
Timer::Stop("tree_testing");
if (CLI::HasParam("test_labels"))
@@ -281,14 +281,10 @@ static void mlpackMain()
100.0 << ")." << endl;
}
if (CLI::HasParam("predictions"))
CLI::GetParam<arma::Row<size_t>>("predictions") = std::move(predictions);
if (CLI::HasParam("probabilities"))
CLI::GetParam<arma::mat>("probabilities") = std::move(probabilities);
CLI::GetParam<arma::Row<size_t>>("predictions") = std::move(predictions);
CLI::GetParam<arma::mat>("probabilities") = std::move(probabilities);
}
// Check the accuracy on the training set.
if (CLI::HasParam("output_model"))
CLI::GetParam<HoeffdingTreeModel>("output_model") = std::move(model);
CLI::GetParam<HoeffdingTreeModel*>("output_model") = model;
}
+11 -12
View File
@@ -120,11 +120,12 @@ static void mlpackMain()
"no results will be saved");
ReportIgnoredParam({{ "test", true }}, "output_predictions");
// Initialize the object.
LARS lars(useCholesky, lambda1, lambda2);
LARS* lars;
if (CLI::HasParam("input"))
{
// Initialize the object.
lars = new LARS(useCholesky, lambda1, lambda2);
// Load covariates. We can avoid LARS transposing our data by choosing to
// not transpose this data (that's why we used PARAM_TMATRIX_IN).
mat matX = std::move(CLI::GetParam<arma::mat>("input"));
@@ -146,11 +147,11 @@ static void mlpackMain()
vec beta;
arma::rowvec y = std::move(matY);
lars.Train(matX, y, beta, false /* do not transpose */);
lars->Train(matX, y, beta, false /* do not transpose */);
}
else // We must have --input_model_file.
{
lars = std::move(CLI::GetParam<LARS>("input_model"));
lars = CLI::GetParam<LARS*>("input_model");
}
if (CLI::HasParam("test"))
@@ -162,19 +163,17 @@ static void mlpackMain()
// Make sure the dimensionality is right. We haven't transposed, so, we
// check n_cols not n_rows.
if (testPoints.n_cols != lars.BetaPath().back().n_elem)
if (testPoints.n_cols != lars->BetaPath().back().n_elem)
Log::Fatal << "Dimensionality of test set (" << testPoints.n_cols << ") "
<< "is not equal to the dimensionality of the model ("
<< lars.BetaPath().back().n_elem << ")!" << endl;
<< lars->BetaPath().back().n_elem << ")!" << endl;
arma::rowvec predictions;
lars.Predict(testPoints.t(), predictions, false);
lars->Predict(testPoints.t(), predictions, false);
// Save test predictions (one per line).
if (CLI::HasParam("output_predictions"))
CLI::GetParam<arma::mat>("output_predictions") = predictions.t();
CLI::GetParam<arma::mat>("output_predictions") = predictions.t();
}
if (CLI::HasParam("output_model"))
CLI::GetParam<LARS>("output_model") = std::move(lars);
CLI::GetParam<LARS*>("output_model") = lars;
}
@@ -96,7 +96,7 @@ static void mlpackMain()
mat regressors;
rowvec responses;
LinearRegression lr;
LinearRegression* lr;
const bool computeModel = !CLI::HasParam("input_model");
const bool computePrediction = CLI::HasParam("test");
@@ -148,14 +148,14 @@ static void mlpackMain()
}
Timer::Start("regression");
lr = LinearRegression(regressors, responses, lambda);
lr = new LinearRegression(regressors, responses, lambda);
Timer::Stop("regression");
}
else
{
// A model file was passed in, so load it.
Timer::Start("load_model");
lr = std::move(CLI::GetParam<LinearRegression>("input_model"));
lr = CLI::GetParam<LinearRegression*>("input_model");
Timer::Stop("load_model");
}
@@ -168,10 +168,15 @@ static void mlpackMain()
Timer::Stop("load_test_points");
// Ensure that test file data has the right number of features.
if ((lr.Parameters().n_elem - 1) != points.n_rows)
if ((lr->Parameters().n_elem - 1) != points.n_rows)
{
Log::Fatal << "The model was trained on " << lr.Parameters().n_elem - 1
<< "-dimensional data, but the test points in '"
// If we built the model, nothing will free it so we have to...
const size_t dimensions = lr->Parameters().n_elem - 1;
if (computeModel)
delete lr;
Log::Fatal << "The model was trained on " << dimensions << "-dimensional "
<< "data, but the test points in '"
<< CLI::GetPrintableParam<mat>("test") << "' are " << points.n_rows
<< "-dimensional!" << endl;
}
@@ -179,15 +184,13 @@ static void mlpackMain()
// Perform the predictions using our model.
rowvec predictions;
Timer::Start("prediction");
lr.Predict(points, predictions);
lr->Predict(points, predictions);
Timer::Stop("prediction");
// Save predictions.
if (CLI::HasParam("output_predictions"))
CLI::GetParam<rowvec>("output_predictions") = std::move(predictions);
CLI::GetParam<rowvec>("output_predictions") = std::move(predictions);
}
// Save the model if needed.
if (CLI::HasParam("output_model"))
CLI::GetParam<LinearRegression>("output_model") = std::move(lr);
CLI::GetParam<LinearRegression*>("output_model") = lr;
}
@@ -120,9 +120,11 @@ static void mlpackMain()
ReportIgnoredParam({{ "training", false }}, "tolerance");
// Do we have an existing model?
LocalCoordinateCoding lcc(0, 0.0);
LocalCoordinateCoding* lcc;
if (CLI::HasParam("input_model"))
lcc = std::move(CLI::GetParam<LocalCoordinateCoding>("input_model"));
lcc = CLI::GetParam<LocalCoordinateCoding*>("input_model");
else
lcc = new LocalCoordinateCoding(0, 0.0);
if (CLI::HasParam("training"))
{
@@ -136,10 +138,10 @@ static void mlpackMain()
matX.col(i) /= norm(matX.col(i), 2);
}
lcc.Lambda() = CLI::GetParam<double>("lambda");
lcc.Atoms() = (size_t) CLI::GetParam<int>("atoms");
lcc.MaxIterations() = (size_t) CLI::GetParam<int>("max_iterations");
lcc.Tolerance() = CLI::GetParam<double>("tolerance");
lcc->Lambda() = CLI::GetParam<double>("lambda");
lcc->Atoms() = (size_t) CLI::GetParam<int>("atoms");
lcc->MaxIterations() = (size_t) CLI::GetParam<int>("max_iterations");
lcc->Tolerance() = CLI::GetParam<double>("tolerance");
// Inform the user if we are overwriting their model.
if (CLI::HasParam("input_model"))
@@ -147,35 +149,35 @@ static void mlpackMain()
Log::Info << "Using dictionary from existing model in '"
<< CLI::GetPrintableParam<string>("input_model") << "' as initial "
<< "dictionary for training." << endl;
lcc.Train<NothingInitializer>(matX);
lcc->Train<NothingInitializer>(matX);
}
else if (CLI::HasParam("initial_dictionary"))
{
// Load initial dictionary directly into LCC object.
lcc.Dictionary() = std::move(CLI::GetParam<mat>("initial_dictionary"));
lcc->Dictionary() = std::move(CLI::GetParam<mat>("initial_dictionary"));
// Validate the size of the initial dictionary.
if (lcc.Dictionary().n_cols != lcc.Atoms())
if (lcc->Dictionary().n_cols != lcc->Atoms())
{
Log::Fatal << "The initial dictionary has " << lcc.Dictionary().n_cols
Log::Fatal << "The initial dictionary has " << lcc->Dictionary().n_cols
<< " atoms, but the number of atoms was specified to be "
<< lcc.Atoms() << "!" << endl;
<< lcc->Atoms() << "!" << endl;
}
if (lcc.Dictionary().n_rows != matX.n_rows)
if (lcc->Dictionary().n_rows != matX.n_rows)
{
Log::Fatal << "The initial dictionary has " << lcc.Dictionary().n_rows
Log::Fatal << "The initial dictionary has " << lcc->Dictionary().n_rows
<< " dimensions, but the data has " << matX.n_rows << " dimensions!"
<< endl;
}
// Train the model.
lcc.Train<NothingInitializer>(matX);
lcc->Train<NothingInitializer>(matX);
}
else
{
// Run with the default initialization.
lcc.Train(matX);
lcc->Train(matX);
}
}
@@ -184,9 +186,9 @@ static void mlpackMain()
{
mat matY = std::move(CLI::GetParam<mat>("test"));
if (matY.n_rows != lcc.Dictionary().n_rows)
if (matY.n_rows != lcc->Dictionary().n_rows)
Log::Fatal << "Model was trained with a dimensionality of "
<< lcc.Dictionary().n_rows << ", but data in test file "
<< lcc->Dictionary().n_rows << ", but data in test file "
<< CLI::GetPrintableParam<mat>("test") << " has a dimensionality of "
<< matY.n_rows << "!" << endl;
@@ -199,17 +201,12 @@ static void mlpackMain()
}
mat codes;
lcc.Encode(matY, codes);
lcc->Encode(matY, codes);
if (CLI::HasParam("codes"))
CLI::GetParam<mat>("codes") = std::move(codes);
CLI::GetParam<mat>("codes") = std::move(codes);
}
// Did the user want to save the dictionary?
if (CLI::HasParam("dictionary"))
CLI::GetParam<mat>("dictionary") = std::move(lcc.Dictionary());
// Did the user want to save the model?
if (CLI::HasParam("output_model"))
CLI::GetParam<LocalCoordinateCoding>("output_model") = std::move(lcc);
// Save the dictionary and the model.
CLI::GetParam<mat>("dictionary") = lcc->Dictionary();
CLI::GetParam<LocalCoordinateCoding*>("output_model") = lcc;
}
@@ -206,16 +206,18 @@ static void mlpackMain()
regressors = std::move(CLI::GetParam<arma::mat>("training"));
// Load the model, if necessary.
LogisticRegression<> model(0, 0); // Empty model.
LogisticRegression<>* model;
if (CLI::HasParam("input_model"))
model = std::move(CLI::GetParam<LogisticRegression<>>("input_model"));
model = CLI::GetParam<LogisticRegression<>*>("input_model");
else
{
model = new LogisticRegression<>(0, 0);
// Set the size of the parameters vector, if necessary.
if (!CLI::HasParam("labels"))
model.Parameters() = arma::zeros<arma::rowvec>(regressors.n_rows);
model->Parameters() = arma::zeros<arma::rowvec>(regressors.n_rows);
else
model.Parameters() = arma::zeros<arma::rowvec>(regressors.n_rows + 1);
model->Parameters() = arma::zeros<arma::rowvec>(regressors.n_rows + 1);
}
// Check if the responses are in a separate file.
@@ -242,7 +244,7 @@ static void mlpackMain()
// Now, do the training.
if (CLI::HasParam("training"))
{
model.Lambda() = lambda;
model->Lambda() = lambda;
if (optimizerType == "sgd")
{
@@ -254,7 +256,7 @@ static void mlpackMain()
Log::Info << "Training model with SGD optimizer." << endl;
// This will train the model.
model.Train(regressors, responses, sgdOpt);
model->Train(regressors, responses, sgdOpt);
}
else if (optimizerType == "lbfgs")
{
@@ -264,7 +266,7 @@ static void mlpackMain()
Log::Info << "Training model with L-BFGS optimizer." << endl;
// This will train the model.
model.Train(regressors, responses, lbfgsOpt);
model->Train(regressors, responses, lbfgsOpt);
}
}
@@ -278,7 +280,7 @@ static void mlpackMain()
{
Log::Info << "Predicting classes of points in '"
<< CLI::GetPrintableParam<arma::mat>("test") << "'." << endl;
model.Classify(testSet, predictions, decisionBoundary);
model->Classify(testSet, predictions, decisionBoundary);
CLI::GetParam<arma::Row<size_t>>("output") = std::move(predictions);
}
@@ -288,13 +290,12 @@ static void mlpackMain()
Log::Info << "Calculating class probabilities of points in '"
<< CLI::GetPrintableParam<arma::mat>("test") << "'." << endl;
arma::mat probabilities;
model.Classify(testSet, probabilities);
model->Classify(testSet, probabilities);
CLI::GetParam<arma::mat>("output_probabilities") =
std::move(probabilities);
}
}
if (CLI::HasParam("output_model"))
CLI::GetParam<LogisticRegression<>>("output_model") = std::move(model);
CLI::GetParam<LogisticRegression<>*>("output_model") = model;
}
+11 -13
View File
@@ -154,9 +154,10 @@ static void mlpackMain()
Log::Info << "Using LSH with " << numProj << " projections (K) and " <<
numTables << " tables (L) with hash width(r): " << hashWidth << endl;
LSHSearch<> allkann;
LSHSearch<>* allkann;
if (CLI::HasParam("reference"))
{
allkann = new LSHSearch<>();
referenceData = std::move(CLI::GetParam<arma::mat>("reference"));
Log::Info << "Using reference data from '"
<< CLI::GetPrintableParam<arma::mat>("reference") << "' ("
@@ -164,13 +165,13 @@ static void mlpackMain()
<< endl;
Timer::Start("hash_building");
allkann.Train(std::move(referenceData), numProj, numTables, hashWidth,
allkann->Train(std::move(referenceData), numProj, numTables, hashWidth,
secondHashSize, bucketSize);
Timer::Stop("hash_building");
}
else if (CLI::HasParam("input_model"))
{
allkann = std::move(CLI::GetParam<LSHSearch<>>("input_model"));
allkann = CLI::GetParam<LSHSearch<>*>("input_model");
}
if (CLI::HasParam("k"))
@@ -184,11 +185,11 @@ static void mlpackMain()
<< CLI::GetPrintableParam<arma::mat>("query") << "' ("
<< queryData.n_rows << " x " << queryData.n_cols << ")." << endl;
allkann.Search(queryData, k, neighbors, distances, 0, numProbes);
allkann->Search(queryData, k, neighbors, distances, 0, numProbes);
}
else
{
allkann.Search(k, neighbors, distances, 0, numProbes);
allkann->Search(k, neighbors, distances, 0, numProbes);
}
Log::Info << "Neighbors computed." << endl;
@@ -205,20 +206,17 @@ static void mlpackMain()
<< endl;
// Compute recall and print it.
double recallPercentage = 100 * allkann.ComputeRecall(neighbors,
double recallPercentage = 100 * allkann->ComputeRecall(neighbors,
trueNeighbors);
Log::Info << "Recall: " << recallPercentage << endl;
}
// Save output, if desired.
// Save output, if we did a search..
if (CLI::HasParam("k"))
{
if (CLI::HasParam("distances"))
CLI::GetParam<arma::mat>("distances") = std::move(distances);
if (CLI::HasParam("neighbors"))
CLI::GetParam<arma::Mat<size_t>>("neighbors") = std::move(neighbors);
CLI::GetParam<arma::mat>("distances") = std::move(distances);
CLI::GetParam<arma::Mat<size_t>>("neighbors") = std::move(neighbors);
}
if (CLI::HasParam("output_model"))
CLI::GetParam<LSHSearch<>>("output_model") = std::move(allkann);
CLI::GetParam<LSHSearch<>*>("output_model") = allkann;
}
+13 -14
View File
@@ -118,9 +118,10 @@ static void mlpackMain()
Log::Warn << "No test set given; no task will be performed!" << std::endl;
// Either we have to train a model, or load a model.
NBCModel model;
NBCModel* model;
if (CLI::HasParam("training"))
{
model = new NBCModel();
mat trainingData = std::move(CLI::GetParam<mat>("training"));
Row<size_t> labels;
@@ -130,7 +131,7 @@ static void mlpackMain()
{
// Load labels.
Row<size_t> rawLabels = std::move(CLI::GetParam<Row<size_t>>("labels"));
data::NormalizeLabels(rawLabels, labels, model.mappings);
data::NormalizeLabels(rawLabels, labels, model->mappings);
}
else
{
@@ -138,7 +139,7 @@ static void mlpackMain()
Log::Info << "Using last dimension of training data as training labels."
<< endl;
data::NormalizeLabels(trainingData.row(trainingData.n_rows - 1), labels,
model.mappings);
model->mappings);
// Remove the label row.
trainingData.shed_row(trainingData.n_rows - 1);
}
@@ -146,14 +147,14 @@ static void mlpackMain()
const bool incrementalVariance = CLI::HasParam("incremental_variance");
Timer::Start("nbc_training");
model.nbc = NaiveBayesClassifier<>(trainingData, labels,
model.mappings.n_elem, incrementalVariance);
model->nbc = NaiveBayesClassifier<>(trainingData, labels,
model->mappings.n_elem, incrementalVariance);
Timer::Stop("nbc_training");
}
else
{
// Load the model from file.
model = std::move(CLI::GetParam<NBCModel>("input_model"));
model = CLI::GetParam<NBCModel*>("input_model");
}
// Do we need to do testing?
@@ -161,10 +162,10 @@ static void mlpackMain()
{
mat testingData = std::move(CLI::GetParam<mat>("test"));
if (testingData.n_rows != model.nbc.Means().n_rows)
if (testingData.n_rows != model->nbc.Means().n_rows)
{
Log::Fatal << "Test data dimensionality (" << testingData.n_rows << ") "
<< "must be the same as training data (" << model.nbc.Means().n_rows
<< "must be the same as training data (" << model->nbc.Means().n_rows
<< ")!" << std::endl;
}
@@ -172,23 +173,21 @@ static void mlpackMain()
Row<size_t> predictions;
mat probabilities;
Timer::Start("nbc_testing");
model.nbc.Classify(testingData, predictions, probabilities);
model->nbc.Classify(testingData, predictions, probabilities);
Timer::Stop("nbc_testing");
if (CLI::HasParam("output"))
{
// Un-normalize labels to prepare output.
Row<size_t> rawResults;
data::RevertLabels(predictions, model.mappings, rawResults);
data::RevertLabels(predictions, model->mappings, rawResults);
// Output results.
CLI::GetParam<Row<size_t>>("output") = std::move(rawResults);
}
if (CLI::HasParam("output_probs"))
CLI::GetParam<mat>("output_probs") = probabilities;
CLI::GetParam<mat>("output_probs") = probabilities;
}
if (CLI::HasParam("output_model"))
CLI::GetParam<NBCModel>("output_model") = std::move(model);
CLI::GetParam<NBCModel*>("output_model") = model;
}
+24 -24
View File
@@ -157,7 +157,7 @@ static void mlpackMain()
epsilon = 1 - percentage;
// We either have to load the reference data, or we have to load the model.
NSModel<FurthestNeighborSort> kfn;
NSModel<FurthestNeighborSort>* kfn;
const string algorithm = CLI::GetParam<string>("algorithm");
RequireParamInSet<string>("algorithm", { "naive", "single_tree", "dual_tree",
@@ -175,6 +175,8 @@ static void mlpackMain()
if (CLI::HasParam("reference"))
{
kfn = new KFNModel();
// Get all the parameters.
RequireParamInSet<string>("tree_type", { "kd", "cover", "r", "r-star",
"ball", "x", "hilbert-r", "r-plus", "r-plus-plus", "vp", "rp", "max-rp",
@@ -212,8 +214,8 @@ static void mlpackMain()
else if (treeType == "oct")
tree = KFNModel::OCTREE;
kfn.TreeType() = tree;
kfn.RandomBasis() = randomBasis;
kfn->TreeType() = tree;
kfn->RandomBasis() = randomBasis;
arma::mat referenceSet = std::move(CLI::GetParam<arma::mat>("reference"));
@@ -221,27 +223,28 @@ static void mlpackMain()
<< CLI::GetPrintableParam<arma::mat>("reference") << "' ("
<< referenceSet.n_rows << "x" << referenceSet.n_cols << ")." << endl;
kfn.BuildModel(std::move(referenceSet), size_t(lsInt), searchMode, epsilon);
kfn->BuildModel(std::move(referenceSet), size_t(lsInt), searchMode,
epsilon);
}
else
{
// Load the model from file.
kfn = std::move(CLI::GetParam<KFNModel>("input_model"));
kfn = CLI::GetParam<KFNModel*>("input_model");
// Adjust search mode.
kfn.SearchMode() = searchMode;
kfn.Epsilon() = epsilon;
kfn->SearchMode() = searchMode;
kfn->Epsilon() = epsilon;
// If leaf_size wasn't provided, let's consider the current value in the
// loaded model. Else, update it (only considered when building the query
// tree).
if (CLI::HasParam("leaf_size"))
kfn.LeafSize() = size_t(lsInt);
kfn->LeafSize() = size_t(lsInt);
Log::Info << "Using kFN model from '"
<< CLI::GetPrintableParam<KFNModel>("input_model") << "' (trained on "
<< kfn.Dataset().n_rows << "x" << kfn.Dataset().n_cols << " dataset)."
<< endl;
<< CLI::GetPrintableParam<KFNModel*>("input_model") << "' (trained on "
<< kfn->Dataset().n_rows << "x" << kfn->Dataset().n_cols
<< " dataset)." << endl;
}
// Perform search, if desired.
@@ -261,11 +264,11 @@ static void mlpackMain()
// Sanity check on k value: must be greater than 0, must be less than the
// number of reference points. Since it is unsigned, we only test the upper
// bound.
if (k > kfn.Dataset().n_cols)
if (k > kfn->Dataset().n_cols)
{
Log::Fatal << "Invalid k: " << k << "; must be greater than 0 and less "
<< "than or equal to the number of reference points ("
<< kfn.Dataset().n_cols << ")." << endl;
<< kfn->Dataset().n_cols << ")." << endl;
}
// Now run the search.
@@ -273,21 +276,19 @@ static void mlpackMain()
arma::mat distances;
if (CLI::HasParam("query"))
kfn.Search(std::move(queryData), k, neighbors, distances);
kfn->Search(std::move(queryData), k, neighbors, distances);
else
kfn.Search(k, neighbors, distances);
kfn->Search(k, neighbors, distances);
Log::Info << "Search complete." << endl;
// Save output, if desired.
if (CLI::HasParam("neighbors"))
CLI::GetParam<arma::Mat<size_t>>("neighbors") = std::move(neighbors);
if (CLI::HasParam("distances"))
CLI::GetParam<arma::mat>("distances") = std::move(distances);
// Save output.
CLI::GetParam<arma::Mat<size_t>>("neighbors") = std::move(neighbors);
CLI::GetParam<arma::mat>("distances") = std::move(distances);
// Calculate the effective error, if desired.
if (CLI::HasParam("true_distances"))
{
if (kfn.Epsilon() == 0)
if (kfn->Epsilon() == 0)
Log::Warn << PRINT_PARAM_STRING("true_distances") << " specified, but "
<< "the search is exact, so there is no need to calculate the "
<< "error!" << endl;
@@ -307,7 +308,7 @@ static void mlpackMain()
// Calculate the recall, if desired.
if (CLI::HasParam("true_neighbors"))
{
if (kfn.Epsilon() == 0)
if (kfn->Epsilon() == 0)
Log::Warn << PRINT_PARAM_STRING("true_neighbors") << " specified, but "
<< "the search is exact, so there is no need to calculate the "
<< "recall!" << endl;
@@ -324,6 +325,5 @@ static void mlpackMain()
}
}
if (CLI::HasParam("output_model"))
CLI::GetParam<KFNModel>("output_model") = std::move(kfn);
CLI::GetParam<KFNModel*>("output_model") = kfn;
}
+27 -27
View File
@@ -166,7 +166,7 @@ static void mlpackMain()
"epsilon must be positive");
// We either have to load the reference data, or we have to load the model.
KNNModel knn;
KNNModel* knn;
const string algorithm = CLI::GetParam<string>("algorithm");
RequireParamInSet<string>("algorithm", { "naive", "single_tree", "dual_tree",
@@ -184,6 +184,8 @@ static void mlpackMain()
if (CLI::HasParam("reference"))
{
knn = new KNNModel();
// Get all the parameters.
const string treeType = CLI::GetParam<string>("tree_type");
const bool randomBasis = CLI::HasParam("random_basis");
@@ -223,11 +225,11 @@ static void mlpackMain()
else if (treeType == "oct")
tree = KNNModel::OCTREE;
knn.TreeType() = tree;
knn.RandomBasis() = randomBasis;
knn.LeafSize() = size_t(lsInt);
knn.Tau() = tau;
knn.Rho() = rho;
knn->TreeType() = tree;
knn->RandomBasis() = randomBasis;
knn->LeafSize() = size_t(lsInt);
knn->Tau() = tau;
knn->Rho() = rho;
arma::mat referenceSet = std::move(CLI::GetParam<arma::mat>("reference"));
@@ -236,27 +238,28 @@ static void mlpackMain()
<< referenceSet.n_rows << " x " << referenceSet.n_cols << ")."
<< endl;
knn.BuildModel(std::move(referenceSet), size_t(lsInt), searchMode, epsilon);
knn->BuildModel(std::move(referenceSet), size_t(lsInt), searchMode,
epsilon);
}
else
{
// Load the model from file.
knn = std::move(CLI::GetParam<KNNModel>("input_model"));
knn = CLI::GetParam<KNNModel*>("input_model");
// Adjust search mode.
knn.SearchMode() = searchMode;
knn.Epsilon() = epsilon;
knn->SearchMode() = searchMode;
knn->Epsilon() = epsilon;
// If leaf_size wasn't provided, let's consider the current value in the
// loaded model. Else, update it (only considered when building the query
// tree).
if (CLI::HasParam("leaf_size"))
knn.LeafSize() = size_t(lsInt);
knn->LeafSize() = size_t(lsInt);
Log::Info << "Loaded kNN model from '"
<< CLI::GetPrintableParam<KNNModel>("input_model") << "' (trained on "
<< knn.Dataset().n_rows << "x" << knn.Dataset().n_cols << " dataset)."
<< endl;
<< CLI::GetPrintableParam<KNNModel*>("input_model") << "' (trained on "
<< knn->Dataset().n_rows << "x" << knn->Dataset().n_cols
<< " dataset)." << endl;
}
// Perform search, if desired.
@@ -276,11 +279,11 @@ static void mlpackMain()
// Sanity check on k value: must be greater than 0, must be less than the
// number of reference points. Since it is unsigned, we only test the upper
// bound.
if (k > knn.Dataset().n_cols)
if (k > knn->Dataset().n_cols)
{
Log::Fatal << "Invalid k: " << k << "; must be greater than 0 and less ";
Log::Fatal << "than or equal to the number of reference points (";
Log::Fatal << knn.Dataset().n_cols << ")." << endl;
Log::Fatal << knn->Dataset().n_cols << ")." << endl;
}
// Now run the search.
@@ -288,21 +291,19 @@ static void mlpackMain()
arma::mat distances;
if (CLI::HasParam("query"))
knn.Search(std::move(queryData), k, neighbors, distances);
knn->Search(std::move(queryData), k, neighbors, distances);
else
knn.Search(k, neighbors, distances);
knn->Search(k, neighbors, distances);
Log::Info << "Search complete." << endl;
// Save output, if desired.
if (CLI::HasParam("neighbors"))
CLI::GetParam<arma::Mat<size_t>>("neighbors") = std::move(neighbors);
if (CLI::HasParam("distances"))
CLI::GetParam<arma::mat>("distances") = std::move(distances);
// Save output.
CLI::GetParam<arma::Mat<size_t>>("neighbors") = std::move(neighbors);
CLI::GetParam<arma::mat>("distances") = std::move(distances);
// Calculate the effective error, if desired.
if (CLI::HasParam("true_distances"))
{
if (knn.TreeType() != KNNModel::SPILL_TREE && knn.Epsilon() == 0)
if (knn->TreeType() != KNNModel::SPILL_TREE && knn->Epsilon() == 0)
Log::Warn << PRINT_PARAM_STRING("true_distances") << "specified, but "
<< "the search is exact, so there is no need to calculate the "
<< "error!" << endl;
@@ -322,7 +323,7 @@ static void mlpackMain()
// Calculate the recall, if desired.
if (CLI::HasParam("true_neighbors"))
{
if (knn.TreeType() != KNNModel::SPILL_TREE && knn.Epsilon() == 0)
if (knn->TreeType() != KNNModel::SPILL_TREE && knn->Epsilon() == 0)
Log::Warn << PRINT_PARAM_STRING("true_neighbors") << " specified, but "
<< " the search is exact, so there is no need to calculate the "
<< "recall!" << endl;
@@ -339,6 +340,5 @@ static void mlpackMain()
}
}
if (CLI::HasParam("output_model"))
CLI::GetParam<KNNModel>("output_model") = std::move(knn);
CLI::GetParam<KNNModel*>("output_model") = knn;
}
@@ -137,14 +137,18 @@ static void mlpackMain()
true, "maximum number of iterations must be nonnegative");
// Now, load our model, if there is one.
PerceptronModel p;
PerceptronModel* p;
if (CLI::HasParam("input_model"))
{
Log::Info << "Using saved perceptron from "
<< CLI::GetPrintableParam<PerceptronModel>("input_model") << "."
<< CLI::GetPrintableParam<PerceptronModel*>("input_model") << "."
<< endl;
p = std::move(CLI::GetParam<PerceptronModel>("input_model"));
p = CLI::GetParam<PerceptronModel*>("input_model");
}
else
{
p = new PerceptronModel();
}
// Next, load the training data and labels (if they have been given).
@@ -186,8 +190,8 @@ static void mlpackMain()
// Normalize the labels.
Row<size_t> labels;
data::NormalizeLabels(labelsIn, labels, p.Map());
const size_t numClasses = p.Map().n_elem;
data::NormalizeLabels(labelsIn, labels, p->Map());
const size_t numClasses = p->Map().n_elem;
// Now, if we haven't already created a perceptron, do it. Otherwise, make
// sure the dimensions are right, then continue training.
@@ -195,35 +199,35 @@ static void mlpackMain()
{
// Create and train the classifier.
Timer::Start("training");
p.P() = Perceptron<>(trainingData, labels, numClasses, maxIterations);
p->P() = Perceptron<>(trainingData, labels, numClasses, maxIterations);
Timer::Stop("training");
}
else
{
// Check dimensionality.
if (p.P().Weights().n_rows != trainingData.n_rows)
if (p->P().Weights().n_rows != trainingData.n_rows)
{
Log::Fatal << "Perceptron from '"
<< CLI::GetPrintableParam<PerceptronModel>("input_model")
<< "' is built on data with " << p.P().Weights().n_rows
<< CLI::GetPrintableParam<PerceptronModel*>("input_model")
<< "' is built on data with " << p->P().Weights().n_rows
<< " dimensions, but data in '"
<< CLI::GetPrintableParam<arma::mat>("training") << "' has "
<< trainingData.n_rows << "dimensions!" << endl;
}
// Check the number of labels.
if (numClasses > p.P().Weights().n_cols)
if (numClasses > p->P().Weights().n_cols)
{
Log::Fatal << "Perceptron from '"
<< CLI::GetPrintableParam<PerceptronModel>("input_model") << "' "
<< "has " << p.P().Weights().n_cols << " classes, but the training "
<< "data has " << numClasses + 1 << " classes!" << endl;
<< CLI::GetPrintableParam<PerceptronModel*>("input_model") << "' "
<< "has " << p->P().Weights().n_cols << " classes, but the training"
<< " data has " << numClasses + 1 << " classes!" << endl;
}
// Now train.
Timer::Start("training");
p.P().MaxIterations() = maxIterations;
p.P().Train(trainingData, labels.t(), numClasses);
p->P().MaxIterations() = maxIterations;
p->P().Train(trainingData, labels.t(), numClasses);
Timer::Stop("training");
}
}
@@ -235,29 +239,28 @@ static void mlpackMain()
<< CLI::GetPrintableParam<arma::mat>("test") << "'." << endl;
mat testData = std::move(CLI::GetParam<arma::mat>("test"));
if (testData.n_rows != p.P().Weights().n_rows)
if (testData.n_rows != p->P().Weights().n_rows)
{
Log::Fatal << "Test data dimensionality (" << testData.n_rows << ") must "
<< "be the same as the dimensionality of the perceptron ("
<< p.P().Weights().n_rows << ")!" << endl;
<< p->P().Weights().n_rows << ")!" << endl;
}
// Time the running of the perceptron classifier.
Row<size_t> predictedLabels(testData.n_cols);
Timer::Start("testing");
p.P().Classify(testData, predictedLabels);
p->P().Classify(testData, predictedLabels);
Timer::Stop("testing");
// Un-normalize labels to prepare output.
Row<size_t> results;
data::RevertLabels(predictedLabels, p.Map(), results);
data::RevertLabels(predictedLabels, p->Map(), results);
// Save the predicted labels.
if (CLI::HasParam("output"))
CLI::GetParam<arma::Row<size_t>>("output") = std::move(results);
}
// Lastly, do we need to save the output model?
if (CLI::HasParam("output_model"))
CLI::GetParam<PerceptronModel>("output_model") = std::move(p);
// Lastly, save the output model.
CLI::GetParam<PerceptronModel*>("output_model") = p;
}
@@ -104,9 +104,11 @@ static void mlpackMain()
ReportIgnoredParam({{ "training", false }}, "num_trees");
ReportIgnoredParam({{ "training", false }}, "minimum_leaf_size");
RandomForestModel rfModel;
RandomForestModel* rfModel;
if (CLI::HasParam("training"))
{
rfModel = new RandomForestModel();
// Train the model on the given input data.
arma::mat data = std::move(CLI::GetParam<arma::mat>("training"));
arma::Row<size_t> labels =
@@ -121,13 +123,13 @@ static void mlpackMain()
const size_t numClasses = arma::max(labels) + 1;
// Train the model.
rfModel.rf.Train(data, labels, numClasses, numTrees, minimumLeafSize);
rfModel->rf.Train(data, labels, numClasses, numTrees, minimumLeafSize);
// Did we want training accuracy?
if (CLI::HasParam("print_training_accuracy"))
{
arma::Row<size_t> predictions;
rfModel.rf.Classify(data, predictions);
rfModel->rf.Classify(data, predictions);
const size_t correct = arma::accu(predictions == labels);
@@ -139,7 +141,7 @@ static void mlpackMain()
else
{
// Then we must be loading a model.
rfModel = std::move(CLI::GetParam<RandomForestModel>("input_model"));
rfModel = CLI::GetParam<RandomForestModel*>("input_model");
}
if (CLI::HasParam("test"))
@@ -149,7 +151,7 @@ static void mlpackMain()
// Get predictions and probabilities.
arma::Row<size_t> predictions;
arma::mat probabilities;
rfModel.rf.Classify(testData, predictions, probabilities);
rfModel->rf.Classify(testData, predictions, probabilities);
// Did we want to calculate test accuracy?
if (CLI::HasParam("test_labels"))
@@ -164,14 +166,11 @@ static void mlpackMain()
<< ")." << endl;
}
// Should we save the outputs?
if (CLI::HasParam("probabilities"))
CLI::GetParam<arma::mat>("probabilities") = std::move(probabilities);
if (CLI::HasParam("predictions"))
CLI::GetParam<arma::Row<size_t>>("predictions") = std::move(predictions);
// Save the outputs.
CLI::GetParam<arma::mat>("probabilities") = std::move(probabilities);
CLI::GetParam<arma::Row<size_t>>("predictions") = std::move(predictions);
}
// Did the user want to save the output model?
if (CLI::HasParam("output_model"))
CLI::GetParam<RandomForestModel>("output_model") = std::move(rfModel);
// Save the output model.
CLI::GetParam<RandomForestModel*>("output_model") = rfModel;
}
@@ -136,11 +136,13 @@ static void mlpackMain()
"leaf size must be greater than 0");
// We either have to load the reference data, or we have to load the model.
RSModel rs;
RSModel* rs;
const bool naive = CLI::HasParam("naive");
const bool singleMode = CLI::HasParam("single_mode");
if (CLI::HasParam("reference"))
{
rs = new RSModel();
// Get all the parameters.
const string treeType = CLI::GetParam<string>("tree_type");
RequireParamInSet<string>("tree_type", { "kd", "cover", "r", "r-star",
@@ -178,8 +180,8 @@ static void mlpackMain()
else if (treeType == "oct")
tree = RSModel::OCTREE;
rs.TreeType() = tree;
rs.RandomBasis() = randomBasis;
rs->TreeType() = tree;
rs->RandomBasis() = randomBasis;
arma::mat referenceSet = std::move(CLI::GetParam<arma::mat>("reference"));
@@ -189,22 +191,22 @@ static void mlpackMain()
const size_t leafSize = size_t(lsInt);
rs.BuildModel(std::move(referenceSet), leafSize, naive, singleMode);
rs->BuildModel(std::move(referenceSet), leafSize, naive, singleMode);
}
else
{
// Load the model from file.
rs = std::move(CLI::GetParam<RSModel>("input_model"));
rs = CLI::GetParam<RSModel*>("input_model");
Log::Info << "Using range search model from '"
<< CLI::GetPrintableParam<RSModel>("input_model") << "' ("
<< "trained on " << rs.Dataset().n_rows << "x" << rs.Dataset().n_cols
<< "trained on " << rs->Dataset().n_rows << "x" << rs->Dataset().n_cols
<< " dataset)." << endl;
// Adjust singleMode and naive if necessary.
rs.SingleMode() = CLI::HasParam("single_mode");
rs.Naive() = CLI::HasParam("naive");
rs.LeafSize() = size_t(lsInt);
rs->SingleMode() = CLI::HasParam("single_mode");
rs->Naive() = CLI::HasParam("naive");
rs->LeafSize() = size_t(lsInt);
}
// Perform search, if desired.
@@ -235,9 +237,9 @@ static void mlpackMain()
vector<vector<double>> distances;
if (CLI::HasParam("query"))
rs.Search(std::move(queryData), r, neighbors, distances);
rs->Search(std::move(queryData), r, neighbors, distances);
else
rs.Search(r, neighbors, distances);
rs->Search(r, neighbors, distances);
Log::Info << "Search complete." << endl;
@@ -301,7 +303,6 @@ static void mlpackMain()
}
}
// Save the output model, if desired.
if (CLI::HasParam("output_model"))
CLI::GetParam<RSModel>("output_model") = std::move(rs);
// Save the output model.
CLI::GetParam<RSModel*>("output_model") = rs;
}
+26 -26
View File
@@ -135,11 +135,13 @@ static void mlpackMain()
"leaf size must be greater than 0");
// We either have to load the reference data, or we have to load the model.
RANNModel rann;
RANNModel* rann;
const bool naive = CLI::HasParam("naive");
const bool singleMode = CLI::HasParam("single_mode");
if (CLI::HasParam("reference"))
{
rann = new RANNModel();
// Get all the parameters.
const string treeType = CLI::GetParam<string>("tree_type");
RequireParamInSet<string>("tree_type", { "kd", "cover", "r", "r-star", "x",
@@ -169,8 +171,8 @@ static void mlpackMain()
else if (treeType == "oct")
tree = RANNModel::OCTREE;
rann.TreeType() = tree;
rann.RandomBasis() = randomBasis;
rann->TreeType() = tree;
rann->RandomBasis() = randomBasis;
arma::mat referenceSet = std::move(CLI::GetParam<arma::mat>("reference"));
@@ -179,33 +181,33 @@ static void mlpackMain()
<< referenceSet.n_rows << " x " << referenceSet.n_cols << ")."
<< endl;
rann.BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode);
rann->BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode);
}
else
{
// Load the model from file.
rann = std::move(CLI::GetParam<RANNModel>("input_model"));
rann = CLI::GetParam<RANNModel*>("input_model");
Log::Info << "Using rank-approximate kNN model from '"
<< CLI::GetPrintableParam<RANNModel>("input_model") << "' (trained on "
<< rann.Dataset().n_rows << "x" << rann.Dataset().n_cols << " dataset)."
<< endl;
<< rann->Dataset().n_rows << "x" << rann->Dataset().n_cols
<< " dataset)." << endl;
// Adjust singleMode and naive if necessary.
rann.SingleMode() = CLI::HasParam("single_mode");
rann.Naive() = CLI::HasParam("naive");
rann.LeafSize() = size_t(lsInt);
rann->SingleMode() = CLI::HasParam("single_mode");
rann->Naive() = CLI::HasParam("naive");
rann->LeafSize() = size_t(lsInt);
}
// Apply the parameters for search.
if (CLI::HasParam("tau"))
rann.Tau() = CLI::GetParam<double>("tau");
rann->Tau() = CLI::GetParam<double>("tau");
if (CLI::HasParam("alpha"))
rann.Alpha() = CLI::GetParam<double>("alpha");
rann->Alpha() = CLI::GetParam<double>("alpha");
if (CLI::HasParam("single_sample_limit"))
rann.SingleSampleLimit() = CLI::GetParam<double>("single_sample_limit");
rann.SampleAtLeaves() = CLI::HasParam("sample_at_leaves");
rann.FirstLeafExact() = CLI::HasParam("sample_at_leaves");
rann->SingleSampleLimit() = CLI::GetParam<double>("single_sample_limit");
rann->SampleAtLeaves() = CLI::HasParam("sample_at_leaves");
rann->FirstLeafExact() = CLI::HasParam("sample_at_leaves");
// Perform search, if desired.
if (CLI::HasParam("k"))
@@ -224,28 +226,26 @@ static void mlpackMain()
// Sanity check on k value: must be greater than 0, must be less than the
// number of reference points. Since it is unsigned, we only test the upper
// bound.
if (k > rann.Dataset().n_cols)
if (k > rann->Dataset().n_cols)
{
Log::Fatal << "Invalid k: " << k << "; must be greater than 0 and less ";
Log::Fatal << "than or equal to the number of reference points (";
Log::Fatal << rann.Dataset().n_cols << ")." << endl;
Log::Fatal << rann->Dataset().n_cols << ")." << endl;
}
arma::Mat<size_t> neighbors;
arma::mat distances;
if (CLI::HasParam("query"))
rann.Search(std::move(queryData), k, neighbors, distances);
rann->Search(std::move(queryData), k, neighbors, distances);
else
rann.Search(k, neighbors, distances);
rann->Search(k, neighbors, distances);
Log::Info << "Search complete." << endl;
// Save output, if desired.
if (CLI::HasParam("neighbors"))
CLI::GetParam<arma::Mat<size_t>>("neighbors") = std::move(neighbors);
if (CLI::HasParam("distances"))
CLI::GetParam<arma::mat>("distances") = std::move(distances);
// Save output.
CLI::GetParam<arma::Mat<size_t>>("neighbors") = std::move(neighbors);
CLI::GetParam<arma::mat>("distances") = std::move(distances);
}
if (CLI::HasParam("output_model"))
CLI::GetParam<RANNModel>("output_model") = std::move(rann);
// Save the output model.
CLI::GetParam<RANNModel*>("output_model") = rann;
}
@@ -113,7 +113,7 @@ void TestClassifyAcc(const size_t numClasses, const Model& model);
// Build the softmax model given the parameters.
template<typename Model>
unique_ptr<Model> TrainSoftmax(const size_t maxIterations);
Model* TrainSoftmax(const size_t maxIterations);
static void mlpackMain()
{
@@ -144,13 +144,11 @@ static void mlpackMain()
RequireAtLeastOnePassed({ "output_model", "predictions" }, false, "no results"
" will be saved");
using SM = SoftmaxRegression;
unique_ptr<SM> sm = TrainSoftmax<SM>(maxIterations);
SoftmaxRegression* sm = TrainSoftmax<SoftmaxRegression>(maxIterations);
TestClassifyAcc(sm->NumClasses(), *sm);
if (CLI::HasParam("output_model"))
CLI::GetParam<SM>("output_model") = std::move(*sm);
CLI::GetParam<SoftmaxRegression*>("output_model") = sm;
}
size_t CalculateNumberOfClasses(const size_t numClasses,
@@ -233,17 +231,14 @@ void TestClassifyAcc(size_t numClasses, const Model& model)
}
template<typename Model>
unique_ptr<Model> TrainSoftmax(const size_t maxIterations)
Model* TrainSoftmax(const size_t maxIterations)
{
using namespace mlpack;
using SRF = regression::SoftmaxRegressionFunction;
unique_ptr<Model> sm;
Model* sm;
if (CLI::HasParam("input_model"))
{
sm.reset(new Model(0, 0, false));
*sm = std::move(CLI::GetParam<Model>("input_model"));
sm = CLI::GetParam<Model*>("input_model");
}
else
{
@@ -262,8 +257,8 @@ unique_ptr<Model> TrainSoftmax(const size_t maxIterations)
const size_t numBasis = 5;
optimization::L_BFGS optimizer(numBasis, maxIterations);
sm.reset(new Model(trainData, trainLabels, numClasses,
CLI::GetParam<double>("lambda"), intercept, std::move(optimizer)));
sm = new Model(trainData, trainLabels, numClasses,
CLI::GetParam<double>("lambda"), intercept, std::move(optimizer));
}
return sm;
@@ -146,9 +146,11 @@ static void mlpackMain()
"Newton method tolerance must be nonnegative");
// Do we have an existing model?
SparseCoding sc(0, 0.0);
SparseCoding* sc;
if (CLI::HasParam("input_model"))
sc = std::move(CLI::GetParam<SparseCoding>("input_model"));
sc = CLI::GetParam<SparseCoding*>("input_model");
else
sc = new SparseCoding(0, 0.0);
if (CLI::HasParam("training"))
{
@@ -162,12 +164,12 @@ static void mlpackMain()
matX.col(i) /= norm(matX.col(i), 2);
}
sc.Lambda1() = CLI::GetParam<double>("lambda1");
sc.Lambda2() = CLI::GetParam<double>("lambda2");
sc.MaxIterations() = (size_t) CLI::GetParam<int>("max_iterations");
sc.Atoms() = (size_t) CLI::GetParam<int>("atoms");
sc.ObjTolerance() = CLI::GetParam<double>("objective_tolerance");
sc.NewtonTolerance() = CLI::GetParam<double>("newton_tolerance");
sc->Lambda1() = CLI::GetParam<double>("lambda1");
sc->Lambda2() = CLI::GetParam<double>("lambda2");
sc->MaxIterations() = (size_t) CLI::GetParam<int>("max_iterations");
sc->Atoms() = (size_t) CLI::GetParam<int>("atoms");
sc->ObjTolerance() = CLI::GetParam<double>("objective_tolerance");
sc->NewtonTolerance() = CLI::GetParam<double>("newton_tolerance");
// Inform the user if we are overwriting their model.
if (CLI::HasParam("input_model"))
@@ -175,36 +177,36 @@ static void mlpackMain()
Log::Info << "Using dictionary from existing model in '"
<< CLI::GetPrintableParam<SparseCoding>("input_model")
<< "' as initial dictionary for training." << endl;
sc.Train<NothingInitializer>(matX);
sc->Train<NothingInitializer>(matX);
}
else if (CLI::HasParam("initial_dictionary"))
{
// Load initial dictionary directly into sparse coding object.
sc.Dictionary() =
sc->Dictionary() =
std::move(CLI::GetParam<arma::mat>("initial_dictionary"));
// Validate size of initial dictionary.
if (sc.Dictionary().n_cols != sc.Atoms())
if (sc->Dictionary().n_cols != sc->Atoms())
{
Log::Fatal << "The initial dictionary has " << sc.Dictionary().n_cols
Log::Fatal << "The initial dictionary has " << sc->Dictionary().n_cols
<< " atoms, but the number of atoms was specified to be "
<< sc.Atoms() << "!" << endl;
<< sc->Atoms() << "!" << endl;
}
if (sc.Dictionary().n_rows != matX.n_rows)
if (sc->Dictionary().n_rows != matX.n_rows)
{
Log::Fatal << "The initial dictionary has " << sc.Dictionary().n_rows
Log::Fatal << "The initial dictionary has " << sc->Dictionary().n_rows
<< " dimensions, but the data has " << matX.n_rows << " dimensions!"
<< endl;
}
// Run sparse coding.
sc.Train<NothingInitializer>(matX);
sc->Train<NothingInitializer>(matX);
}
else
{
// Run sparse coding with the default initialization.
sc.Train(matX);
sc->Train(matX);
}
}
@@ -213,9 +215,9 @@ static void mlpackMain()
{
mat matY = std::move(CLI::GetParam<arma::mat>("test"));
if (matY.n_rows != sc.Dictionary().n_rows)
if (matY.n_rows != sc->Dictionary().n_rows)
Log::Fatal << "Model was trained with a dimensionality of "
<< sc.Dictionary().n_rows << ", but test data '"
<< sc->Dictionary().n_rows << ", but test data '"
<< CLI::GetPrintableParam<arma::mat>("test") << "' have a "
<< "dimensionality of " << matY.n_rows << "!" << endl;
@@ -228,20 +230,15 @@ static void mlpackMain()
}
mat codes;
sc.Encode(matY, codes);
sc->Encode(matY, codes);
if (CLI::HasParam("codes"))
CLI::GetParam<arma::mat>("codes") = std::move(codes);
CLI::GetParam<arma::mat>("codes") = std::move(codes);
}
// Did the user want to save the dictionary? If so we can move that, but only
// if we are not also saving an output model.
if (CLI::HasParam("dictionary") && !CLI::HasParam("output_model"))
CLI::GetParam<arma::mat>("dictionary") = std::move(sc.Dictionary());
else if (CLI::HasParam("dictionary"))
CLI::GetParam<arma::mat>("dictionary") = sc.Dictionary();
// Did the user want to save the dictionary? Use an alias for the dictionary.
CLI::GetParam<arma::mat>("dictionary") = arma::mat(sc->Dictionary().memptr(),
sc->Dictionary().n_rows, sc->Dictionary().n_cols, false, false);
// Did the user want to save the model?
if (CLI::HasParam("output_model"))
CLI::GetParam<SparseCoding>("output_model") = std::move(sc);
// Save the model.
CLI::GetParam<SparseCoding*>("output_model") = sc;
}
+107 -13
View File
@@ -238,20 +238,21 @@ BOOST_AUTO_TEST_CASE(GetParamModelTest)
data::Save("kernel.bin", "model", gk);
// Create tuple.
gk.Bandwidth(2.0);
tuple<GaussianKernel, string> t = make_tuple(gk, filename);
tuple<GaussianKernel*, string> t = make_tuple((GaussianKernel*) NULL,
filename);
d.value = boost::any(t);
// Make sure it is not loaded yet.
d.input = true;
d.loaded = false;
GaussianKernel* output = NULL;
GetParam<GaussianKernel>((const util::ParamData&) d, (void*) NULL,
GaussianKernel** output = NULL;
GetParam<GaussianKernel*>((const util::ParamData&) d, (void*) NULL,
(void*) &output);
BOOST_REQUIRE_EQUAL(output->Bandwidth(), 5.0);
BOOST_REQUIRE_EQUAL((*output)->Bandwidth(), 5.0);
remove("kernel.bin");
delete *output;
}
BOOST_AUTO_TEST_CASE(RawParamDoubleTest)
@@ -300,17 +301,17 @@ BOOST_AUTO_TEST_CASE(GetRawParamModelTest)
kernel::GaussianKernel gk(5.0);
// Create tuple.
tuple<GaussianKernel, string> t = make_tuple(gk, filename);
tuple<GaussianKernel*, string> t = make_tuple(&gk, filename);
d.value = boost::any(t);
// Make sure it is not loaded yet.
d.input = true;
d.loaded = false;
tuple<GaussianKernel, string>* output = NULL;
GetRawParam<tuple<GaussianKernel, string>>((const util::ParamData&) d,
tuple<GaussianKernel*, string>* output = NULL;
GetRawParam<tuple<GaussianKernel*, string>>((const util::ParamData&) d,
(void*) NULL, (void*) &output);
BOOST_REQUIRE_EQUAL(get<0>(*output).Bandwidth(), 5.0);
BOOST_REQUIRE_EQUAL(get<0>(*output)->Bandwidth(), 5.0);
}
BOOST_AUTO_TEST_CASE(GetRawParamDatasetInfoTest)
@@ -403,7 +404,7 @@ BOOST_AUTO_TEST_CASE(OutputParamModelTest)
// Create value.
string filename = "kernel.bin";
GaussianKernel gk(5.0);
tuple<GaussianKernel, string> t = make_tuple(gk, filename);
tuple<GaussianKernel*, string> t = make_tuple(&gk, filename);
d.value = boost::any(t);
d.input = false;
@@ -491,7 +492,7 @@ BOOST_AUTO_TEST_CASE(SetParamModelTest)
// Create initial value.
string filename = "kernel.bin";
GaussianKernel gk(2.0);
d.value = boost::any(make_tuple(gk, filename));
d.value = boost::any(make_tuple(&gk, filename));
// Get a new string.
string newFilename = "new_kernel.bin";
@@ -501,8 +502,8 @@ BOOST_AUTO_TEST_CASE(SetParamModelTest)
(void*) NULL);
// Make sure the change went through.
tuple<GaussianKernel, string>& t =
*boost::any_cast<tuple<GaussianKernel, string>>(&d.value);
tuple<GaussianKernel*, string>& t =
*boost::any_cast<tuple<GaussianKernel*, string>>(&d.value);
BOOST_REQUIRE_EQUAL(get<1>(t), "new_kernel.bin");
}
@@ -537,4 +538,97 @@ BOOST_AUTO_TEST_CASE(SetParamDatasetInfoMatTest)
BOOST_REQUIRE_EQUAL(get<1>(t3), "new_filename.csv");
}
// Test that GetAllocatedMemory() will properly return NULL for a non-model
// type.
BOOST_AUTO_TEST_CASE(GetAllocatedMemoryNonModelTest)
{
util::ParamData d;
bool b = true;
d.value = boost::any(b);
d.input = true;
void* result = (void*) 1; // Invalid pointer, should be overwritten.
GetAllocatedMemory<bool>((const util::ParamData&) d,
(const void*) NULL, (void*) &result);
BOOST_REQUIRE_EQUAL(result, (void*) NULL);
// Also test with a matrix type.
arma::mat test(10, 10, arma::fill::ones);
string filename = "test.csv";
tuple<arma::mat, string> t = make_tuple(test, filename);
d.value = boost::any(t);
result = (void*) 1;
GetAllocatedMemory<arma::mat>((const util::ParamData&) d,
(const void*) NULL, (void*) &result);
BOOST_REQUIRE_EQUAL(result, (void*) NULL);
}
// Test that GetAllocatedMemory() will properly return pointers for a
// serializable model type.
BOOST_AUTO_TEST_CASE(GetAllocatedMemoryModelTest)
{
util::ParamData d;
GaussianKernel g(2.0);
string filename = "hello.bin";
tuple<GaussianKernel*, string> t = make_tuple(&g, filename);
d.value = boost::any(t);
d.input = true;
void* result = NULL;
GetAllocatedMemory<GaussianKernel*>((const util::ParamData&) d,
(const void*) NULL, (void*) &result);
BOOST_REQUIRE_EQUAL(&g, (GaussianKernel*) result);
}
// Test that calling DeleteAllocatedMemory() on non-model types does not delete
// pointers.
BOOST_AUTO_TEST_CASE(DeleteAllocatedMemoryNonModelTest)
{
util::ParamData d;
bool b = true;
d.value = boost::any(b);
d.input = true;
DeleteAllocatedMemory<bool>((const util::ParamData&) d,
(const void*) NULL, (void*) NULL);
arma::mat test(10, 10, arma::fill::ones);
string filename = "test.csv";
tuple<arma::mat, string> t = make_tuple(test, filename);
d.value = boost::any(t);
DeleteAllocatedMemory<arma::mat>((const util::ParamData&) d,
(const void*) NULL, (void*) NULL);
}
// Test that DeleteAllocatedMemory() will properly delete pointers for a
// serializable model type.
BOOST_AUTO_TEST_CASE(DeleteAllocatedMemoryModelTest)
{
// This test will just delete it, and we'll hope that it worked and that
// valgrind won't throw any issues (so really we can't *quite* test this in
// the context of the boost unit test framework).
util::ParamData d;
GaussianKernel* g = new GaussianKernel(2.0);
string filename = "hello.bin";
tuple<GaussianKernel*, string> t = make_tuple(g, filename);
d.value = boost::any(t);
d.input = false;
DeleteAllocatedMemory<GaussianKernel*>((const util::ParamData&) d,
(const void*) NULL, (void*) NULL);
}
BOOST_AUTO_TEST_SUITE_END();
+9 -6
View File
@@ -866,9 +866,9 @@ BOOST_AUTO_TEST_CASE(UnmappedParamTest)
BOOST_REQUIRE_EQUAL(CLI::GetPrintableParam<arma::mat>("matrix"), "file1.csv");
BOOST_REQUIRE_EQUAL(CLI::GetPrintableParam<arma::mat>("matrix2"),
"file2.csv");
BOOST_REQUIRE_EQUAL(CLI::GetPrintableParam<GaussianKernel>("kernel"),
BOOST_REQUIRE_EQUAL(CLI::GetPrintableParam<GaussianKernel*>("kernel"),
"kernel.txt");
BOOST_REQUIRE_EQUAL(CLI::GetPrintableParam<GaussianKernel>("kernel2"),
BOOST_REQUIRE_EQUAL(CLI::GetPrintableParam<GaussianKernel*>("kernel2"),
"kernel2.txt");
remove("kernel.txt");
@@ -894,9 +894,9 @@ BOOST_AUTO_TEST_CASE(SerializationTest)
ParseCommandLine(argc, const_cast<char**>(argv));
// Create the kernel we'll save.
GaussianKernel gk(0.5);
GaussianKernel* gk = new GaussianKernel(0.5);
CLI::GetParam<GaussianKernel>("kernel") = move(gk);
CLI::GetParam<GaussianKernel*>("kernel") = gk;
// Save it.
EndProgram();
@@ -910,9 +910,12 @@ BOOST_AUTO_TEST_CASE(SerializationTest)
ParseCommandLine(argc, const_cast<char**>(argv));
// Load the kernel from file.
GaussianKernel gk2 = move(CLI::GetParam<GaussianKernel>("kernel"));
GaussianKernel* gk2 = CLI::GetParam<GaussianKernel*>("kernel");
BOOST_REQUIRE_CLOSE(gk2.Bandwidth(), 0.5, 1e-5);
BOOST_REQUIRE_CLOSE(gk2->Bandwidth(), 0.5, 1e-5);
// Clean up the memory...
delete gk2;
// Now remove the file we made.
remove("kernel.txt");
-1
View File
@@ -761,7 +761,6 @@ BOOST_AUTO_TEST_CASE(UseExistingModelTest)
*/
BOOST_AUTO_TEST_CASE(DiagonalGMMTrainTest)
{
Log::Warn.ignoreInput = false;
// We'll have three diagonal-covariance Gaussian distributions from this
// mixture.
distribution::GaussianDistribution d1("0.0 1.0 0.0", "1.0 0.0 0.0;"
@@ -35,6 +35,7 @@ struct DecisionStumpTestFixture
~DecisionStumpTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
@@ -136,6 +137,9 @@ BOOST_AUTO_TEST_CASE(DecisionStumpLabelsLessDimensionTest)
arma::Row<size_t> predictions;
predictions = std::move(CLI::GetParam<arma::Row<size_t>>("predictions"));
// Delete the previous model.
bindings::tests::CleanMemory();
// Now train DS with labels provided.
// Delete last row of inputData.
@@ -198,7 +202,7 @@ BOOST_AUTO_TEST_CASE(DecisionStumpModelReuseTest)
// Input trained model.
SetInputParam("test", std::move(testData));
SetInputParam("input_model",
std::move(CLI::GetParam<DSModel>("output_model")));
std::move(CLI::GetParam<DSModel*>("output_model")));
mlpackMain();
@@ -249,7 +253,7 @@ BOOST_AUTO_TEST_CASE(DecisionStumpTrainingVerTest)
// Input pre-trained model.
SetInputParam("input_model",
std::move(CLI::GetParam<DSModel>("output_model")));
std::move(CLI::GetParam<DSModel*>("output_model")));
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
@@ -36,6 +36,7 @@ struct DecisionTreeTestFixture
~DecisionTreeTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
@@ -207,7 +208,7 @@ BOOST_AUTO_TEST_CASE(DecisionModelReuseTest)
// Input trained model.
SetInputParam("test", std::move(std::make_tuple(info, testData)));
SetInputParam("input_model",
std::move(CLI::GetParam<DecisionTreeModel>("output_model")));
std::move(CLI::GetParam<DecisionTreeModel*>("output_model")));
mlpackMain();
@@ -254,7 +255,7 @@ BOOST_AUTO_TEST_CASE(DecisionTreeTrainingVerTest)
// Input pre-trained model.
SetInputParam("input_model",
std::move(CLI::GetParam<DecisionTreeModel>("output_model")));
std::move(CLI::GetParam<DecisionTreeModel*>("output_model")));
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
@@ -308,7 +309,7 @@ BOOST_AUTO_TEST_CASE(DecisionModelCategoricalReuseTest)
// Input trained model.
SetInputParam("test", std::move(std::make_tuple(info, testData)));
SetInputParam("input_model",
std::move(CLI::GetParam<DecisionTreeModel>("output_model")));
std::move(CLI::GetParam<DecisionTreeModel*>("output_model")));
mlpackMain();
@@ -38,6 +38,7 @@ struct EMSTTestFixture
~EMSTTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
@@ -32,6 +32,7 @@ struct LRTestFixture
~LRTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
@@ -66,6 +67,7 @@ BOOST_AUTO_TEST_CASE(LRDifferentLambdas)
mlpackMain();
const double testY1 = CLI::GetParam<arma::rowvec>("output_predictions")(0);
bindings::tests::CleanMemory();
ResetSettings();
SetInputParam("training", std::move(trainX));
@@ -100,6 +102,7 @@ BOOST_AUTO_TEST_CASE(LRResponsesRepresentation)
mlpackMain();
const double testY1 = CLI::GetParam<arma::rowvec>("output_predictions")(0);
bindings::tests::CleanMemory();
ResetSettings();
arma::mat trainX2({1.0, 2.0, 3.0});
@@ -135,12 +138,12 @@ BOOST_AUTO_TEST_CASE(LRModelReload)
mlpackMain();
LinearRegression model = CLI::GetParam<LinearRegression>("output_model");
LinearRegression* model = CLI::GetParam<LinearRegression*>("output_model");
const arma::rowvec testY1 = CLI::GetParam<arma::rowvec>("output_predictions");
ResetSettings();
SetInputParam("input_model", std::move(model));
SetInputParam("input_model", model);
SetInputParam("test", std::move(testX));
mlpackMain();
@@ -209,7 +212,7 @@ BOOST_AUTO_TEST_CASE(LRWrongDimOfDataTest2)
mlpackMain();
LinearRegression model = CLI::GetParam<LinearRegression>("output_model");
LinearRegression* model = CLI::GetParam<LinearRegression*>("output_model");
ResetSettings();
+7 -2
View File
@@ -35,6 +35,7 @@ struct NBCTestFixture
~NBCTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
@@ -142,6 +143,8 @@ BOOST_AUTO_TEST_CASE(NBCLabelsLessDimensionTest)
output = std::move(CLI::GetParam<arma::Row<size_t>>("output"));
output_probs = std::move(CLI::GetParam<arma::mat>("output_probs"));
bindings::tests::CleanMemory();
// Now train NBC with labels provided.
inputData.shed_row(inputData.n_rows - 1);
@@ -208,7 +211,7 @@ BOOST_AUTO_TEST_CASE(NBCModelReuseTest)
// Input trained model.
SetInputParam("test", std::move(testData));
SetInputParam("input_model",
std::move(CLI::GetParam<NBCModel>("output_model")));
std::move(CLI::GetParam<NBCModel*>("output_model")));
mlpackMain();
@@ -244,7 +247,7 @@ BOOST_AUTO_TEST_CASE(NBCTrainingVerTest)
// Input pre-trained model.
SetInputParam("input_model",
std::move(CLI::GetParam<NBCModel>("output_model")));
std::move(CLI::GetParam<NBCModel*>("output_model")));
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
@@ -290,6 +293,8 @@ BOOST_AUTO_TEST_CASE(NBCIncrementalVarianceTest)
BOOST_REQUIRE_EQUAL(CLI::GetParam<arma::Row<size_t>>("output").n_rows, 1);
BOOST_REQUIRE_EQUAL(CLI::GetParam<arma::mat>("output_probs").n_rows, 2);
bindings::tests::CleanMemory();
// Reset data passed.
CLI::GetSingleton().Parameters()["training"].wasPassed = false;
CLI::GetSingleton().Parameters()["incremental_variance"].wasPassed = false;
+1
View File
@@ -31,6 +31,7 @@ struct PCATestFixture
~PCATestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
@@ -35,6 +35,7 @@ struct PerceptronTestFixture
~PerceptronTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
@@ -137,7 +138,9 @@ BOOST_AUTO_TEST_CASE(PerceptronLabelsLessDimensionTest)
arma::Row<size_t> output;
output = std::move(CLI::GetParam<arma::Row<size_t>>("output"));
// Now train pereptron with labels provided.
bindings::tests::CleanMemory();
// Now train perceptron with labels provided.
// Input training data.
SetInputParam("training", std::move(inputData));
@@ -195,7 +198,7 @@ BOOST_AUTO_TEST_CASE(PerceptronModelReuseTest)
// Input trained model.
SetInputParam("test", std::move(testData));
SetInputParam("input_model",
std::move(CLI::GetParam<PerceptronModel>("output_model")));
CLI::GetParam<PerceptronModel*>("output_model"));
mlpackMain();
@@ -35,6 +35,7 @@ struct PreprocessBinarizeTestFixture
~PreprocessBinarizeTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
@@ -37,6 +37,7 @@ struct PreprocessImputerTestFixture
~PreprocessImputerTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
@@ -37,6 +37,7 @@ struct PreprocessSplitTestFixture
~PreprocessSplitTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
@@ -35,6 +35,7 @@ struct RandomForestTestFixture
~RandomForestTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
@@ -124,7 +125,7 @@ BOOST_AUTO_TEST_CASE(RandomForestModelReuseTest)
// Input trained model.
SetInputParam("test", std::move(testData));
SetInputParam("input_model",
std::move(CLI::GetParam<RandomForestModel>("output_model")));
CLI::GetParam<RandomForestModel*>("output_model"));
mlpackMain();
@@ -206,7 +207,7 @@ BOOST_AUTO_TEST_CASE(RandomForestTrainingVerTest)
// Input pre-trained model.
SetInputParam("input_model",
std::move(CLI::GetParam<RandomForestModel>("output_model")));
CLI::GetParam<RandomForestModel*>("output_model"));
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
@@ -218,7 +219,7 @@ BOOST_AUTO_TEST_CASE(RandomForestTrainingVerTest)
*/
BOOST_AUTO_TEST_CASE(RandomForestDiffMinLeafSizeTest)
{
// Train for minimium leaf size 20.
// Train for minimum leaf size 20.
arma::mat inputData;
if (!data::Load("vc2.csv", inputData))
BOOST_FAIL("Cannot load train dataset vc2.csv!");
@@ -236,13 +237,15 @@ BOOST_AUTO_TEST_CASE(RandomForestDiffMinLeafSizeTest)
// Calculate training accuracy.
arma::Row<size_t> predictions;
CLI::GetParam<RandomForestModel>("output_model").rf.Classify(inputData,
CLI::GetParam<RandomForestModel*>("output_model")->rf.Classify(inputData,
predictions);
size_t correct = arma::accu(predictions == labels);
double accuracy20 = (double(correct) / double(labels.n_elem) * 100);
// Train for minimium leaf size 10.
bindings::tests::CleanMemory();
// Train for minimum leaf size 10.
// Input training data.
SetInputParam("training", inputData);
@@ -252,13 +255,15 @@ BOOST_AUTO_TEST_CASE(RandomForestDiffMinLeafSizeTest)
mlpackMain();
// Calculate training accuracy.
CLI::GetParam<RandomForestModel>("output_model").rf.Classify(inputData,
CLI::GetParam<RandomForestModel*>("output_model")->rf.Classify(inputData,
predictions);
correct = arma::accu(predictions == labels);
double accuracy10 = (double(correct) / double(labels.n_elem) * 100);
// Train for minimium leaf size 1.
bindings::tests::CleanMemory();
// Train for minimum leaf size 1.
// Input training data.
SetInputParam("training", inputData);
@@ -268,7 +273,7 @@ BOOST_AUTO_TEST_CASE(RandomForestDiffMinLeafSizeTest)
mlpackMain();
// Calculate training accuracy.
CLI::GetParam<RandomForestModel>("output_model").rf.Classify(inputData,
CLI::GetParam<RandomForestModel*>("output_model")->rf.Classify(inputData,
predictions);
correct = arma::accu(predictions == labels);
@@ -308,8 +313,9 @@ BOOST_AUTO_TEST_CASE(RandomForestDiffNumTreeTest)
// Calculate training accuracy.
arma::Row<size_t> predictions;
CLI::GetParam<RandomForestModel>("output_model").rf.Classify(testData,
CLI::GetParam<RandomForestModel*>("output_model")->rf.Classify(testData,
predictions);
bindings::tests::CleanMemory();
size_t correct = arma::accu(predictions == testLabels);
double accuracy1 = (double(correct) / double(testLabels.n_elem) * 100);
@@ -324,8 +330,9 @@ BOOST_AUTO_TEST_CASE(RandomForestDiffNumTreeTest)
mlpackMain();
// Calculate training accuracy.
CLI::GetParam<RandomForestModel>("output_model").rf.Classify(testData,
CLI::GetParam<RandomForestModel*>("output_model")->rf.Classify(testData,
predictions);
bindings::tests::CleanMemory();
correct = arma::accu(predictions == testLabels);
double accuracy5 = (double(correct) / double(testLabels.n_elem) * 100);
@@ -340,7 +347,7 @@ BOOST_AUTO_TEST_CASE(RandomForestDiffNumTreeTest)
mlpackMain();
// Calculate training accuracy.
CLI::GetParam<RandomForestModel>("output_model").rf.Classify(testData,
CLI::GetParam<RandomForestModel*>("output_model")->rf.Classify(testData,
predictions);
correct = arma::accu(predictions == testLabels);
@@ -35,6 +35,7 @@ struct SoftmaxRegressionTestFixture
~SoftmaxRegressionTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
@@ -150,7 +151,7 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionModelReuseTest)
// Input trained model.
SetInputParam("test", std::move(testData));
SetInputParam("input_model",
std::move(CLI::GetParam<SoftmaxRegression>("output_model")));
CLI::GetParam<SoftmaxRegression*>("output_model"));
mlpackMain();
@@ -273,7 +274,7 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionTrainingVerTest)
// Input pre-trained model.
SetInputParam("input_model",
std::move(CLI::GetParam<SoftmaxRegression>("output_model")));
CLI::GetParam<SoftmaxRegression*>("output_model"));
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
@@ -318,7 +319,9 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffLambdaTest)
// Store output parameters.
arma::mat modelParam;
modelParam = CLI::GetParam<SoftmaxRegression>("output_model").Parameters();
modelParam = CLI::GetParam<SoftmaxRegression*>("output_model")->Parameters();
bindings::tests::CleanMemory();
// Reset passed parameters.
CLI::GetSingleton().Parameters()["training"].wasPassed = false;
@@ -340,13 +343,13 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffLambdaTest)
for (size_t i = 0; i < modelParam.n_elem; ++i)
{
BOOST_REQUIRE_NE(modelParam[i],
CLI::GetParam<SoftmaxRegression>("output_model").Parameters()[i]);
CLI::GetParam<SoftmaxRegression*>("output_model")->Parameters()[i]);
}
}
/**
* Check that output object parameters are different
* for different numbers of max_iterations.
* Check that output object parameters are different for different numbers of
* max_iterations.
*/
BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffMaxItrTest)
{
@@ -382,7 +385,9 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffMaxItrTest)
// Store output parameters.
arma::mat modelParam;
modelParam = CLI::GetParam<SoftmaxRegression>("output_model").Parameters();
modelParam = CLI::GetParam<SoftmaxRegression*>("output_model")->Parameters();
bindings::tests::CleanMemory();
// Reset passed parameters.
CLI::GetSingleton().Parameters()["training"].wasPassed = false;
@@ -404,7 +409,7 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffMaxItrTest)
for (size_t i = 0; i < modelParam.n_elem; ++i)
{
BOOST_REQUIRE_NE(modelParam[i],
CLI::GetParam<SoftmaxRegression>("output_model").Parameters()[i]);
CLI::GetParam<SoftmaxRegression*>("output_model")->Parameters()[i]);
}
}
@@ -446,7 +451,9 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffInterceptTest)
// Store output parameters.
arma::mat modelParam;
modelParam = CLI::GetParam<SoftmaxRegression>("output_model").Parameters();
modelParam = CLI::GetParam<SoftmaxRegression*>("output_model")->Parameters();
bindings::tests::CleanMemory();
// Reset passed parameters.
CLI::GetSingleton().Parameters()["training"].wasPassed = false;
@@ -466,7 +473,7 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffInterceptTest)
// Check that initial parameters has 1 more parameter than
// final parameters matrix.
BOOST_REQUIRE_EQUAL(
CLI::GetParam<SoftmaxRegression>("output_model").Parameters().n_cols,
CLI::GetParam<SoftmaxRegression*>("output_model")->Parameters().n_cols,
modelParam.n_cols + 1);
}
@@ -9,6 +9,10 @@
* 3-clause BSD license along with mlpack. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef MLPACK_TESTS_MAIN_TESTS_TEST_HELPER_HPP
#define MLPACK_TESTS_MAIN_TESTS_TEST_HELPER_HPP
#include <mlpack/prereqs.hpp>
namespace mlpack {
namespace util {
@@ -31,3 +35,5 @@ void SetInputParam(const std::string& name, T&& value)
} // namespace util
} // namespace mlpack
#endif