blob: 6db474fdb74d6032f4d81809716408043cbb2e5d [file] [log] [blame]
/* ----------------------------------------------------------------------- *//**
*
* @file dense_linear_systems.cpp
*
* @brief Dense linear systems
*
* We implement dense linear systems using the direct.
*
*//* ----------------------------------------------------------------------- */
#include <limits>
#include <dbconnector/dbconnector.hpp>
#include <modules/shared/HandleTraits.hpp>
#include <modules/prob/boost.hpp>
#include "dense_linear_systems.hpp"
// Common SQL functions used by all modules
#include "dense_linear_systems_states.hpp"
namespace madlib {
// Use Eigen
using namespace dbal::eigen_integration;
namespace modules {
// Import names from other MADlib modules
using dbal::NoSolutionFoundException;
namespace linear_systems{
// Residual Computation
// ---------------------------------------------------------------------------
typedef ResidualState<RootContainer> IResidualState;
typedef ResidualState<MutableRootContainer> MutableResidualState;
/**
* @brief Residual computation transition step
*/
AnyType dense_residual_norm_transition::run(AnyType& args){
MutableResidualState state = args[0].getAs<MutableByteString>();
MappedColumnVector _a = args[1].getAs<MappedColumnVector>();
double _b = args[2].getAs<double>();
MappedColumnVector x = args[3].getAs<MappedColumnVector>();
state.numRows++;
double x_a = dot(x,_a);
// Avoiding 2 norm for overflow reasons
state.residual_norm += std::abs(x_a - _b);
state.b_norm += std::abs(_b);
return state.storage();
}
/**
* @brief Perform the preliminary aggregation function: Merge transition states
*/
AnyType dense_residual_norm_merge_states::run(AnyType& args)
{
MutableResidualState state1 = args[0].getAs<MutableByteString>();
IResidualState state2 = args[1].getAs<ByteString>();
if (state1.numRows == 0)
return state2.storage();
else if (state2.numRows == 0)
return state1.storage();
state1.numRows += state2.numRows;
state1.residual_norm += state2.residual_norm;
state1.b_norm += state2.b_norm;
return state1.storage();
}
/**
* @brief Final step of the residual computations
*/
AnyType dense_residual_norm_final::run(AnyType& args)
{
IResidualState state = args[0].getAs<ByteString>();
if (state.numRows == 0)
return Null();
AnyType tuple;
// Return scaled norm
tuple << state.residual_norm / state.b_norm;
return tuple;
}
// ---------------------------------------------------------------------------
// Direct Dense Linear System States
// ---------------------------------------------------------------------------
// Function protofype of Internal functions
AnyType direct_dense_stateToResult(
const Allocator &inAllocator,
const ColumnVector &x);
/**
* @brief States for linear systems
*
* TransitionState encapsualtes the transition state during the
* logistic-regression aggregate function. To the database, the state is
* exposed as a single DOUBLE PRECISION array, to the C++ code it is a proper
* object containing scalars and vectors.
*
* Note: We assume that the DOUBLE PRECISION array is initialized by the
* database with length at least 5, and all elemenets are 0.
*
*/
template <class Handle>
class DenseDirectLinearSystemTransitionState {
template <class OtherHandle>
friend class DenseDirectLinearSystemTransitionState;
public:
DenseDirectLinearSystemTransitionState(const AnyType &inArray)
: mStorage(inArray.getAs<Handle>()) {
rebind( static_cast<uint32_t>(mStorage[0]),
static_cast<uint32_t>(mStorage[1]));
}
/**
* @brief Convert to backend representation
*
* We define this function so that we can use State in the
* argument list and as a return type.
*/
inline operator AnyType() const {
return mStorage;
}
/**
* @brief Initialize the conjugate-gradient state.
*
* This function is only called for the first iteration, for the first row.
*/
inline void initialize(const Allocator &inAllocator,
uint32_t inWidthOfA,
uint32_t inWidthOfb
) {
mStorage = inAllocator.allocateArray<double, dbal::AggregateContext,
dbal::DoZero, dbal::ThrowBadAlloc>(arraySize(inWidthOfA, inWidthOfb));
rebind(inWidthOfA, inWidthOfb);
widthOfA = inWidthOfA;
widthOfb = inWidthOfb;
}
/**
* @brief We need to support assigning the previous state
*/
template <class OtherHandle>
DenseDirectLinearSystemTransitionState &operator=(
const DenseDirectLinearSystemTransitionState<OtherHandle> &inOtherState) {
for (size_t i = 0; i < mStorage.size(); i++)
mStorage[i] = inOtherState.mStorage[i];
return *this;
}
/**
* @brief Merge with another State object by copying the intra-iteration
* fields
*/
template <class OtherHandle>
DenseDirectLinearSystemTransitionState &operator+=(
const DenseDirectLinearSystemTransitionState<OtherHandle> &inOtherState) {
if (mStorage.size() != inOtherState.mStorage.size() ||
widthOfA != inOtherState.widthOfA ||
widthOfb != inOtherState.widthOfb)
throw std::logic_error("Internal error: Incompatible transition "
"states");
numRows += inOtherState.numRows;
A += inOtherState.A;
b += inOtherState.b;
return *this;
}
/**
* @brief Reset the inter-iteration fields.
*/
inline void reset() {
numRows = 0;
algorithm = 0;
A.fill(0);
b.fill(0);
}
private:
static inline size_t arraySize(const uint32_t inWidthOfA,
const uint32_t inWidthOfb) {
return 4 + 1 * inWidthOfb * inWidthOfA + 1 * inWidthOfb;
}
/**
* @brief Rebind to a new storage array
*
* @param inWidthOfA The number of independent variables.
*
* Array layout (iteration refers to one aggregate-function call):
* Inter-iteration components (updated in final function):
* - 0: widthOfA (number of variables)
* - 1: widthOfb (number of equations)
* - 2: numRows
* - 3: algorithm
* - 4: b (RHS vector)
* - 4 + 1 * widthOfb: a (LHS matrix)
*/
void rebind(uint32_t inWidthOfA, uint32_t inWidthOfb) {
widthOfA.rebind(&mStorage[0]);
widthOfb.rebind(&mStorage[1]);
numRows.rebind(&mStorage[2]);
algorithm.rebind(&mStorage[3]);
b.rebind(&mStorage[4], inWidthOfb);
A.rebind(&mStorage[4 + 1 * inWidthOfb], inWidthOfb, inWidthOfA);
}
Handle mStorage;
public:
typename HandleTraits<Handle>::ReferenceToUInt32 widthOfA;
typename HandleTraits<Handle>::ReferenceToUInt32 widthOfb;
typename HandleTraits<Handle>::ReferenceToUInt32 numRows;
typename HandleTraits<Handle>::ReferenceToUInt32 algorithm;
typename HandleTraits<Handle>::ColumnVectorTransparentHandleMap b;
typename HandleTraits<Handle>::MatrixTransparentHandleMap A;
};
/**
* @brief Perform the dense linear system transition step
*/
AnyType
dense_direct_linear_system_transition::run(AnyType &args) {
DenseDirectLinearSystemTransitionState<MutableArrayHandle<double> >
state = args[0];
int32_t row_id = args[1].getAs<int32_t>();
MappedColumnVector _a = args[2].getAs<MappedColumnVector>();
double _b = args[3].getAs<double>();
int32_t num_equations = args[4].getAs<int32_t>();
// The following check was added with MADLIB-138.
if (!dbal::eigen_integration::isfinite(_a)) {
throw std::domain_error("Input matrix is not finite.");
return state;
}
if (state.numRows == 0) {
int algorithm = args[5].getAs<int>();
state.initialize(*this,
static_cast<uint32_t>(_a.size()),
num_equations
);
state.algorithm = algorithm;
}
state.numRows++;
// Now do the transition step
// First we create a block of zeros in memory
// and then add the vector in the appropriate position
ColumnVector bvec(static_cast<uint32_t>(state.widthOfb));
Matrix Amat(static_cast<uint32_t>(state.widthOfb),
static_cast<uint32_t>(state.widthOfA));
bvec.setZero();
Amat.setZero();
bvec(row_id) = _b;
Amat.row(row_id) = _a;
// Build the vector & matrices based on row_id
state.A += Amat;
state.b += bvec;
return state;
}
/**
* @brief Perform the preliminary aggregation function: Merge transition states
*/
AnyType
dense_direct_linear_system_merge_states::run(AnyType &args) {
DenseDirectLinearSystemTransitionState<MutableArrayHandle<double> > stateLeft = args[0];
DenseDirectLinearSystemTransitionState<ArrayHandle<double> > stateRight = args[1];
// We first handle the trivial case where this function is called with one
// of the states being the initial state
if (stateLeft.numRows == 0)
return stateRight;
else if (stateRight.numRows == 0)
return stateLeft;
// Merge states together and return
stateLeft += stateRight;
return stateLeft;
}
/**
* @brief Perform the linear system final step
*/
AnyType
dense_direct_linear_system_final::run(AnyType &args) {
// We request a mutable object. Depending on the backend, this might perform
// a deep copy.
DenseDirectLinearSystemTransitionState<MutableArrayHandle<double> > state = args[0];
// Aggregates that haven't seen any data just return Null.
if (state.numRows == 0)
return Null();
ColumnVector x;
switch (state.algorithm) {
case 1: x = (state.A).partialPivLu().solve(state.b);
break;
case 2: x = (state.A).fullPivLu().solve(state.b);
break;
case 3: x = (state.A).householderQr().solve(state.b);
break;
case 4: x = (state.A).colPivHouseholderQr().solve(state.b);
break;
case 5: x = (state.A).fullPivHouseholderQr().solve(state.b);
break;
case 6: x = (state.A).llt().solve(state.b);
break;
case 7: x = (state.A).ldlt().solve(state.b);
break;
}
// Compute the residual
return direct_dense_stateToResult(*this, x);
}
/**
* @brief Helper function that computes the final statistics for the robust variance
*/
AnyType direct_dense_stateToResult(
const Allocator &inAllocator,
const ColumnVector &x) {
MutableNativeColumnVector solution(
inAllocator.allocateArray<double>(x.size()));
for (Index i = 0; i < x.size(); ++i) {
solution(i) = x(i);
}
// Return all coefficients, standard errors, etc. in a tuple
AnyType tuple;
tuple << solution
<< Null()
<< Null();
return tuple;
}
} // namespace linear_systems
} // namespace modules
} // namespace madlib