Files
arpack-ng/EXAMPLES/PYARPACK/pyarpack.cpp
T
Franck HOUSSEN 43d3e5606b Fix arpack solver (#468)
* arpackmm: improve command line.

* [BUG FIX] arpackmm: run arpack with real or imag shift.

* [BUG FIX] arpackmm: make sure the restart file is written.

* [BUG FIX] arpackmm: rewrite the test.

  - Do NOT use eval in bash script which does NOT return command exit code.

  - Use a dedicated maxResNorm to test the residual (no point to deduce
    it from tol).

* Update .gitignore.

* Update CHANGELOG.
2024-09-18 21:55:38 +02:00

435 lines
20 KiB
C++

#include <Python.h> // PyErr_SetString.
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <complex>
#include <pyarpackDrtSolver.hpp>
#include <pyarpackItrSolver.hpp>
#include <sstream> // ostringstream.
#include <string>
#include <vector>
namespace bp = boost::python;
namespace bn = boost::python::numpy;
template <typename RC, typename FD, typename EM, typename SLV>
void exportArpackSparseItr(bp::scope& pySlv, std::string const& dtype) {
// Created nested namespace in module.
pySlv.attr(dtype.c_str()) =
bp::class_<pyarpackSparseItrSolver<RC, FD, EM, SLV>>(
dtype.c_str(),
"arpack data type (must be consistent with numpy dtype)")
.def("solve", &pyarpackSparseItrSolver<RC, FD, EM, SLV>::solve,
(bp::arg("A"), bp::arg("B") = bp::tuple()),
"solve standard or generalised eigen problem where A and B must "
"be sparse and provided in coo format: (dimension, row-indice "
"array, column-indice array, matrice-value array) tuple")
.def("checkEigVec",
&pyarpackSparseItrSolver<RC, FD, EM, SLV>::checkEigVec,
(bp::arg("A"), bp::arg("B") = bp::tuple(),
bp::arg("maxResNorm") = 1.e-3),
"check eigen vectors accuracy (according to max allowed residual norm) "
"where A and B must be sparse and "
"provided in coo format: (dimension, row-indice array, "
"column-indice array, matrice-value array) tuple")
ARPACKSOLVERMEMBER(pyarpackSparseItrSolver)
.def_readwrite(
"slvTol", &pyarpackSparseItrSolver<RC, FD, EM, SLV>::slvTol,
"tolerance of the iterative mode solver - default: 1.e-6")
.def_readwrite("slvMaxIt",
&pyarpackSparseItrSolver<RC, FD, EM, SLV>::slvMaxIt,
"maximum number of iterations of the iterative mode "
"solver - default: 100")
.def_readwrite(
"slvILUDropTol",
&pyarpackSparseItrSolver<RC, FD, EM, SLV>::slvILUDropTol,
"drop tolerance of the ILU preconditioner (if any) of the "
"iterative mode solver - default: 1")
.def_readwrite(
"slvILUFillFactor",
&pyarpackSparseItrSolver<RC, FD, EM, SLV>::slvILUFillFactor,
"fill factor of the ILU preconditioner (if any) of the iterative "
"mode solver - default: 2");
};
template <typename RC, typename FD, typename EM, typename SLV>
void exportArpackSparseDrt(bp::scope& pySlv, std::string const& dtype) {
// Created nested namespace in module.
pySlv.attr(dtype.c_str()) =
bp::class_<pyarpackSparseDrtSolver<RC, FD, EM, SLV>>(
dtype.c_str(),
"arpack data type (must be consistent with numpy dtype)")
.def("solve", &pyarpackSparseDrtSolver<RC, FD, EM, SLV>::solve,
(bp::arg("A"), bp::arg("B") = bp::tuple()),
"solve standard or generalised eigen problem where A and B must "
"be sparse and provided in coo format: (dimension, row-indice "
"array, column-indice array, matrice-value array) tuple")
.def("checkEigVec",
&pyarpackSparseDrtSolver<RC, FD, EM, SLV>::checkEigVec,
(bp::arg("A"), bp::arg("B") = bp::tuple(),
bp::arg("maxResNorm") = 1.e-3),
"check eigen vectors accuracy (according to max allowed residual norm) "
"where A and B must be sparse and "
"provided in coo format: (dimension, row-indice array, "
"column-indice array, matrice-value array) tuple")
ARPACKSOLVERMEMBER(pyarpackSparseDrtSolver)
.def_readwrite(
"slvPvtThd", &pyarpackSparseDrtSolver<RC, FD, EM, SLV>::slvPvtThd,
"pivoting tolerance of the direct mode solver - default: 1.e-6")
.def_readwrite("slvOffset",
&pyarpackSparseDrtSolver<RC, FD, EM, SLV>::slvOffset,
"cholesky offset (LLT, LDLT) of the direct mode "
"solver - default: 0.")
.def_readwrite("slvScale",
&pyarpackSparseDrtSolver<RC, FD, EM, SLV>::slvScale,
"cholesky scale (LLT, LDLT) of the direct mode solver "
"- default: 1.");
};
template <typename RC, typename FD, typename EM, typename SLV>
void exportArpackDenseDrt(bp::scope& pySlv, std::string const& dtype) {
// Created nested namespace in module.
pySlv.attr(dtype.c_str()) =
bp::class_<pyarpackDenseDrtSolver<RC, FD, EM, SLV>>(
dtype.c_str(),
"arpack data type (must be consistent with numpy dtype)")
.def("solve", &pyarpackDenseDrtSolver<RC, FD, EM, SLV>::solve,
(bp::arg("A"), bp::arg("B") = bp::tuple()),
"solve standard or generalised eigen problem where A and B must "
"be dense and provided in raw format: (n-squared matrice-value "
"array, row or column ordered boolean)")
.def("checkEigVec",
&pyarpackDenseDrtSolver<RC, FD, EM, SLV>::checkEigVec,
(bp::arg("A"), bp::arg("B") = bp::tuple(),
bp::arg("maxResNorm") = 1.e-3),
"check eigen vectors accuracy (according to max allowed residual norm) "
"where A and B must be dense and "
"provided in raw format: (n-squared matrice-value array, row or "
"column ordered boolean)")
ARPACKSOLVERMEMBER(pyarpackDenseDrtSolver)
.def_readwrite(
"slvPvtThd", &pyarpackDenseDrtSolver<RC, FD, EM, SLV>::slvPvtThd,
"pivoting tolerance of the direct mode solver - default: 1.e-6")
.def_readwrite("slvOffset",
&pyarpackDenseDrtSolver<RC, FD, EM, SLV>::slvOffset,
"cholesky offset (LLT, LDLT) of the direct mode "
"solver - default: 0.")
.def_readwrite("slvScale",
&pyarpackDenseDrtSolver<RC, FD, EM, SLV>::slvScale,
"cholesky scale (LLT, LDLT) of the direct mode solver "
"- default: 1.");
};
class sparseBiCGDiag {};
class sparseBiCGILU {};
class sparseCGDiag {};
class sparseCGILU {};
class sparseLLT {};
class sparseLDLT {};
class sparseLU {};
class sparseQR {};
class denseLLT {};
class denseLDLT {};
class denseLURR {};
class denseQRRR {};
class denseLUPP {};
class denseQRPP {};
std::complex<double> EigVecZGetItem(
Eigen::Matrix<std::complex<double>, Eigen::Dynamic, 1>& M, int idx) {
if (idx < 0 || idx >= M.size()) {
pyarpackThrowError("index out of range");
return std::complex<double>();
}
return M[idx];
};
std::string EigVecZToString(EigVecZ const& vec) {
std::ostringstream s;
s << vec;
return s.str();
};
BOOST_PYTHON_MODULE(pyarpack) {
// Initialize.
bn::initialize();
bp::class_<std::vector<std::complex<double>>>("StdVecZ").def(
bp::vector_indexing_suite<std::vector<std::complex<double>>>());
bp::class_<Eigen::Matrix<std::complex<double>, Eigen::Dynamic, 1>>("EigVecZ")
.def("__getitem__", &EigVecZGetItem)
.def("__str__", &EigVecZToString);
bp::class_<std::vector<EigVecZ>>("StdVecEVZ")
.def("__iter__", bp::iterator<std::vector<EigVecZ>>())
.def(bp::vector_indexing_suite<std::vector<EigVecZ>>());
// Documentation of the python module.
std::ostringstream doc;
doc << "You can use sparse or dense matrices, and, play with iterative or direct mode solvers (CG, LU, ...):" << std::endl;
doc << "1. choose arpack solver with a given mode solver" << std::endl;
doc << " 1.1. if you need to handle sparse matrices" << std::endl;
doc << " >> from pyarpack import sparseBiCG as pyarpackSlv" << std::endl;
doc << " 1.2. if you need to handle dense matrices" << std::endl;
doc << " >> from pyarpack import denseBiCG as pyarpackSlv" << std::endl;
doc << "2. choose arpack data type (float, double, ...)" << std::endl;
doc << " >> arpackSlv = pyarpackSlv.double()" << std::endl;
doc << "3. solve the eigen problem" << std::endl;
doc << " >> arpackSlv.solve(A, B)" << std::endl;
doc << "4. get eigen values and vectors" << std::endl;
doc << " >> print(arpackSlv.vec)" << std::endl;
doc << " >> print(arpackSlv.val)" << std::endl;
doc << std::endl;
doc << "Notes:" << std::endl;
doc << "1. arpack data type (float, double, ...) must be consistent with A/B numpy dtypes (float32, float64, ...)." << std::endl;
doc << " at python side, the data MUST be casted in the EXACT expected type (int32, int64, float, double, ...)." << std::endl;
doc << " otherwise, C++ may not get the data the way it expects them: C++ will not know how to read python data." << std::endl;
doc << " if you are not sure how data have been passed from python to C++, set arpackSlv.debug = 1 and check out debug traces." << std::endl;
doc << " in other words, pyarpack users MUST :" << std::endl;
doc << " 1.1. create numpy arrays specifying explicitly the type:" << std::endl;
doc << " >> Aij = np.array([], dtype='complex128')" << std::endl;
doc << " 1.2. filling numpy arrays casting value on append:" << std::endl;
doc << " >> Aij = np.append(Aij, np.complex128(complex( 200., 200.))) # Casting value on append is MANDATORY or C++ won't get the expected type." << std::endl;
doc << " 1.3. calling the solver flavor which is consistent with the numpy array data type:" << std::endl;
doc << " >> arpackSlv = pyarpackSlv.complexDouble() # Caution: complexDouble <=> np.array(..., dtype='complex128')" << std::endl;
doc << " note: NO data type check can be done at C++ side, the pyarpack user MUST insure data consistency." << std::endl;
doc << "2. sparse matrices must be provided in coo format (n, i, j, Mij), that is, as a tuple where:" << std::endl;
doc << " 2.1. n is an integer." << std::endl;
doc << " 2.2. i, j, Mij are 1 x nnz numpy arrays." << std::endl;
doc << "3. dense matrices must be provided in raw format (Mij, rowOrdered), that is, as a tuple where:" << std::endl;
doc << " 3.1. Mij is an n x n numpy array." << std::endl;
doc << " 3.2. rowOrdered is a boolean (column ordered if False)." << std::endl;
doc << "4. arpack mode solver are provided by eigen:" << std::endl;
doc << " 4.1. when solver is iterative, A and B can be sparse only." << std::endl;
doc << " 4.2. when solver is direct, A and B can be sparse or dense." << std::endl;
bp::scope().attr("__doc__") = doc.str().c_str();
// Specify that this module is actually a package.
bp::object package = bp::scope();
package.attr("__path__") = "pyarpack";
// Create python module.
std::string module = "pyarpack";
bp::object pyModule(
bp::handle<>(bp::borrowed(PyImport_AddModule(module.c_str()))));
// Create modules.
{
std::string slv = "sparseBiCGDiag";
std::string slvHelp =
"arpack internal mode solver (mode > 1): BiCG with diagonal (Jacobi) "
"preconditioner";
bp::scope pySlvBiCGDiag =
bp::class_<sparseBiCGDiag>(slv.c_str(), slvHelp.c_str());
exportArpackSparseItr<float, float, EigSMxS, EigSBiCGS>(pySlvBiCGDiag,
"float");
exportArpackSparseItr<double, double, EigSMxD, EigSBiCGD>(pySlvBiCGDiag,
"double");
exportArpackSparseItr<std::complex<float>, float, EigSMxC, EigSBiCGC>(
pySlvBiCGDiag, "complexFloat");
exportArpackSparseItr<std::complex<double>, double, EigSMxZ, EigSBiCGZ>(
pySlvBiCGDiag, "complexDouble");
}
{
std::string slv = "sparseBiCGILU";
std::string slvHelp =
"arpack internal mode solver (mode > 1): BiCG with ILU preconditioner";
bp::scope pySlvBiCGILU =
bp::class_<sparseBiCGILU>(slv.c_str(), slvHelp.c_str());
exportArpackSparseItr<float, float, EigSMxS, EigSBiCGILUS>(pySlvBiCGILU,
"float");
exportArpackSparseItr<double, double, EigSMxD, EigSBiCGILUD>(pySlvBiCGILU,
"double");
exportArpackSparseItr<std::complex<float>, float, EigSMxC, EigSBiCGILUC>(
pySlvBiCGILU, "complexFloat");
exportArpackSparseItr<std::complex<double>, double, EigSMxZ, EigSBiCGILUZ>(
pySlvBiCGILU, "complexDouble");
}
{
std::string slv = "sparseCGDiag";
std::string slvHelp =
"arpack internal mode solver (mode > 1): CG with diagonal (Jacobi) "
"preconditioner";
bp::scope pySlvCGDiag =
bp::class_<sparseCGDiag>(slv.c_str(), slvHelp.c_str());
exportArpackSparseItr<float, float, EigSMxS, EigSCGS>(pySlvCGDiag, "float");
exportArpackSparseItr<double, double, EigSMxD, EigSCGD>(pySlvCGDiag,
"double");
exportArpackSparseItr<std::complex<float>, float, EigSMxC, EigSCGC>(
pySlvCGDiag, "complexFloat");
exportArpackSparseItr<std::complex<double>, double, EigSMxZ, EigSCGZ>(
pySlvCGDiag, "complexDouble");
}
{
std::string slv = "sparseCGILU";
std::string slvHelp =
"arpack internal mode solver (mode > 1): CG with ILU preconditioner";
bp::scope pySlvCGILU =
bp::class_<sparseCGILU>(slv.c_str(), slvHelp.c_str());
exportArpackSparseItr<float, float, EigSMxS, EigSCGILUS>(pySlvCGILU,
"float");
exportArpackSparseItr<double, double, EigSMxD, EigSCGILUD>(pySlvCGILU,
"double");
exportArpackSparseItr<std::complex<float>, float, EigSMxC, EigSCGILUC>(
pySlvCGILU, "complexFloat");
exportArpackSparseItr<std::complex<double>, double, EigSMxZ, EigSCGILUZ>(
pySlvCGILU, "complexDouble");
}
{
std::string slv = "sparseLLT";
std::string slvHelp = "arpack internal mode solver (mode > 1): LLT";
bp::scope pySlvLLT = bp::class_<sparseLLT>(slv.c_str(), slvHelp.c_str());
exportArpackSparseDrt<float, float, EigSMxS, EigSLLTS>(pySlvLLT, "float");
exportArpackSparseDrt<double, double, EigSMxD, EigSLLTD>(pySlvLLT,
"double");
exportArpackSparseDrt<std::complex<float>, float, EigSMxC, EigSLLTC>(
pySlvLLT, "complexFloat");
exportArpackSparseDrt<std::complex<double>, double, EigSMxZ, EigSLLTZ>(
pySlvLLT, "complexDouble");
}
{
std::string slv = "sparseLDLT";
std::string slvHelp = "arpack internal mode solver (mode > 1): LDLT";
bp::scope pySlvLDLT = bp::class_<sparseLDLT>(slv.c_str(), slvHelp.c_str());
exportArpackSparseDrt<float, float, EigSMxS, EigSLDLTS>(pySlvLDLT, "float");
exportArpackSparseDrt<double, double, EigSMxD, EigSLDLTD>(pySlvLDLT,
"double");
exportArpackSparseDrt<std::complex<float>, float, EigSMxC, EigSLDLTC>(
pySlvLDLT, "complexFloat");
exportArpackSparseDrt<std::complex<double>, double, EigSMxZ, EigSLDLTZ>(
pySlvLDLT, "complexDouble");
}
{
std::string slv = "sparseLU";
std::string slvHelp = "arpack internal mode solver (mode > 1): LU";
bp::scope pySlvLU = bp::class_<sparseLU>(slv.c_str(), slvHelp.c_str());
exportArpackSparseDrt<float, float, EigSMxS, EigSLUS>(pySlvLU, "float");
exportArpackSparseDrt<double, double, EigSMxD, EigSLUD>(pySlvLU, "double");
exportArpackSparseDrt<std::complex<float>, float, EigSMxC, EigSLUC>(
pySlvLU, "complexFloat");
exportArpackSparseDrt<std::complex<double>, double, EigSMxZ, EigSLUZ>(
pySlvLU, "complexDouble");
}
{
std::string slv = "sparseQR";
std::string slvHelp = "arpack internal mode solver (mode > 1): QR";
bp::scope pySlvQR = bp::class_<sparseQR>(slv.c_str(), slvHelp.c_str());
exportArpackSparseDrt<float, float, EigSMxS, EigSQRS>(pySlvQR, "float");
exportArpackSparseDrt<double, double, EigSMxD, EigSQRD>(pySlvQR, "double");
exportArpackSparseDrt<std::complex<float>, float, EigSMxC, EigSQRC>(
pySlvQR, "complexFloat");
exportArpackSparseDrt<std::complex<double>, double, EigSMxZ, EigSQRZ>(
pySlvQR, "complexDouble");
}
{
std::string slv = "denseLLT";
std::string slvHelp = "arpack internal mode solver (mode > 1): LLT";
bp::scope pySlvLLT = bp::class_<denseLLT>(slv.c_str(), slvHelp.c_str());
exportArpackDenseDrt<float, float, EigDMxS, EigDLLTS>(pySlvLLT, "float");
exportArpackDenseDrt<double, double, EigDMxD, EigDLLTD>(pySlvLLT, "double");
exportArpackDenseDrt<std::complex<float>, float, EigDMxC, EigDLLTC>(
pySlvLLT, "complexFloat");
exportArpackDenseDrt<std::complex<double>, double, EigDMxZ, EigDLLTZ>(
pySlvLLT, "complexDouble");
}
{
std::string slv = "denseLDLT";
std::string slvHelp = "arpack internal mode solver (mode > 1): LDLT";
bp::scope pySlvLDLT = bp::class_<denseLDLT>(slv.c_str(), slvHelp.c_str());
exportArpackDenseDrt<float, float, EigDMxS, EigDLDLTS>(pySlvLDLT, "float");
exportArpackDenseDrt<double, double, EigDMxD, EigDLDLTD>(pySlvLDLT,
"double");
exportArpackDenseDrt<std::complex<float>, float, EigDMxC, EigDLDLTC>(
pySlvLDLT, "complexFloat");
exportArpackDenseDrt<std::complex<double>, double, EigDMxZ, EigDLDLTZ>(
pySlvLDLT, "complexDouble");
}
{
std::string slv = "denseLURR";
std::string slvHelp =
"arpack internal mode solver (mode > 1): LU Rank Revealing (slower, "
"more stable)";
bp::scope pySlvLURR = bp::class_<denseLURR>(slv.c_str(), slvHelp.c_str());
exportArpackDenseDrt<float, float, EigDMxS, EigDFLUS>(pySlvLURR, "float");
exportArpackDenseDrt<double, double, EigDMxD, EigDFLUD>(pySlvLURR,
"double");
exportArpackDenseDrt<std::complex<float>, float, EigDMxC, EigDFLUC>(
pySlvLURR, "complexFloat");
exportArpackDenseDrt<std::complex<double>, double, EigDMxZ, EigDFLUZ>(
pySlvLURR, "complexDouble");
}
{
std::string slv = "denseQRRR";
std::string slvHelp =
"arpack internal mode solver (mode > 1): QR Rank Revealing (slower, "
"more stable)";
bp::scope pySlvQRRR = bp::class_<denseQRRR>(slv.c_str(), slvHelp.c_str());
exportArpackDenseDrt<float, float, EigDMxS, EigDFQRS>(pySlvQRRR, "float");
exportArpackDenseDrt<double, double, EigDMxD, EigDFQRD>(pySlvQRRR,
"double");
exportArpackDenseDrt<std::complex<float>, float, EigDMxC, EigDFQRC>(
pySlvQRRR, "complexFloat");
exportArpackDenseDrt<std::complex<double>, double, EigDMxZ, EigDFQRZ>(
pySlvQRRR, "complexDouble");
}
{
std::string slv = "denseLUPP";
std::string slvHelp =
"arpack internal mode solver (mode > 1): LU Partial Pivoting (faster, "
"less stable)";
bp::scope pySlvLUPP = bp::class_<denseLUPP>(slv.c_str(), slvHelp.c_str());
exportArpackDenseDrt<float, float, EigDMxS, EigDPLUS>(pySlvLUPP, "float");
exportArpackDenseDrt<double, double, EigDMxD, EigDPLUD>(pySlvLUPP,
"double");
exportArpackDenseDrt<std::complex<float>, float, EigDMxC, EigDPLUC>(
pySlvLUPP, "complexFloat");
exportArpackDenseDrt<std::complex<double>, double, EigDMxZ, EigDPLUZ>(
pySlvLUPP, "complexDouble");
}
{
std::string slv = "denseQRPP";
std::string slvHelp =
"arpack internal mode solver (mode > 1): QR Partial Pivoting (faster, "
"less stable)";
bp::scope pySlvQPPR = bp::class_<denseQRPP>(slv.c_str(), slvHelp.c_str());
exportArpackDenseDrt<float, float, EigDMxS, EigDPQRS>(pySlvQPPR, "float");
exportArpackDenseDrt<double, double, EigDMxD, EigDPQRD>(pySlvQPPR,
"double");
exportArpackDenseDrt<std::complex<float>, float, EigDMxC, EigDPQRC>(
pySlvQPPR, "complexFloat");
exportArpackDenseDrt<std::complex<double>, double, EigDMxZ, EigDPQRZ>(
pySlvQPPR, "complexDouble");
}
}
// Local Variables:
// mode: c++
// c-file-style:"stroustrup"
// show-trailing-whitespace: t
// End:
/* vim: set sw=2 ts=2 et smartindent :*/