Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sparse solver on GPU #600

Merged
merged 10 commits into from
Mar 14, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
add sparse raja example
nychiang committed Mar 13, 2023
commit 9cc25bd7511983f3fab3f1ef46277e0d80a8c4db
476 changes: 476 additions & 0 deletions src/Drivers/Sparse/NlpSparseRajaEx2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,476 @@
// Copyright (c) 2017, Lawrence Livermore National Security, LLC.
// Produced at the Lawrence Livermore National Laboratory (LLNL).
// LLNL-CODE-742473. All rights reserved.
//
// This file is part of HiOp. For details, see https://github.com/LLNL/hiop. HiOp
// is released under the BSD 3-clause license (https://opensource.org/licenses/BSD-3-Clause).
// Please also read "Additional BSD Notice" below.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// i. Redistributions of source code must retain the above copyright notice, this list
// of conditions and the disclaimer below.
// ii. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the disclaimer (as noted below) in the documentation and/or
// other materials provided with the distribution.
// iii. Neither the name of the LLNS/LLNL nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific prior written
// permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
// SHALL LAWRENCE LIVERMORE NATIONAL SECURITY, LLC, THE U.S. DEPARTMENT OF ENERGY OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Additional BSD Notice
// 1. This notice is required to be provided under our contract with the U.S. Department
// of Energy (DOE). This work was produced at Lawrence Livermore National Laboratory under
// Contract No. DE-AC52-07NA27344 with the DOE.
// 2. Neither the United States Government nor Lawrence Livermore National Security, LLC
// nor any of their employees, makes any warranty, express or implied, or assumes any
// liability or responsibility for the accuracy, completeness, or usefulness of any
// information, apparatus, product, or process disclosed, or represents that its use would
// not infringe privately-owned rights.
// 3. Also, reference herein to any specific commercial products, process, or services by
// trade name, trademark, manufacturer or otherwise does not necessarily constitute or
// imply its endorsement, recommendation, or favoring by the United States Government or
// Lawrence Livermore National Security, LLC. The views and opinions of authors expressed
// herein do not necessarily state or reflect those of the United States Government or
// Lawrence Livermore National Security, LLC, and shall not be used for advertising or
// product endorsement purposes.

/**
* @file NlpSparseRajaEx2.cpp
*
* @author Nai-Yuan Chiang <chiang7@lnnl.gov>, LNNL
*
*/

#include "SparseRajaEx2.hpp"

#include <umpire/Allocator.hpp>
#include <umpire/ResourceManager.hpp>
#include <RAJA/RAJA.hpp>

#include <hiopMatrixRajaSparseTriplet.hpp>

#include <cmath>
#include <cstring> //for memcpy
#include <cstdio>


//TODO: A good idea to not use the internal HiOp Raja policies here and, instead, give self-containing
// definitions of the policies here so that the user gets a better grasp of the concept and does not
// rely on the internals of HiOp. For example:
// #define RAJA_LAMBDA [=] __device__
// using ex2_raja_exec = RAJA::cuda_exec<128>;
// more defs here


#if defined(HIOP_USE_CUDA)
#include "ExecPoliciesRajaCudaImpl.hpp"
using ex2_raja_exec = hiop::ExecRajaPoliciesBackend<hiop::ExecPolicyRajaCuda>::hiop_raja_exec;
using ex2_raja_reduce = hiop::ExecRajaPoliciesBackend<hiop::ExecPolicyRajaCuda>::hiop_raja_reduce;
using hiopMatrixRajaSparse = hiop::hiopMatrixRajaSparseTriplet<hiop::MemBackendUmpire, hiop::ExecPolicyRajaCuda>;
#elif defined(HIOP_USE_HIP)
#include <ExecPoliciesRajaHipImpl.hpp>
using ex2_raja_exec = hiop::ExecRajaPoliciesBackend<hiop::ExecPolicyRajaHip>::hiop_raja_exec;
using ex2_raja_reduce = hiop::ExecRajaPoliciesBackend<hiop::ExecPolicyRajaHip>::hiop_raja_reduce;
using hiopMatrixRajaSparse = hiop::hiopMatrixRajaSparseTriplet<hiop::MemBackendUmpire, hiop::ExecPolicyRajaHip>;
#else
//#if !defined(HIOP_USE_CUDA) && !defined(HIOP_USE_HIP)
#include <ExecPoliciesRajaOmpImpl.hpp>
using ex2_raja_exec = hiop::ExecRajaPoliciesBackend<hiop::ExecPolicyRajaOmp>::hiop_raja_exec;
using ex2_raja_reduce = hiop::ExecRajaPoliciesBackend<hiop::ExecPolicyRajaOmp>::hiop_raja_reduce;
using hiopMatrixRajaSparse = hiop::hiopMatrixRajaSparseTriplet<hiop::MemBackendUmpire, hiop::ExecPolicyRajaOmp>;
#endif


/** Nonlinear *highly nonconvex* and *rank deficient* problem test for the Filter IPM
* Newton of HiOp. It uses a Sparse NLP formulation. The problem is based on SparseEx1.
*
* min (2*convex_obj-1)*scal*sum 1/4* { (x_{i}-1)^4 : i=1,...,n} + 0.5x^Tx
* s.t.
* 4*x_1 + 2*x_2 == 10
* 5<= 2*x_1 + x_3
* 1<= 2*x_1 + 0.5*x_i <= 2*n, for i=4,...,n
* x_1 free
* 0.0 <= x_2
* 1.0 <= x_3 <= 10
* x_i >=0.5, i=4,...,n
*
* Optionally, one can add the following constraints to obtain a rank-deficient Jacobian
*
* s.t. [-inf] <= 4*x_1 + 2*x_3 <= [ 19 ] (rnkdef-con1)
* 4*x_1 + 2*x_2 == 10 (rnkdef-con2)
*
* other parameters are:
* convex_obj: set to 1 to have a convex problem, otherwise set it to 0.
* scale_quartic_obj_term: scaling factor for the quartic term in the objective (1.0 by default).
*
*/
SparseRajaEx2::SparseRajaEx2(std::string mem_space,
int n,
bool convex_obj,
bool rankdefic_Jac_eq,
bool rankdefic_Jac_ineq,
double scal_neg_obj)
: mem_space_{mem_space},
convex_obj_{convex_obj},
rankdefic_eq_{rankdefic_Jac_eq},
rankdefic_ineq_{rankdefic_Jac_ineq},
n_vars_{n},
scal_neg_obj_{scal_neg_obj},
n_cons_{2}
{
// Make sure mem_space_ is uppercase
transform(mem_space_.begin(), mem_space_.end(), mem_space_.begin(), ::toupper);

assert(n>=3 && "number of variables should be greater than 3 for this example");
if(n>3) {
n_cons_ += n-3;
}
n_cons_ += rankdefic_eq_ + rankdefic_ineq_;
}

SparseRajaEx2::~SparseRajaEx2()
{
}

bool SparseRajaEx2::get_prob_sizes(size_type& n, size_type& m)
{
n = n_vars_;
m = n_cons_;
return true;
}

bool SparseRajaEx2::get_vars_info(const size_type& n, double *xlow, double* xupp, NonlinearityType* type)
{
assert(n==n_vars_);

RAJA::forall<ex1_raja_exec>(RAJA::RangeSegment(0, 3),
RAJA_LAMBDA(RAJA::Index_type i)
{
if(i==0) {
xlow[i] = -1e20;
xupp[i] = 1e20;
} else if(i==1) {
xlow[i] = 0.0;
xupp[i] = 1e20;
} else {
xlow[i] = 1.0;
xupp[i] = 10.0;
}
});

if(n>3) {
RAJA::forall<ex1_raja_exec>(RAJA::RangeSegment(3, n),
RAJA_LAMBDA(RAJA::Index_type i)
{
xlow[i] = 0.5;
xupp[i] = 1e20;
});
}

RAJA::forall<ex1_raja_exec>(RAJA::RangeSegment(0, n),
RAJA_LAMBDA(RAJA::Index_type i)
{
type[i] = hiopNonlinear;
});
return true;
}

bool SparseRajaEx2::get_cons_info(const size_type& m, double* clow, double* cupp, NonlinearityType* type)
{
assert(m==n_cons_);

RAJA::forall<ex1_raja_exec>(RAJA::RangeSegment(0, 2),
RAJA_LAMBDA(RAJA::Index_type i)
{
if(i==0) {
clow[i] = 10.0;
cupp[i] = 10.0;
} else {
clow[i] = 5.0;
cupp[i] = 1e20;
}
});

if(n>3) {
RAJA::forall<ex1_raja_exec>(RAJA::RangeSegment(3, m),
RAJA_LAMBDA(RAJA::Index_type conidx)
{
clow[conidx] = 1.0;
cupp[conidx] = 2*n;
});
}

if(rankdefic_ineq_) {
RAJA::forall<ex1_raja_exec>(RAJA::RangeSegment(m, m+1),
RAJA_LAMBDA(RAJA::Index_type conidx)
{
// [-inf] <= 4*x_1 + 2*x_3 <= [ 19 ]
clow[conidx] = -1e+20;
cupp[conidx] = 19.;
});
}

if(rankdefic_eq_) {
RAJA::forall<ex1_raja_exec>(RAJA::RangeSegment(m+rankdefic_ineq_, m+rankdefic_ineq_+1),
RAJA_LAMBDA(RAJA::Index_type conidx)
{
// 4*x_1 + 2*x_2 == 10
clow[conidx] = 10;
cupp[conidx] = 10;
});
}

RAJA::forall<ex1_raja_exec>(RAJA::RangeSegment(0, m),
RAJA_LAMBDA(RAJA::Index_type i)
{
type[i] = hiopInterfaceBase::hiopNonlinear;
});

return true;
}

bool SparseRajaEx2::get_sparse_blocks_info(int& nx,
int& nnz_sparse_Jaceq,
int& nnz_sparse_Jacineq,
int& nnz_sparse_Hess_Lagr)
{
nx = n_vars_;;
nnz_sparse_Jaceq = 2 + 2*rankdefic_eq_;
nnz_sparse_Jacineq = 2 + 2*(n_vars_-3) + 2*rankdefic_ineq_;
nnz_sparse_Hess_Lagr = n_vars_;
return true;
}

bool SparseRajaEx2::eval_f(const size_type& n, const double* x, bool new_x, double& obj_value)
{
assert(n==n_vars_);
obj_value=0.;
{
int convex_obj = (int) convex_obj_;
double scal_neg_obj = scal_neg_obj_;

RAJA::ReduceSum<ex2_raja_reduce, double> aux(0);
RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(0, n),
RAJA_LAMBDA(RAJA::Index_type i)
{
aux += (2*convex_obj-1) * scal_neg_obj * 0.25 * std::pow(x[i]-1., 4) + 0.5 * std::pow(x[i], 2);
});
obj_value += aux.get();
}
return true;
}

bool SparseRajaEx2::eval_grad_f(const size_type& n, const double* x, bool new_x, double* gradf)
{
assert(n==n_vars_);
{
int convex_obj = (int) convex_obj_;
double scal_neg_obj = scal_neg_obj_;

RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(0, n),
RAJA_LAMBDA(RAJA::Index_type i)
{
gradf[i] = (2*convex_obj-1) * scal_neg_obj * std::pow(x[i]-1.,3) + x[i];
});
}
return true;
}

bool SparseRajaEx2::eval_cons(const size_type& n, const size_type& m, const double* x, bool new_x, double* cons)
{
assert(n==n_vars_); assert(m==n_cons_);
assert(n_cons_==2+n-3+rankdefic_eq_+rankdefic_ineq_);

RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(0, 2),
RAJA_LAMBDA(RAJA::Index_type i)
{
if(i==0) {
// --- constraint 1 body ---> 4*x_1 + 2*x_2 == 10
cons[i] = 4*x[0] + 2*x[1];
} else if(i==1) {
// --- constraint 2 body ---> 2*x_1 + x_3
cons[i] = 2*x[0] + 1*x[2];
}
});

RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(2, n-1),
RAJA_LAMBDA(RAJA::Index_type i)
{
// --- constraint 3 body ---> 2*x_1 + 0.5*x_i, for i>=4
cons[i] = 2*x[0] + 0.5*x[i+1];
});


if(rankdefic_ineq_) {
RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(n-1, n),
RAJA_LAMBDA(RAJA::Index_type i)
{
// [-inf] <= 4*x_1 + 2*x_3 <= [ 19 ]
cons[i] = 4*x[0] + 2*x[2];
});
}

if(rankdefic_eq_) {
RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(n-1+rankdefic_ineq_, n+rankdefic_ineq_),
RAJA_LAMBDA(RAJA::Index_type i)
{
// 4*x_1 + 2*x_2 == 10
cons[i] = 4*x[0] + 2*x[1];
});
}
assert(conidx==m);

return true;
}

bool SparseRajaEx2::eval_Jac_cons(const size_type& n,
const size_type& m,
const double* x,
bool new_x,
const int& nnzJacS,
index_type* iJacS,
index_type* jJacS,
double* MJacS)
{
assert(n==n_vars_); assert(m==n_cons_);
assert(n>=3);

assert(nnzJacS == 4 + 2*(n-3) + 2*rankdefic_eq_ + 2*rankdefic_ineq_);

RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(0, 2),
RAJA_LAMBDA(RAJA::Index_type itrow)
{
if(itrow==0) {
// --- constraint 1 body ---> 4*x_1 + 2*x_2 == 10
iJacS[2*itrow] = itrow;
jJacS[2*itrow] = 0;
iJacS[2*itrow+1] = itrow;
jJacS[2*itrow+1] = 1;
if(MJacS != nullptr) {
MJacS[2*itrow] = 4.0;
MJacS[2*itrow+1] = 2.0;
}
} else if(itrow==1) {
// --- constraint 2 body ---> 2*x_1 + x_3
iJacS[2*itrow] = itrow;
jJacS[2*itrow] = 0;
iJacS[2*itrow+1] = itrow;
jJacS[2*itrow+1] = 2;
if(MJacS != nullptr) {
MJacS[2*itrow] = 2.0;
MJacS[2*itrow+1] = 1.0;
}
}
});

RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(2, n-1),
RAJA_LAMBDA(RAJA::Index_type itrow)
{
// --- constraint 3 body ---> 2*x_1 + 0.5*x_i, for i>=4
iJacS[2*itrow] = itrow;
jJacS[2*itrow] = 0;
iJacS[2*itrow+1] = itrow;
jJacS[2*itrow+1] = itrow+1;
if(MJacS != nullptr) {
MJacS[2*itrow] = 2.0;
MJacS[2*itrow+1] = 0.5;
}
});


if(rankdefic_ineq_) {
RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(n-1, n),
RAJA_LAMBDA(RAJA::Index_type itrow)
{
// [-inf] <= 4*x_1 + 2*x_3 <= [ 19 ]
iJacS[2*itrow] = itrow;
jJacS[2*itrow] = 0;
iJacS[2*itrow+1] = itrow;
jJacS[2*itrow+1] = 2;
if(MJacS != nullptr) {
MJacS[2*itrow] = 4.0;
MJacS[2*itrow+1] = 2.0;
}
});
}

if(rankdefic_eq_) {
RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(n-1+rankdefic_ineq_, n+rankdefic_ineq_),
RAJA_LAMBDA(RAJA::Index_type i)
{
// 4*x_1 + 2*x_2 == 10
iJacS[2*itrow] = itrow;
jJacS[2*itrow] = 0;
iJacS[2*itrow+1] = itrow;
jJacS[2*itrow+1] = 1;
if(MJacS != nullptr) {
MJacS[2*itrow] = 4.0;
MJacS[2*itrow+1] = 2.0;
}
});
}

return true;
}

bool SparseRajaEx2::eval_Hess_Lagr(const size_type& n,
const size_type& m,
const double* x,
bool new_x,
const double& obj_factor,
const double* lambda,
bool new_lambda,
const size_type& nnzHSS,
index_type* iHSS,
index_type* jHSS,
double* MHSS)
{
//Note: lambda is not used since all the constraints are linear and, therefore, do
//not contribute to the Hessian of the Lagrangian
assert(nnzHSS == n);

if(iHSS!=NULL && jHSS!=NULL) {
RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(0, n),
RAJA_LAMBDA(RAJA::Index_type i)
{
iHSS[i] = i;
jHSS[i] = i;
});
}

int convex_obj = (int) convex_obj_;
double scal_neg_obj = scal_neg_obj_;
if(MHSS!=NULL) {
RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(0, n),
RAJA_LAMBDA(RAJA::Index_type i)
{
MHSS[i] = obj_factor * ( (2*convex_obj-1) * scal_neg_obj * 3 * std::pow(x[i]-1., 2) + 1);
});
}
return true;
}




bool SparseRajaEx2::get_starting_point(const size_type& n, double* x0)
{
assert(n==n_vars_);
RAJA::forall<ex2_raja_exec>(RAJA::RangeSegment(0, n),
RAJA_LAMBDA(RAJA::Index_type i)
{
x0[i] = 0.0;
});
return true;
}
309 changes: 309 additions & 0 deletions src/Drivers/Sparse/NlpSparseRajaEx2.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
// Copyright (c) 2017, Lawrence Livermore National Security, LLC.
// Produced at the Lawrence Livermore National Laboratory (LLNL).
// LLNL-CODE-742473. All rights reserved.
//
// This file is part of HiOp. For details, see https://github.com/LLNL/hiop. HiOp
// is released under the BSD 3-clause license (https://opensource.org/licenses/BSD-3-Clause).
// Please also read "Additional BSD Notice" below.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// i. Redistributions of source code must retain the above copyright notice, this list
// of conditions and the disclaimer below.
// ii. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the disclaimer (as noted below) in the documentation and/or
// other materials provided with the distribution.
// iii. Neither the name of the LLNS/LLNL nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific prior written
// permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
// SHALL LAWRENCE LIVERMORE NATIONAL SECURITY, LLC, THE U.S. DEPARTMENT OF ENERGY OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Additional BSD Notice
// 1. This notice is required to be provided under our contract with the U.S. Department
// of Energy (DOE). This work was produced at Lawrence Livermore National Laboratory under
// Contract No. DE-AC52-07NA27344 with the DOE.
// 2. Neither the United States Government nor Lawrence Livermore National Security, LLC
// nor any of their employees, makes any warranty, express or implied, or assumes any
// liability or responsibility for the accuracy, completeness, or usefulness of any
// information, apparatus, product, or process disclosed, or represents that its use would
// not infringe privately-owned rights.
// 3. Also, reference herein to any specific commercial products, process, or services by
// trade name, trademark, manufacturer or otherwise does not necessarily constitute or
// imply its endorsement, recommendation, or favoring by the United States Government or
// Lawrence Livermore National Security, LLC. The views and opinions of authors expressed
// herein do not necessarily state or reflect those of the United States Government or
// Lawrence Livermore National Security, LLC, and shall not be used for advertising or
// product endorsement purposes.

/**
* @file NlpSparseRajaEx2.hpp
*
* @author Nai-Yuan Chiang <chiang7@lnnl.gov>, LNNL
*
*/

#ifndef HIOP_EXAMPLE_SPARSE_RAJA_EX2
#define HIOP_EXAMPLE_SPARSE_RAJA_EX2

#include "hiopInterface.hpp"
#include "LinAlgFactory.hpp"

#ifdef HIOP_USE_MPI
#include "mpi.h"
#else
#define MPI_COMM_WORLD 0
#define MPI_Comm int
#endif

#include <cassert>
#include <cstring> //for memcpy
#include <cstdio>
#include <cmath>

using size_type = hiop::size_type;
using index_type = hiop::index_type;

/** Nonlinear *highly nonconvex* and *rank deficient* problem test for the Filter IPM
* Newton of HiOp. It uses a Sparse NLP formulation. The problem is based on SparseEx2.
*
* min (2*convex_obj-1)*scal*sum 1/4* { (x_{i}-1)^4 : i=1,...,n} + 0.5x^Tx
* s.t.
* 4*x_1 + 2*x_2 == 10
* 5<= 2*x_1 + x_3
* 1<= 2*x_1 + 0.5*x_i <= 2*n, for i=4,...,n
* x_1 free
* 0.0 <= x_2
* 1.0 <= x_3 <= 10
* x_i >=0.5, i=4,...,n
*
* Optionally, one can add the following constraints to obtain a rank-deficient Jacobian
*
* s.t. [-inf] <= 4*x_1 + 2*x_3 <= [ 19 ] (rnkdef-con1 --- ineq con)
* 4*x_1 + 2*x_2 == 10 (rnkdef-con2 --- eq con)
*
* other parameters are:
* convex_obj: set to 1 to have a convex problem, otherwise set it to 0
* scale_quartic_obj_term: scaling factor for the quartic term in the objective (1.0 by default).
*
* @note All pointers marked as "managed by Umpire" are allocated by HiOp using the
* Umpire's API. They all are addresses in the same memory space; however, the memory
* space can be host (typically CPU), device (typically GPU), or unified memory (um)
* spaces as per Umpire specification. The selection of the memory space is done via
* the option "mem_space" of HiOp. It is the responsibility of the implementers of
* the HiOp's interfaces (such as the hiop::hiopInterfaceMDS used in this example) to
* work with the "managed by Umpire" pointers in the same memory space as the one
* specified by the "mem_space" option.
*
*/
class SparseRajaEx2 : public hiop::hiopInterfaceSparse
{
public:
SparseRajaEx2(std::string mem_space,
int n,
bool convex_obj,
bool rankdefic_Jac_eq,
bool rankdefic_Jac_ineq,
double scal_neg_obj = 1.0);
virtual ~SparseRajaEx2();

/**
* @brief Number of variables and constraints.
*/
virtual bool get_prob_sizes(size_type& n, size_type& m);

/**
* @brief Get types and bounds on the variables.
*
* @param[in] n number of variables
* @param[out] ixlow array with lower bounds (managed by Umpire)
* @param[out] ixupp array with upper bounds (managed by Umpire)
* @param[out] type array with the variable types (on host)
*/
virtual bool get_vars_info(const size_type& n, double *xlow, double* xupp, NonlinearityType* type);

/**
* Get types and bounds corresponding to constraints. An equality constraint is specified
* by setting the lower and upper bounds equal.
*
* @param[in] m Number of constraints
* @param[out] iclow array with lower bounds (managed by Umpire)
* @param[out] icupp array with upper bounds (managed by Umpire)
* @param[out] type array with the variable types (on host)
*/
virtual bool get_cons_info(const size_type& m, double* clow, double* cupp, NonlinearityType* type);

/**
* Returns the sizes and number of nonzeros of the sparse blocks
*
* @param[out] nx number of variables
* @param[out] nnz_sparse_Jace number of nonzeros in the Jacobian of the equalities w.r.t.
* sparse variables
* @param[out] nnz_sparse_Jaci number of nonzeros in the Jacobian of the inequalities w.r.t.
* sparse variables
* @param[out] nnz_sparse_Hess_Lagr number of nonzeros in the (sparse) Hessian
*/
virtual bool get_sparse_blocks_info(index_type& nx,
index_type& nnz_sparse_Jace,
index_type& nnz_sparse_Jaci,
index_type& nnz_sparse_Hess_Lagr);

/**
* Evaluate objective.
*
* @param[in] n number of variables
* @param[in] x array with the optimization variables or point at which to evaluate
* (managed by Umpire)
* @param[in] new_x indicates whether any of the other eval functions have been
* evaluated previously (false) or not (true) at x
* @param[out] obj_value the objective function value.
*/
virtual bool eval_f(const size_type& n, const double* x, bool new_x, double& obj_value);

/**
* Evaluate a subset of the constraints specified by idx_cons
*
* @param[in] num_cons number of constraints to evaluate (size of idx_cons array)
* @param[in] idx_cons indexes of the constraints to evaluate (managed by Umpire)
* @param[in] x the point at which to evaluate (managed by Umpire)
* @param[in] new_x indicates whether any of the other eval functions have been evaluated
* previously (false) or not (true) at x
* @param[out] cons array with values of the constraints (managed by Umpire, size num_cons)
*/
virtual bool eval_cons(const size_type& n,
const size_type& m,
const size_type& num_cons,
const index_type* idx_cons,
const double* x,
bool new_x,
double* cons)
{
//return false so that HiOp will rely on the constraint evaluator defined below
return false;
}

virtual bool eval_cons(const size_type& n,
const size_type& m,
const double* x,
bool new_x,
double* cons);

/**
* Evaluation of the gradient of the objective.
*
* @param[in] n number of variables
* @param[in] x array with the optimization variables or point at which to evaluate
* (managed by Umpire)
* @param[in] new_x indicates whether any of the other eval functions have been evaluated
* previously (false) or not (true) at x
* @param[out] gradf array with the values of the gradient (managed by Umpire)
*/
virtual bool eval_grad_f(const size_type& n,
const double* x,
bool new_x,
double* gradf);

/**
* Evaluates the Jacobian of the constraints. Please check the user manual and the
* documentation of hiop::hiopInterfaceMDS for a detailed discussion of how the last
* four arguments are expected to behave.
*
* @param[in] n number of variables
* @param[in] m Number of constraints
* @param[in] num_cons number of constraints to evaluate (size of idx_cons array)
* @param[in] idx_cons indexes of the constraints to evaluate (managed by Umpire)
* @param[in] x the point at which to evaluate (managed by Umpire)
* @param[in] new_x indicates whether any of the other eval functions have been evaluated
* previously (false) or not (true) at x
* @param[in] nnzJacS number of nonzeros in the sparse Jacobian
* @param[out] iJacS array of row indexes in the sparse Jacobian (managed by Umpire)
* @param[out] jJacS array of column indexes in the sparse Jacobian (managed by Umpire)
* @param[out] MJacS array of nonzero values in the sparse Jacobian (managed by Umpire)
*/
virtual bool eval_Jac_cons(const size_type& n,
const size_type& m,
const size_type& num_cons,
const index_type* idx_cons,
const double* x,
bool new_x,
const size_type& nnzJacS,
index_type* iJacS,
index_type* jJacS,
double* MJacS)
{
//return false so that HiOp will rely on the Jacobian evaluator defined below
return false;
}

/// Similar to the above, but not used in this example.
virtual bool eval_Jac_cons(const size_type& n,
const size_type& m,
const double* x,
bool new_x,
const size_type& nnzJacS,
index_type* iJacS,
index_type* jJacS,
double* MJacS);


/**
* Evaluate the Hessian of the Lagrangian function. Please consult the user manual for a
* detailed discussion of the form the Lagrangian function takes.
*
* @param[in] n number of variables
* @param[in] m Number of constraints
* @param[in] x the point at which to evaluate (managed by Umpire)
* @param[in] new_x indicates whether any of the other eval functions have been evaluated
* previously (false) or not (true) at x
* @param[in] obj_factor scalar that multiplies the objective term in the Lagrangian function
* @param[in] lambda array with values of the multipliers used by the Lagrangian function
* @param[in] new_lambda indicates whether lambda values changed since last call
* @param[in] nnzHSS number of nonzeros in the (sparse) Hessian w.r.t. sparse variables
* @param[out] iHSS array of row indexes in the Hessian w.r.t. sparse variables
* (managed by Umpire)
* @param[out] jHSS array of column indexes in the Hessian w.r.t. sparse variables
* (managed by Umpire)
* @param[out] MHSS array of nonzero values in the Hessian w.r.t. sparse variables
* (managed by Umpire)
*/
virtual bool eval_Hess_Lagr(const size_type& n,
const size_type& m,
const double* x,
bool new_x,
const double& obj_factor,
const double* lambda,
bool new_lambda,
const size_type& nnzHSS,
index_type* iHSS,
index_type* jHSS,
double* MHSS);

/**
* Implementation of the primal starting point specification
*
* @param[in] n number of variables
* @param[in] x0 the primal starting point(managed by Umpire)
*/
virtual bool get_starting_point(const size_type&n, double* x0);

private:
int n_vars_;
int n_cons_;
bool convex_obj_;
bool rankdefic_eq_;
bool rankdefic_ineq_;
double scal_neg_obj_;

std::string mem_space_;
};

#endif