#include "IpoptConfig.h"
#include "IpMa86SolverInterface.hpp"
#include <iostream>
#include <cmath>
#ifdef IPOPT_HAS_HSL
#include "CoinHslConfig.h"
#endif
using namespace std;
namespace Ipopt
{
static IPOPT_DECL_MA86_DEFAULT_CONTROL(*user_ma86_default_control) = NULL;
static IPOPT_DECL_MA86_ANALYSE(*user_ma86_analyse) = NULL;
static IPOPT_DECL_MA86_FACTOR(*user_ma86_factor) = NULL;
static IPOPT_DECL_MA86_FACTOR_SOLVE(*user_ma86_factor_solve) = NULL;
static IPOPT_DECL_MA86_SOLVE(*user_ma86_solve) = NULL;
static IPOPT_DECL_MA86_FINALISE(*user_ma86_finalise) = NULL;
static IPOPT_DECL_MC68_DEFAULT_CONTROL(*user_mc68_default_control) = NULL;
static IPOPT_DECL_MC68_ORDER(*user_mc68_order) = NULL;
Ma86SolverInterface::~Ma86SolverInterface()
{
delete[] val_;
delete[] order_;
if( keep_ )
{
ma86_finalise(&keep_, &control_);
}
}
void Ma86SolverInterface::RegisterOptions(
SmartPtr<RegisteredOptions> roptions
)
{
roptions->AddIntegerOption(
"ma86_print_level",
"Debug printing level",
-1,
"<0: no printing; 0: Error and warning messages only; 1: Limited diagnostic printing; >1 Additional diagnostic printing.");
roptions->AddLowerBoundedIntegerOption(
"ma86_nemin",
"Node Amalgamation parameter",
1,
32,
"Two nodes in elimination tree are merged if result has fewer than ma86_nemin variables.");
roptions->AddLowerBoundedNumberOption(
"ma86_small",
"Zero Pivot Threshold",
0.0, false,
1e-20,
"Any pivot less than ma86_small is treated as zero.");
roptions->AddLowerBoundedNumberOption(
"ma86_static",
"Static Pivoting Threshold",
0.0, false,
0.0,
"See MA86 documentation. "
"Either ma86_static=0.0 or ma86_static>ma86_small. "
"ma86_static=0.0 disables static pivoting.");
roptions->AddBoundedNumberOption(
"ma86_u",
"Pivoting Threshold",
0.0, false,
0.5, false,
1e-8,
"See MA86 documentation.");
roptions->AddBoundedNumberOption(
"ma86_umax",
"Maximum Pivoting Threshold",
0.0, false,
0.5, false,
1e-4,
"Maximum value to which u will be increased to improve quality.");
roptions->AddStringOption3(
"ma86_scaling",
"Controls scaling of matrix",
"mc64",
"none", "Do not scale the linear system matrix",
"mc64", "Scale linear system matrix using MC64",
"mc77", "Scale linear system matrix using MC77 [1,3,0]");
roptions->AddStringOption3(
"ma86_order",
"Controls type of ordering",
#ifdef COINHSL_HAS_METIS
"auto",
#else
"amd",
#endif
"auto", "Try both AMD and MeTiS, pick best",
"amd", "Use the HSL_MC68 approximate minimum degree algorithm",
"metis", "Use the MeTiS nested dissection algorithm (if available)");
}
void Ma86SolverInterface::SetFunctions(
IPOPT_DECL_MA86_DEFAULT_CONTROL(*ma86_default_control),
IPOPT_DECL_MA86_ANALYSE(*ma86_analyse),
IPOPT_DECL_MA86_FACTOR(*ma86_factor),
IPOPT_DECL_MA86_FACTOR_SOLVE(*ma86_factor_solve),
IPOPT_DECL_MA86_SOLVE(*ma86_solve),
IPOPT_DECL_MA86_FINALISE(*ma86_finalise),
IPOPT_DECL_MC68_DEFAULT_CONTROL(*mc68_default_control),
IPOPT_DECL_MC68_ORDER(*mc68_order)
)
{
DBG_ASSERT(ma86_default_control != NULL);
DBG_ASSERT(ma86_analyse != NULL);
DBG_ASSERT(ma86_factor != NULL);
DBG_ASSERT(ma86_factor_solve != NULL);
DBG_ASSERT(ma86_solve != NULL);
DBG_ASSERT(ma86_finalise != NULL);
DBG_ASSERT(mc68_default_control != NULL);
DBG_ASSERT(mc68_order != NULL);
user_ma86_default_control = ma86_default_control;
user_ma86_analyse = ma86_analyse;
user_ma86_factor = ma86_factor;
user_ma86_factor_solve = ma86_factor_solve;
user_ma86_solve = ma86_solve;
user_ma86_finalise = ma86_finalise;
user_mc68_default_control = mc68_default_control;
user_mc68_order = mc68_order;
}
bool Ma86SolverInterface::InitializeImpl(
const OptionsList& options,
const std::string& prefix
)
{
if( user_ma86_default_control != NULL )
{
ma86_default_control = user_ma86_default_control;
ma86_analyse = user_ma86_analyse;
ma86_factor = user_ma86_factor;
ma86_factor_solve = user_ma86_factor_solve;
ma86_solve = user_ma86_solve;
ma86_finalise = user_ma86_finalise;
mc68_default_control = user_mc68_default_control;
mc68_order = user_mc68_order;
}
else
{
#if (defined(COINHSL_HAS_MA86) && !defined(IPOPT_SINGLE)) || (defined(COINHSL_HAS_MA86S) && defined(IPOPT_SINGLE))
ma86_default_control = &::ma86_default_control;
ma86_analyse = &::ma86_analyse;
ma86_factor = &::ma86_factor;
ma86_factor_solve = &::ma86_factor_solve;
ma86_solve = &::ma86_solve;
ma86_finalise = &::ma86_finalise;
mc68_default_control = &::mc68_default_control;
mc68_order = &::mc68_order;
#else
DBG_ASSERT(IsValid(hslloader));
#define STR2(x) #x
#define STR(x) STR2(x)
ma86_default_control = (IPOPT_DECL_MA86_DEFAULT_CONTROL(*))hslloader->loadSymbol(STR(ma86_default_control));
ma86_analyse = (IPOPT_DECL_MA86_ANALYSE(*))hslloader->loadSymbol(STR(ma86_analyse));
ma86_factor = (IPOPT_DECL_MA86_FACTOR(*))hslloader->loadSymbol(STR(ma86_factor));
ma86_factor_solve = (IPOPT_DECL_MA86_FACTOR_SOLVE(*))hslloader->loadSymbol(STR(ma86_factor_solve));
ma86_solve = (IPOPT_DECL_MA86_SOLVE(*))hslloader->loadSymbol(STR(ma86_solve));
ma86_finalise = (IPOPT_DECL_MA86_FINALISE(*))hslloader->loadSymbol(STR(ma86_finalise));
mc68_default_control = (IPOPT_DECL_MC68_DEFAULT_CONTROL(*))hslloader->loadSymbol(STR(mc68_default_control));
mc68_order = (IPOPT_DECL_MC68_ORDER(*))hslloader->loadSymbol(STR(mc68_order));
#endif
}
DBG_ASSERT(ma86_default_control != NULL);
DBG_ASSERT(ma86_analyse != NULL);
DBG_ASSERT(ma86_factor != NULL);
DBG_ASSERT(ma86_factor_solve != NULL);
DBG_ASSERT(ma86_solve != NULL);
DBG_ASSERT(ma86_finalise != NULL);
DBG_ASSERT(mc68_default_control != NULL);
DBG_ASSERT(mc68_order != NULL);
ma86_default_control(&control_);
control_.f_arrays = 1;
Index temp;
options.GetIntegerValue("ma86_print_level", temp, prefix);
control_.diagnostics_level = temp;
options.GetIntegerValue("ma86_nemin", temp, prefix);
control_.nemin = temp;
options.GetNumericValue("ma86_small", control_.small_, prefix);
options.GetNumericValue("ma86_static", control_.static_, prefix);
options.GetNumericValue("ma86_u", control_.u, prefix);
options.GetNumericValue("ma86_umax", umax_, prefix);
std::string order_method, scaling_method;
options.GetStringValue("ma86_order", order_method, prefix);
if( order_method == "metis" )
{
ordering_ = ORDER_METIS;
}
else if( order_method == "amd" )
{
ordering_ = ORDER_AMD;
}
else
{
ordering_ = ORDER_AUTO;
}
options.GetStringValue("ma86_scaling", scaling_method, prefix);
if( scaling_method == "mc64" )
{
control_.scaling = 1;
}
else if( scaling_method == "mc77" )
{
control_.scaling = 2;
}
else
{
control_.scaling = 0;
}
return true; }
ESymSolverStatus Ma86SolverInterface::InitializeStructure(
Index dim,
Index nonzeros,
const Index* ia,
const Index* ja
)
{
struct ma86_info info, info2;
struct mc68_control control68;
struct mc68_info info68;
int* order_amd;
int* order_metis;
void* keep_amd;
void* keep_metis;
ndim_ = dim;
if( HaveIpData() )
{
IpData().TimingStats().LinearSystemSymbolicFactorization().Start();
}
mc68_default_control(&control68);
control68.f_array_in = 1; control68.f_array_out = 1; order_amd = NULL;
order_metis = NULL;
DBG_ASSERT(ordering_ == ORDER_METIS || ordering_ == ORDER_AMD || ordering_ == ORDER_AUTO);
if( ordering_ == ORDER_METIS || ordering_ == ORDER_AUTO )
{
order_metis = new int[dim];
mc68_order(3, dim, ia, ja, order_metis, &control68, &info68);
if( info68.flag == -5 )
{
ordering_ = ORDER_AMD;
delete[] order_metis;
order_metis = NULL;
}
else if( info68.flag < 0 )
{
return SYMSOLVER_FATAL_ERROR;
}
}
if( ordering_ == ORDER_AMD || ordering_ == ORDER_AUTO )
{
order_amd = new int[dim];
mc68_order(1, dim, ia, ja, order_amd, &control68, &info68);
}
if( info68.flag < 0 )
{
return SYMSOLVER_FATAL_ERROR;
}
if( ordering_ == ORDER_AUTO )
{
ma86_analyse(dim, ia, ja, order_amd, &keep_amd, &control_, &info2);
if( info2.flag < 0 )
{
return SYMSOLVER_FATAL_ERROR;
}
ma86_analyse(dim, ia, ja, order_metis, &keep_metis, &control_, &info);
if( info.flag < 0 )
{
return SYMSOLVER_FATAL_ERROR;
}
if( info.num_flops > info2.num_flops )
{
order_ = order_amd;
keep_ = keep_amd;
delete[] order_metis;
ma86_finalise(&keep_metis, &control_);
}
else
{
order_ = order_metis;
keep_ = keep_metis;
delete[] order_amd;
ma86_finalise(&keep_amd, &control_);
}
}
else
{
if( ordering_ == ORDER_AMD )
{
order_ = order_amd;
}
if( ordering_ == ORDER_METIS )
{
order_ = order_metis;
}
ma86_analyse(dim, ia, ja, order_, &keep_, &control_, &info);
}
if( HaveIpData() )
{
IpData().TimingStats().LinearSystemSymbolicFactorization().End();
}
if( val_ != NULL )
{
delete[] val_;
}
val_ = new Number[nonzeros];
if( info.flag >= 0 )
{
return SYMSOLVER_SUCCESS;
}
else
{
return SYMSOLVER_FATAL_ERROR;
}
}
ESymSolverStatus Ma86SolverInterface::MultiSolve(
bool new_matrix,
const Index* ia,
const Index* ja,
Index nrhs,
Number* rhs_vals,
bool check_NegEVals,
Index numberOfNegEVals)
{
struct ma86_info info;
if( new_matrix || pivtol_changed_ )
{
if( HaveIpData() )
{
IpData().TimingStats().LinearSystemFactorization().Start();
}
ma86_factor_solve(ndim_, ia, ja, val_, order_, &keep_, &control_, &info, nrhs, ndim_, rhs_vals, NULL);
if( HaveIpData() )
{
IpData().TimingStats().LinearSystemFactorization().End();
}
if( info.flag < 0 )
{
return SYMSOLVER_FATAL_ERROR;
}
if( info.flag == 2 )
{
return SYMSOLVER_SINGULAR;
}
if( check_NegEVals && info.num_neg != numberOfNegEVals )
{
return SYMSOLVER_WRONG_INERTIA;
}
numneg_ = info.num_neg;
pivtol_changed_ = false;
}
else
{
if( HaveIpData() )
{
IpData().TimingStats().LinearSystemBackSolve().Start();
}
ma86_solve(0, nrhs, ndim_, rhs_vals, order_, &keep_, &control_, &info,
NULL);
if( HaveIpData() )
{
IpData().TimingStats().LinearSystemBackSolve().End();
}
}
return SYMSOLVER_SUCCESS;
}
bool Ma86SolverInterface::IncreaseQuality()
{
if( control_.u >= umax_ )
{
return false;
}
pivtol_changed_ = true;
Jnlst().Printf(J_DETAILED, J_LINEAR_ALGEBRA,
"Increasing pivot tolerance for HSL_MA86 from %7.2e ", control_.u);
control_.u = Min(umax_, std::pow(control_.u, Number(0.75)));
Jnlst().Printf(J_DETAILED, J_LINEAR_ALGEBRA,
"to %7.2e.\n", control_.u);
return true;
}
}