Files
armadillo-code/include/armadillo_bits/spsolve_factoriser_meat.hpp
T

290 lines
6.8 KiB
C++

// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2008-2016 Conrad Sanderson (http://conradsanderson.id.au)
// Copyright 2008-2016 National ICT Australia (NICTA)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
//! \addtogroup spsolve_factoriser
//! @{
template<typename worker_type>
inline
void
spsolve_factoriser::delete_worker()
{
arma_debug_sigprint();
if(worker_ptr != nullptr)
{
worker_type* ptr = reinterpret_cast<worker_type*>(worker_ptr);
delete ptr;
worker_ptr = nullptr;
}
}
inline
void
spsolve_factoriser::cleanup()
{
arma_debug_sigprint();
#if defined(ARMA_USE_SUPERLU)
{
if(elem_type_indicator == 1) { delete_worker< superlu_worker< float> >(); }
else if(elem_type_indicator == 2) { delete_worker< superlu_worker< double> >(); }
else if(elem_type_indicator == 3) { delete_worker< superlu_worker< cx_float> >(); }
else if(elem_type_indicator == 4) { delete_worker< superlu_worker<cx_double> >(); }
}
#endif
worker_ptr = nullptr;
elem_type_indicator = 0;
n_rows = 0;
rcond_value = double(0);
}
inline
spsolve_factoriser::~spsolve_factoriser()
{
arma_debug_sigprint_this(this);
cleanup();
}
inline
spsolve_factoriser::spsolve_factoriser()
{
arma_debug_sigprint_this(this);
}
inline
void
spsolve_factoriser::reset()
{
arma_debug_sigprint();
cleanup();
}
inline
double
spsolve_factoriser::rcond() const
{
arma_debug_sigprint();
return rcond_value;
}
template<typename T1>
inline
bool
spsolve_factoriser::factorise
(
const SpBase<typename T1::elem_type,T1>& A_expr,
const spsolve_opts_base& settings,
const typename arma_blas_type_only<typename T1::elem_type>::result* junk
)
{
arma_debug_sigprint();
arma_ignore(junk);
#if defined(ARMA_USE_SUPERLU)
{
typedef typename T1::elem_type eT;
typedef typename get_pod_type<eT>::result T;
typedef superlu_worker<eT> worker_type;
//
cleanup();
//
const unwrap_spmat<T1> U(A_expr.get_ref());
const SpMat<eT>& A = U.M;
if(A.is_square() == false)
{
arma_warn(1, "spsolve_factoriser::factorise(): solving under-determined / over-determined systems is currently not supported");
return false;
}
n_rows = A.n_rows;
//
superlu_opts superlu_opts_default;
const superlu_opts& opts = (settings.id == 1) ? static_cast<const superlu_opts&>(settings) : superlu_opts_default;
if( (opts.pivot_thresh < double(0)) || (opts.pivot_thresh > double(1)) )
{
arma_warn(1, "spsolve_factoriser::factorise(): pivot_thresh must be in the [0,1] interval" );
return false;
}
//
worker_ptr = new(std::nothrow) worker_type;
if(worker_ptr == nullptr)
{
arma_warn(3, "spsolve_factoriser::factorise(): could not construct worker object");
return false;
}
//
if( is_float<eT>::value) { elem_type_indicator = 1; }
else if( is_double<eT>::value) { elem_type_indicator = 2; }
else if( is_cx_float<eT>::value) { elem_type_indicator = 3; }
else if(is_cx_double<eT>::value) { elem_type_indicator = 4; }
//
worker_type* local_worker_ptr = reinterpret_cast<worker_type*>(worker_ptr);
worker_type& local_worker_ref = (*local_worker_ptr);
//
T local_rcond_value = T(0);
const bool status = local_worker_ref.factorise(local_rcond_value, A, opts);
rcond_value = double(local_rcond_value);
if( (status == false) || arma_isnan(local_rcond_value) || ((opts.allow_ugly == false) && (local_rcond_value < std::numeric_limits<T>::epsilon())) )
{
arma_warn(3, "spsolve_factoriser::factorise(): factorisation failed; rcond: ", local_rcond_value);
delete_worker<worker_type>();
return false;
}
return true;
}
#else
{
arma_ignore(A_expr);
arma_ignore(settings);
arma_stop_logic_error("spsolve_factoriser::factorise(): use of SuperLU must be enabled");
return false;
}
#endif
}
template<typename T1>
inline
bool
spsolve_factoriser::solve
(
Mat<typename T1::elem_type>& X,
const Base<typename T1::elem_type,T1>& B_expr,
const typename arma_blas_type_only<typename T1::elem_type>::result* junk
)
{
arma_debug_sigprint();
arma_ignore(junk);
#if defined(ARMA_USE_SUPERLU)
{
typedef typename T1::elem_type eT;
typedef superlu_worker<eT> worker_type;
if(worker_ptr == nullptr)
{
arma_warn(2, "spsolve_factoriser::solve(): no factorisation available");
X.soft_reset();
return false;
}
bool type_mismatch = false;
if( (is_float<eT>::value) && (elem_type_indicator != 1) ) { type_mismatch = true; }
else if( (is_double<eT>::value) && (elem_type_indicator != 2) ) { type_mismatch = true; }
else if( (is_cx_float<eT>::value) && (elem_type_indicator != 3) ) { type_mismatch = true; }
else if((is_cx_double<eT>::value) && (elem_type_indicator != 4) ) { type_mismatch = true; }
if(type_mismatch)
{
arma_warn(1, "spsolve_factoriser::solve(): matrix type mismatch");
X.soft_reset();
return false;
}
const quasi_unwrap<T1> U(B_expr.get_ref());
const Mat<eT>& B = U.M;
if(n_rows != B.n_rows)
{
arma_warn(1, "spsolve_factoriser::solve(): matrix size mismatch");
X.soft_reset();
return false;
}
const bool is_alias = U.is_alias(X);
Mat<eT> tmp;
Mat<eT>& out = is_alias ? tmp : X;
worker_type* local_worker_ptr = reinterpret_cast<worker_type*>(worker_ptr);
worker_type& local_worker_ref = (*local_worker_ptr);
const bool status = local_worker_ref.solve(out,B);
if(is_alias) { X.steal_mem(tmp); }
if(status == false)
{
arma_warn(3, "spsolve_factoriser::solve(): solution not found");
X.soft_reset();
return false;
}
return true;
}
#else
{
arma_ignore(X);
arma_ignore(B_expr);
arma_stop_logic_error("spsolve_factoriser::solve(): use of SuperLU must be enabled");
return false;
}
#endif
}
//! @}