1
1

Compare commits

...

54 Commits

Author SHA1 Message Date
a2a7ca6aa1 SLIM: Fix uppercase folder name in CMake file 2018-05-27 19:46:34 +02:00
bbfee34e9e Merge remote-tracking branch 'origin/master' into uv_unwrapping_slim_algorithm 2018-05-01 17:03:20 +02:00
Aurel Gruber
ae7b679021 SLIM: disallowing pins to move to places that the SLIM algorithm can't handle. Also, this simplifiey the way we transfer uv coords back to native part. 2017-04-13 16:33:11 +02:00
Aurel Gruber
3cae116704 SLIM: In live Unwrap mode unpinned vertices can now be moved. Also, a single pin also invokes SLIM. That also avoids crashing minimize_stretch when invoked on chart with one pin. 2017-04-13 16:22:49 +02:00
58caf3121e SLIM: reuse LSCM logic for pinning, to fix issue with multiple charts. 2017-03-25 16:55:59 +01:00
183ca1af3f SLIM: move most SLIM integration behind param_* API, reuse more code. 2017-03-25 16:55:24 +01:00
bf2603baf6 SLIM: transfer weights in construction, fix subsurf case. 2017-03-25 16:37:58 +01:00
2e662bbca4 SLIM: use operator history instead of toolsettings for remembering settings. 2017-03-25 16:37:58 +01:00
21bccefd12 SLIM: move code around, no functional changes. 2017-03-25 16:37:33 +01:00
65f32e105f SLIM: match code style. 2017-03-25 14:18:04 +01:00
Aurel Gruber
9fdc5345cd merging master into release 2017-03-23 16:44:22 +01:00
Aurel Gruber
0475c0c41e UV Unwrapping SLIM reducing SLIM iterations per live-unwrap-step to 3, taking care of memory leaks, removing packing when minimizi-stretch with non-fix border 2017-03-16 11:08:02 +01:00
Aurel Gruber
be22fc6720 Merge branch 'live_unwrap' 2017-03-15 16:44:39 +01:00
Aurel Gruber
92d7b4b1c2 merging in master 2017-03-15 16:39:37 +01:00
AurelGruber
c1a242ab58 Merge branch 'slim' into 'master'
Slim

See merge request !3
2017-03-15 15:36:00 +00:00
Aurel Gruber
47e71e8746 taking care of compiler warnings 2017-03-15 16:33:38 +01:00
Aurel Gruber
e4e202cb1c UV Unwrapping SLIM Changing ui parameter names and showing parameters based on method 2017-03-15 16:06:40 +01:00
Aurel Gruber
7b59b53938 UV Unwrapping SLIM adding liveUnwrap with slim 2017-03-15 14:16:13 +01:00
Aurel Gruber
dbe69d982f UV Unwrapping SLIM: small bugfixes and putting char array and not just pointer into toolsettings for vertexgroupname 2017-03-14 09:37:00 +01:00
Aurel Gruber
968f1c0de3 UV Unwrapping SLIM: reordering functino definitions to remove prototypes and cleaning header files 2017-03-14 09:37:00 +01:00
Aurel Gruber
901f02b8ec UV Unwrapping SLIM: removing remainder of old minimize stretch 2017-03-14 09:37:00 +01:00
Aurel Gruber
2a76f4ea7d UV Unwrapping SLIM: space added after if 2017-03-14 09:37:00 +01:00
Aurel Gruber
c9b3e34b9a UV Unwrapping SLIM: minimize_stretch now only affects selected vertices. RESPECTS user defined pins! 2017-03-14 09:37:00 +01:00
Aurel Gruber
f5b51e4c65 UV Unwrapping SLIM: removing unnecessary ui parameters from minimize_stretch_operator 2017-03-14 09:37:00 +01:00
Aurel Gruber
c603ba9e7c UV Unwrapping SLIM: renaming slim_c_interface to slim_capi and renaming contained functions 2017-03-14 09:36:59 +01:00
Aurel Gruber
eef1392c47 UV Unwrapping SLIM: renaming src to intern 2017-03-14 09:36:59 +01:00
Aurel Gruber
c35c83a3ab UV Unwrapping SLIM: adding slim_matrix_transfer.h 2017-03-14 09:36:59 +01:00
Aurel Gruber
732159dcf8 UV Unwrapping SLIM: further refactoring according to discussion on D2530 2017-03-14 09:36:59 +01:00
Aurel Gruber
bccca31bd1 UV Unwrapping SLIM: removing old minimize stretch operator 2017-03-14 09:36:59 +01:00
Aurel Gruber
67ef01a2c3 UV Unwrapping SLIM: respecting source/blender style conventions 2017-03-14 09:36:59 +01:00
Aurel Gruber
118d63712d UV Unwrapping SLIM: removing thesis marker-comments from code 2017-03-14 09:36:59 +01:00
Aurel Gruber
3027e0bdca UV Unwrapping SLIM: adding GPL header comment 2017-03-14 09:36:59 +01:00
Aurel Gruber
99ed9041e0 UV Unwrapping SLIM: renaming another file 2017-03-14 09:36:59 +01:00
Aurel Gruber
ebae2d6aa2 UV Unwrapping SLIM: renaming files and moving headers 2017-03-14 09:36:58 +01:00
Aurel Gruber
5c4eedc91f UV Unwrapping SLIM: renaming intern/SLIM to intern/slim 2017-03-14 09:36:58 +01:00
Aurel Gruber
5681b886a0 UV Unwrapping SLIM: refactoring UVInitializer 2017-03-14 09:36:58 +01:00
Aurel Gruber
31dd611003 UV Unwrapping SLIM: adding harmonic and mvc to uvinitializer 2017-03-14 09:36:58 +01:00
Aurel Gruber
95863bbb98 Merge branch 'uv_unwrapping_slim_algorithm' of git.blender.org:blender into uv_unwrapping_slim_algorithm 2017-02-27 14:15:46 +01:00
Aurel Gruber
2f86198cee Implementation of UV unwrapping with SLIM as disdussed on T48036.
commits:

Category: UV Unwrapping SLIM Algorithm Integration

Added SLIM Subfolder

Category: UV Unwrapping SLIM Algorithm Integration

added subfolder SLIM to CMakeFile of /intern

Category: UV Unwrapping SLIM Algorithm Integration

integrating SLIM including data gathering and transfer from Blender to SLIM

This commit is huge, because I copied over the code from a different repository. Not commit-by-commit.

The Algorithm can be invoked either by choosing SLIM from the dropdown in the unwrapping settings or by
hitting ctrl. + m in the uv editor for relaxation. Tried adding it to the menu the same way as minimizing stretch is there but failed.

Category: UV Unwrapping SLIM Algorithm Integration

preserving vertex ids and gathering weights

Category: UV Unwrapping SLIM Algorithm Integration

adding more members to phandle and creating param_begin

Category: UV Unwrapping SLIM Algorithm Integration

fixing bug that causes weight-per-vertex mapping to be wrong

Category: UV Unwrapping SLIM Algorithm Integration

adjustments to the UI parameters

Category: UV Unwrapping SLIM Algorithm Integration

adding weightinfluence

Category: UV Unwrapping SLIM Algorithm Integration

slim interactive exec now only for changing parameters

Category: UV Unwrapping SLIM Algorithm Integration

taking care of memory leaks

Category: UV Unwrapping SLIM Algorithm Integration

adding relative scale, reflection mode and Vertex group input

Category: UV Unwrapping SLIM Algorithm Integration

correcting wrong comment on SLIM phases

Category: UV Unwrapping SLIM Algorithm Integration

Adding SLIM code by means of git read-tree

Category: UV Unwrapping SLIM Algorithm Integration

freeing matrix_transfer properly

Category: UV Unwrapping SLIM Algorithm Integration

adding (unsupported-) eigen files

Reviewers: brecht, sergey

Differential Revision: https://developer.blender.org/D2530
2017-02-27 14:04:24 +01:00
Aurel Gruber
16c6c8a1c4 Category: UV Unwrapping SLIM Algorithm Integration
adding (unsupported-) eigen files
2017-02-24 09:31:38 +01:00
Aurel Gruber
1cb016491a Category: UV Unwrapping SLIM Algorithm Integration
freeing matrix_transfer properly
2017-02-24 09:28:33 +01:00
Aurel Gruber
7e2e77714c Category: UV Unwrapping SLIM Algorithm Integration
Adding SLIM code by means of git read-tree
2017-02-23 16:34:16 +01:00
Aurel Gruber
15785145b1 Category: UV Unwrapping SLIM Algorithm Integration
correcting wrong comment on SLIM phases
2017-02-23 16:34:16 +01:00
Aurel Gruber
26fd7e084f Category: UV Unwrapping SLIM Algorithm Integration
adding relative scale, reflection mode and Vertex group input
2017-02-23 16:34:16 +01:00
Aurel Gruber
88a327d3ed Category: UV Unwrapping SLIM Algorithm Integration
taking care of memory leaks
2017-02-23 16:34:16 +01:00
Aurel Gruber
2bf059ad3b Category: UV Unwrapping SLIM Algorithm Integration
slim interactive exec now only for changing parameters
2017-02-23 16:34:16 +01:00
Aurel Gruber
9c55a8374e Category: UV Unwrapping SLIM Algorithm Integration
adding weightinfluence
2017-02-23 16:34:16 +01:00
Aurel Gruber
1f88850826 Category: UV Unwrapping SLIM Algorithm Integration
adjustments to the UI parameters
2017-02-23 16:34:16 +01:00
Aurel Gruber
9f7c4aa581 Category: UV Unwrapping SLIM Algorithm Integration
fixing bug that causes weight-per-vertex mapping to be wrong
2017-02-23 16:34:16 +01:00
Aurel Gruber
3e184a57c5 Category: UV Unwrapping SLIM Algorithm Integration
adding more members to phandle and creating param_begin
2017-02-23 16:34:16 +01:00
Aurel Gruber
b6ab20876b Category: UV Unwrapping SLIM Algorithm Integration
preserving vertex ids and gathering weights
2017-02-23 16:34:16 +01:00
Aurel Gruber
08d1b312be Category: UV Unwrapping SLIM Algorithm Integration
integrating SLIM including data gathering and transfer from Blender to SLIM

This commit is huge, because I copied over the code from a different repository. Not commit-by-commit.

The Algorithm can be invoked either by choosing SLIM from the dropdown in the unwrapping settings or by
hitting ctrl. + m in the uv editor for relaxation. Tried adding it to the menu the same way as minimizing stretch is there but failed.
2017-02-23 16:34:16 +01:00
Aurel Gruber
5793441a35 Category: UV Unwrapping SLIM Algorithm Integration
added subfolder SLIM to CMakeFile of /intern
2017-02-23 16:34:16 +01:00
Aurel Gruber
979b1b1159 Category: UV Unwrapping SLIM Algorithm Integration
Added SLIM Subfolder
2017-02-23 16:34:16 +01:00
201 changed files with 30547 additions and 420 deletions

View File

@@ -0,0 +1,156 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <g.gael@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_ADLOC_FORWARD
#define EIGEN_ADLOC_FORWARD
//--------------------------------------------------------------------------------
//
// This file provides support for adolc's adouble type in forward mode.
// ADOL-C is a C++ automatic differentiation library,
// see https://projects.coin-or.org/ADOL-C for more information.
//
// Note that the maximal number of directions is controlled by
// the preprocessor token NUMBER_DIRECTIONS. The default is 2.
//
//--------------------------------------------------------------------------------
#define ADOLC_TAPELESS
#ifndef NUMBER_DIRECTIONS
# define NUMBER_DIRECTIONS 2
#endif
#include <adolc/adouble.h>
// adolc defines some very stupid macros:
#if defined(malloc)
# undef malloc
#endif
#if defined(calloc)
# undef calloc
#endif
#if defined(realloc)
# undef realloc
#endif
#include <Eigen/Core>
namespace Eigen {
/**
* \defgroup AdolcForward_Module Adolc forward module
* This module provides support for adolc's adouble type in forward mode.
* ADOL-C is a C++ automatic differentiation library,
* see https://projects.coin-or.org/ADOL-C for more information.
* It mainly consists in:
* - a struct Eigen::NumTraits<adtl::adouble> specialization
* - overloads of internal::* math function for adtl::adouble type.
*
* Note that the maximal number of directions is controlled by
* the preprocessor token NUMBER_DIRECTIONS. The default is 2.
*
* \code
* #include <unsupported/Eigen/AdolcSupport>
* \endcode
*/
//@{
} // namespace Eigen
// Eigen's require a few additional functions which must be defined in the same namespace
// than the custom scalar type own namespace
namespace adtl {
inline const adouble& conj(const adouble& x) { return x; }
inline const adouble& real(const adouble& x) { return x; }
inline adouble imag(const adouble&) { return 0.; }
inline adouble abs(const adouble& x) { return fabs(x); }
inline adouble abs2(const adouble& x) { return x*x; }
}
namespace Eigen {
template<> struct NumTraits<adtl::adouble>
: NumTraits<double>
{
typedef adtl::adouble Real;
typedef adtl::adouble NonInteger;
typedef adtl::adouble Nested;
enum {
IsComplex = 0,
IsInteger = 0,
IsSigned = 1,
RequireInitialization = 1,
ReadCost = 1,
AddCost = 1,
MulCost = 1
};
};
template<typename Functor> class AdolcForwardJacobian : public Functor
{
typedef adtl::adouble ActiveScalar;
public:
AdolcForwardJacobian() : Functor() {}
AdolcForwardJacobian(const Functor& f) : Functor(f) {}
// forward constructors
template<typename T0>
AdolcForwardJacobian(const T0& a0) : Functor(a0) {}
template<typename T0, typename T1>
AdolcForwardJacobian(const T0& a0, const T1& a1) : Functor(a0, a1) {}
template<typename T0, typename T1, typename T2>
AdolcForwardJacobian(const T0& a0, const T1& a1, const T1& a2) : Functor(a0, a1, a2) {}
typedef typename Functor::InputType InputType;
typedef typename Functor::ValueType ValueType;
typedef typename Functor::JacobianType JacobianType;
typedef Matrix<ActiveScalar, InputType::SizeAtCompileTime, 1> ActiveInput;
typedef Matrix<ActiveScalar, ValueType::SizeAtCompileTime, 1> ActiveValue;
void operator() (const InputType& x, ValueType* v, JacobianType* _jac) const
{
eigen_assert(v!=0);
if (!_jac)
{
Functor::operator()(x, v);
return;
}
JacobianType& jac = *_jac;
ActiveInput ax = x.template cast<ActiveScalar>();
ActiveValue av(jac.rows());
for (int j=0; j<jac.cols(); j++)
for (int i=0; i<jac.cols(); i++)
ax[i].setADValue(j, i==j ? 1 : 0);
Functor::operator()(ax, &av);
for (int i=0; i<jac.rows(); i++)
{
(*v)[i] = av[i].getValue();
for (int j=0; j<jac.cols(); j++)
jac.coeffRef(i,j) = av[i].getADValue(j);
}
}
protected:
};
//@}
}
#endif // EIGEN_ADLOC_FORWARD

View File

@@ -0,0 +1,190 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Gael Guennebaud <g.gael@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_ALIGNED_VECTOR3
#define EIGEN_ALIGNED_VECTOR3
#include <Eigen/Geometry>
namespace Eigen {
/**
* \defgroup AlignedVector3_Module Aligned vector3 module
*
* \code
* #include <unsupported/Eigen/AlignedVector3>
* \endcode
*/
//@{
/** \class AlignedVector3
*
* \brief A vectorization friendly 3D vector
*
* This class represents a 3D vector internally using a 4D vector
* such that vectorization can be seamlessly enabled. Of course,
* the same result can be achieved by directly using a 4D vector.
* This class makes this process simpler.
*
*/
// TODO specialize Cwise
template<typename _Scalar> class AlignedVector3;
namespace internal {
template<typename _Scalar> struct traits<AlignedVector3<_Scalar> >
: traits<Matrix<_Scalar,3,1,0,4,1> >
{
};
}
template<typename _Scalar> class AlignedVector3
: public MatrixBase<AlignedVector3<_Scalar> >
{
typedef Matrix<_Scalar,4,1> CoeffType;
CoeffType m_coeffs;
public:
typedef MatrixBase<AlignedVector3<_Scalar> > Base;
EIGEN_DENSE_PUBLIC_INTERFACE(AlignedVector3)
using Base::operator*;
inline Index rows() const { return 3; }
inline Index cols() const { return 1; }
inline const Scalar& coeff(Index row, Index col) const
{ return m_coeffs.coeff(row, col); }
inline Scalar& coeffRef(Index row, Index col)
{ return m_coeffs.coeffRef(row, col); }
inline const Scalar& coeff(Index index) const
{ return m_coeffs.coeff(index); }
inline Scalar& coeffRef(Index index)
{ return m_coeffs.coeffRef(index);}
inline AlignedVector3(const Scalar& x, const Scalar& y, const Scalar& z)
: m_coeffs(x, y, z, Scalar(0))
{}
inline AlignedVector3(const AlignedVector3& other)
: Base(), m_coeffs(other.m_coeffs)
{}
template<typename XprType, int Size=XprType::SizeAtCompileTime>
struct generic_assign_selector {};
template<typename XprType> struct generic_assign_selector<XprType,4>
{
inline static void run(AlignedVector3& dest, const XprType& src)
{
dest.m_coeffs = src;
}
};
template<typename XprType> struct generic_assign_selector<XprType,3>
{
inline static void run(AlignedVector3& dest, const XprType& src)
{
dest.m_coeffs.template head<3>() = src;
dest.m_coeffs.w() = Scalar(0);
}
};
template<typename Derived>
inline explicit AlignedVector3(const MatrixBase<Derived>& other)
{
generic_assign_selector<Derived>::run(*this,other.derived());
}
inline AlignedVector3& operator=(const AlignedVector3& other)
{ m_coeffs = other.m_coeffs; return *this; }
inline AlignedVector3 operator+(const AlignedVector3& other) const
{ return AlignedVector3(m_coeffs + other.m_coeffs); }
inline AlignedVector3& operator+=(const AlignedVector3& other)
{ m_coeffs += other.m_coeffs; return *this; }
inline AlignedVector3 operator-(const AlignedVector3& other) const
{ return AlignedVector3(m_coeffs - other.m_coeffs); }
inline AlignedVector3 operator-=(const AlignedVector3& other)
{ m_coeffs -= other.m_coeffs; return *this; }
inline AlignedVector3 operator*(const Scalar& s) const
{ return AlignedVector3(m_coeffs * s); }
inline friend AlignedVector3 operator*(const Scalar& s,const AlignedVector3& vec)
{ return AlignedVector3(s * vec.m_coeffs); }
inline AlignedVector3& operator*=(const Scalar& s)
{ m_coeffs *= s; return *this; }
inline AlignedVector3 operator/(const Scalar& s) const
{ return AlignedVector3(m_coeffs / s); }
inline AlignedVector3& operator/=(const Scalar& s)
{ m_coeffs /= s; return *this; }
inline Scalar dot(const AlignedVector3& other) const
{
eigen_assert(m_coeffs.w()==Scalar(0));
eigen_assert(other.m_coeffs.w()==Scalar(0));
return m_coeffs.dot(other.m_coeffs);
}
inline void normalize()
{
m_coeffs /= norm();
}
inline AlignedVector3 normalized()
{
return AlignedVector3(m_coeffs / norm());
}
inline Scalar sum() const
{
eigen_assert(m_coeffs.w()==Scalar(0));
return m_coeffs.sum();
}
inline Scalar squaredNorm() const
{
eigen_assert(m_coeffs.w()==Scalar(0));
return m_coeffs.squaredNorm();
}
inline Scalar norm() const
{
using std::sqrt;
return sqrt(squaredNorm());
}
inline AlignedVector3 cross(const AlignedVector3& other) const
{
return AlignedVector3(m_coeffs.cross3(other.m_coeffs));
}
template<typename Derived>
inline bool isApprox(const MatrixBase<Derived>& other, const RealScalar& eps=NumTraits<Scalar>::dummy_precision()) const
{
return m_coeffs.template head<3>().isApprox(other,eps);
}
};
//@}
}
#endif // EIGEN_ALIGNED_VECTOR3

View File

@@ -0,0 +1,31 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_ARPACKSUPPORT_MODULE_H
#define EIGEN_ARPACKSUPPORT_MODULE_H
#include <Eigen/Core>
#include <Eigen/src/Core/util/DisableStupidWarnings.h>
/** \defgroup ArpackSupport_Module Arpack support module
*
* This module provides a wrapper to Arpack, a library for sparse eigenvalue decomposition.
*
* \code
* #include <Eigen/ArpackSupport>
* \endcode
*/
#include <Eigen/SparseCholesky>
#include "src/Eigenvalues/ArpackSelfAdjointEigenSolver.h"
#include <Eigen/src/Core/util/ReenableStupidWarnings.h>
#endif // EIGEN_ARPACKSUPPORT_MODULE_H
/* vim: set filetype=cpp et sw=2 ts=2 ai: */

View File

@@ -0,0 +1,40 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <g.gael@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_AUTODIFF_MODULE
#define EIGEN_AUTODIFF_MODULE
namespace Eigen {
/**
* \defgroup AutoDiff_Module Auto Diff module
*
* This module features forward automatic differentation via a simple
* templated scalar type wrapper AutoDiffScalar.
*
* Warning : this should NOT be confused with numerical differentiation, which
* is a different method and has its own module in Eigen : \ref NumericalDiff_Module.
*
* \code
* #include <unsupported/Eigen/AutoDiff>
* \endcode
*/
//@{
}
#include "src/AutoDiff/AutoDiffScalar.h"
// #include "src/AutoDiff/AutoDiffVector.h"
#include "src/AutoDiff/AutoDiffJacobian.h"
namespace Eigen {
//@}
}
#endif // EIGEN_AUTODIFF_MODULE

95
extern/Eigen3/unsupported/Eigen/BVH vendored Normal file
View File

@@ -0,0 +1,95 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Ilya Baran <ibaran@mit.edu>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_BVH_MODULE_H
#define EIGEN_BVH_MODULE_H
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <Eigen/StdVector>
#include <algorithm>
#include <queue>
namespace Eigen {
/**
* \defgroup BVH_Module BVH module
* \brief This module provides generic bounding volume hierarchy algorithms
* and reference tree implementations.
*
*
* \code
* #include <unsupported/Eigen/BVH>
* \endcode
*
* A bounding volume hierarchy (BVH) can accelerate many geometric queries. This module provides a generic implementation
* of the two basic algorithms over a BVH: intersection of a query object against all objects in the hierarchy and minimization
* of a function over the objects in the hierarchy. It also provides intersection and minimization over a cartesian product of
* two BVH's. A BVH accelerates intersection by using the fact that if a query object does not intersect a volume, then it cannot
* intersect any object contained in that volume. Similarly, a BVH accelerates minimization because the minimum of a function
* over a volume is no greater than the minimum of a function over any object contained in it.
*
* Some sample queries that can be written in terms of intersection are:
* - Determine all points where a ray intersects a triangle mesh
* - Given a set of points, determine which are contained in a query sphere
* - Given a set of spheres, determine which contain the query point
* - Given a set of disks, determine if any is completely contained in a query rectangle (represent each 2D disk as a point \f$(x,y,r)\f$
* in 3D and represent the rectangle as a pyramid based on the original rectangle and shrinking in the \f$r\f$ direction)
* - Given a set of points, count how many pairs are \f$d\pm\epsilon\f$ apart (done by looking at the cartesian product of the set
* of points with itself)
*
* Some sample queries that can be written in terms of function minimization over a set of objects are:
* - Find the intersection between a ray and a triangle mesh closest to the ray origin (function is infinite off the ray)
* - Given a polyline and a query point, determine the closest point on the polyline to the query
* - Find the diameter of a point cloud (done by looking at the cartesian product and using negative distance as the function)
* - Determine how far two meshes are from colliding (this is also a cartesian product query)
*
* This implementation decouples the basic algorithms both from the type of hierarchy (and the types of the bounding volumes) and
* from the particulars of the query. To enable abstraction from the BVH, the BVH is required to implement a generic mechanism
* for traversal. To abstract from the query, the query is responsible for keeping track of results.
*
* To be used in the algorithms, a hierarchy must implement the following traversal mechanism (see KdBVH for a sample implementation): \code
typedef Volume //the type of bounding volume
typedef Object //the type of object in the hierarchy
typedef Index //a reference to a node in the hierarchy--typically an int or a pointer
typedef VolumeIterator //an iterator type over node children--returns Index
typedef ObjectIterator //an iterator over object (leaf) children--returns const Object &
Index getRootIndex() const //returns the index of the hierarchy root
const Volume &getVolume(Index index) const //returns the bounding volume of the node at given index
void getChildren(Index index, VolumeIterator &outVBegin, VolumeIterator &outVEnd,
ObjectIterator &outOBegin, ObjectIterator &outOEnd) const
//getChildren takes a node index and makes [outVBegin, outVEnd) range over its node children
//and [outOBegin, outOEnd) range over its object children
\endcode
*
* To use the hierarchy, call BVIntersect or BVMinimize, passing it a BVH (or two, for cartesian product) and a minimizer or intersector.
* For an intersection query on a single BVH, the intersector encapsulates the query and must provide two functions:
* \code
bool intersectVolume(const Volume &volume) //returns true if the query intersects the volume
bool intersectObject(const Object &object) //returns true if the intersection search should terminate immediately
\endcode
* The guarantee that BVIntersect provides is that intersectObject will be called on every object whose bounding volume
* intersects the query (but possibly on other objects too) unless the search is terminated prematurely. It is the
* responsibility of the intersectObject function to keep track of the results in whatever manner is appropriate.
* The cartesian product intersection and the BVMinimize queries are similar--see their individual documentation.
*
* The following is a simple but complete example for how to use the BVH to accelerate the search for a closest red-blue point pair:
* \include BVH_Example.cpp
* Output: \verbinclude BVH_Example.out
*/
}
//@{
#include "src/BVH/BVAlgorithms.h"
#include "src/BVH/KdBVH.h"
//@}
#endif // EIGEN_BVH_MODULE_H

View File

@@ -0,0 +1,11 @@
set(Eigen_HEADERS AdolcForward AlignedVector3 ArpackSupport AutoDiff BVH FFT IterativeSolvers KroneckerProduct LevenbergMarquardt
MatrixFunctions MoreVectorization MPRealSupport NonLinearOptimization NumericalDiff OpenGLSupport Polynomials
Skyline SparseExtra Splines
)
install(FILES
${Eigen_HEADERS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen COMPONENT Devel
)
add_subdirectory(src)

418
extern/Eigen3/unsupported/Eigen/FFT vendored Normal file
View File

@@ -0,0 +1,418 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Mark Borgerding mark a borgerding net
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_FFT_H
#define EIGEN_FFT_H
#include <complex>
#include <vector>
#include <map>
#include <Eigen/Core>
/**
* \defgroup FFT_Module Fast Fourier Transform module
*
* \code
* #include <unsupported/Eigen/FFT>
* \endcode
*
* This module provides Fast Fourier transformation, with a configurable backend
* implementation.
*
* The default implementation is based on kissfft. It is a small, free, and
* reasonably efficient default.
*
* There are currently two implementation backend:
*
* - fftw (http://www.fftw.org) : faster, GPL -- incompatible with Eigen in LGPL form, bigger code size.
* - MKL (http://en.wikipedia.org/wiki/Math_Kernel_Library) : fastest, commercial -- may be incompatible with Eigen in GPL form.
*
* \section FFTDesign Design
*
* The following design decisions were made concerning scaling and
* half-spectrum for real FFT.
*
* The intent is to facilitate generic programming and ease migrating code
* from Matlab/octave.
* We think the default behavior of Eigen/FFT should favor correctness and
* generality over speed. Of course, the caller should be able to "opt-out" from this
* behavior and get the speed increase if they want it.
*
* 1) %Scaling:
* Other libraries (FFTW,IMKL,KISSFFT) do not perform scaling, so there
* is a constant gain incurred after the forward&inverse transforms , so
* IFFT(FFT(x)) = Kx; this is done to avoid a vector-by-value multiply.
* The downside is that algorithms that worked correctly in Matlab/octave
* don't behave the same way once implemented in C++.
*
* How Eigen/FFT differs: invertible scaling is performed so IFFT( FFT(x) ) = x.
*
* 2) Real FFT half-spectrum
* Other libraries use only half the frequency spectrum (plus one extra
* sample for the Nyquist bin) for a real FFT, the other half is the
* conjugate-symmetric of the first half. This saves them a copy and some
* memory. The downside is the caller needs to have special logic for the
* number of bins in complex vs real.
*
* How Eigen/FFT differs: The full spectrum is returned from the forward
* transform. This facilitates generic template programming by obviating
* separate specializations for real vs complex. On the inverse
* transform, only half the spectrum is actually used if the output type is real.
*/
#ifdef EIGEN_FFTW_DEFAULT
// FFTW: faster, GPL -- incompatible with Eigen in LGPL form, bigger code size
# include <fftw3.h>
# include "src/FFT/ei_fftw_impl.h"
namespace Eigen {
//template <typename T> typedef struct internal::fftw_impl default_fft_impl; this does not work
template <typename T> struct default_fft_impl : public internal::fftw_impl<T> {};
}
#elif defined EIGEN_MKL_DEFAULT
// TODO
// intel Math Kernel Library: fastest, commercial -- may be incompatible with Eigen in GPL form
# include "src/FFT/ei_imklfft_impl.h"
namespace Eigen {
template <typename T> struct default_fft_impl : public internal::imklfft_impl {};
}
#else
// internal::kissfft_impl: small, free, reasonably efficient default, derived from kissfft
//
# include "src/FFT/ei_kissfft_impl.h"
namespace Eigen {
template <typename T>
struct default_fft_impl : public internal::kissfft_impl<T> {};
}
#endif
namespace Eigen {
//
template<typename T_SrcMat,typename T_FftIfc> struct fft_fwd_proxy;
template<typename T_SrcMat,typename T_FftIfc> struct fft_inv_proxy;
namespace internal {
template<typename T_SrcMat,typename T_FftIfc>
struct traits< fft_fwd_proxy<T_SrcMat,T_FftIfc> >
{
typedef typename T_SrcMat::PlainObject ReturnType;
};
template<typename T_SrcMat,typename T_FftIfc>
struct traits< fft_inv_proxy<T_SrcMat,T_FftIfc> >
{
typedef typename T_SrcMat::PlainObject ReturnType;
};
}
template<typename T_SrcMat,typename T_FftIfc>
struct fft_fwd_proxy
: public ReturnByValue<fft_fwd_proxy<T_SrcMat,T_FftIfc> >
{
typedef DenseIndex Index;
fft_fwd_proxy(const T_SrcMat& src,T_FftIfc & fft, Index nfft) : m_src(src),m_ifc(fft), m_nfft(nfft) {}
template<typename T_DestMat> void evalTo(T_DestMat& dst) const;
Index rows() const { return m_src.rows(); }
Index cols() const { return m_src.cols(); }
protected:
const T_SrcMat & m_src;
T_FftIfc & m_ifc;
Index m_nfft;
private:
fft_fwd_proxy& operator=(const fft_fwd_proxy&);
};
template<typename T_SrcMat,typename T_FftIfc>
struct fft_inv_proxy
: public ReturnByValue<fft_inv_proxy<T_SrcMat,T_FftIfc> >
{
typedef DenseIndex Index;
fft_inv_proxy(const T_SrcMat& src,T_FftIfc & fft, Index nfft) : m_src(src),m_ifc(fft), m_nfft(nfft) {}
template<typename T_DestMat> void evalTo(T_DestMat& dst) const;
Index rows() const { return m_src.rows(); }
Index cols() const { return m_src.cols(); }
protected:
const T_SrcMat & m_src;
T_FftIfc & m_ifc;
Index m_nfft;
private:
fft_inv_proxy& operator=(const fft_inv_proxy&);
};
template <typename T_Scalar,
typename T_Impl=default_fft_impl<T_Scalar> >
class FFT
{
public:
typedef T_Impl impl_type;
typedef DenseIndex Index;
typedef typename impl_type::Scalar Scalar;
typedef typename impl_type::Complex Complex;
enum Flag {
Default=0, // goof proof
Unscaled=1,
HalfSpectrum=2,
// SomeOtherSpeedOptimization=4
Speedy=32767
};
FFT( const impl_type & impl=impl_type() , Flag flags=Default ) :m_impl(impl),m_flag(flags) { }
inline
bool HasFlag(Flag f) const { return (m_flag & (int)f) == f;}
inline
void SetFlag(Flag f) { m_flag |= (int)f;}
inline
void ClearFlag(Flag f) { m_flag &= (~(int)f);}
inline
void fwd( Complex * dst, const Scalar * src, Index nfft)
{
m_impl.fwd(dst,src,static_cast<int>(nfft));
if ( HasFlag(HalfSpectrum) == false)
ReflectSpectrum(dst,nfft);
}
inline
void fwd( Complex * dst, const Complex * src, Index nfft)
{
m_impl.fwd(dst,src,static_cast<int>(nfft));
}
/*
inline
void fwd2(Complex * dst, const Complex * src, int n0,int n1)
{
m_impl.fwd2(dst,src,n0,n1);
}
*/
template <typename _Input>
inline
void fwd( std::vector<Complex> & dst, const std::vector<_Input> & src)
{
if ( NumTraits<_Input>::IsComplex == 0 && HasFlag(HalfSpectrum) )
dst.resize( (src.size()>>1)+1); // half the bins + Nyquist bin
else
dst.resize(src.size());
fwd(&dst[0],&src[0],src.size());
}
template<typename InputDerived, typename ComplexDerived>
inline
void fwd( MatrixBase<ComplexDerived> & dst, const MatrixBase<InputDerived> & src, Index nfft=-1)
{
typedef typename ComplexDerived::Scalar dst_type;
typedef typename InputDerived::Scalar src_type;
EIGEN_STATIC_ASSERT_VECTOR_ONLY(InputDerived)
EIGEN_STATIC_ASSERT_VECTOR_ONLY(ComplexDerived)
EIGEN_STATIC_ASSERT_SAME_VECTOR_SIZE(ComplexDerived,InputDerived) // size at compile-time
EIGEN_STATIC_ASSERT((internal::is_same<dst_type, Complex>::value),
YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
EIGEN_STATIC_ASSERT(int(InputDerived::Flags)&int(ComplexDerived::Flags)&DirectAccessBit,
THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_WITH_DIRECT_MEMORY_ACCESS_SUCH_AS_MAP_OR_PLAIN_MATRICES)
if (nfft<1)
nfft = src.size();
if ( NumTraits< src_type >::IsComplex == 0 && HasFlag(HalfSpectrum) )
dst.derived().resize( (nfft>>1)+1);
else
dst.derived().resize(nfft);
if ( src.innerStride() != 1 || src.size() < nfft ) {
Matrix<src_type,1,Dynamic> tmp;
if (src.size()<nfft) {
tmp.setZero(nfft);
tmp.block(0,0,src.size(),1 ) = src;
}else{
tmp = src;
}
fwd( &dst[0],&tmp[0],nfft );
}else{
fwd( &dst[0],&src[0],nfft );
}
}
template<typename InputDerived>
inline
fft_fwd_proxy< MatrixBase<InputDerived>, FFT<T_Scalar,T_Impl> >
fwd( const MatrixBase<InputDerived> & src, Index nfft=-1)
{
return fft_fwd_proxy< MatrixBase<InputDerived> ,FFT<T_Scalar,T_Impl> >( src, *this,nfft );
}
template<typename InputDerived>
inline
fft_inv_proxy< MatrixBase<InputDerived>, FFT<T_Scalar,T_Impl> >
inv( const MatrixBase<InputDerived> & src, Index nfft=-1)
{
return fft_inv_proxy< MatrixBase<InputDerived> ,FFT<T_Scalar,T_Impl> >( src, *this,nfft );
}
inline
void inv( Complex * dst, const Complex * src, Index nfft)
{
m_impl.inv( dst,src,static_cast<int>(nfft) );
if ( HasFlag( Unscaled ) == false)
scale(dst,Scalar(1./nfft),nfft); // scale the time series
}
inline
void inv( Scalar * dst, const Complex * src, Index nfft)
{
m_impl.inv( dst,src,static_cast<int>(nfft) );
if ( HasFlag( Unscaled ) == false)
scale(dst,Scalar(1./nfft),nfft); // scale the time series
}
template<typename OutputDerived, typename ComplexDerived>
inline
void inv( MatrixBase<OutputDerived> & dst, const MatrixBase<ComplexDerived> & src, Index nfft=-1)
{
typedef typename ComplexDerived::Scalar src_type;
typedef typename OutputDerived::Scalar dst_type;
const bool realfft= (NumTraits<dst_type>::IsComplex == 0);
EIGEN_STATIC_ASSERT_VECTOR_ONLY(OutputDerived)
EIGEN_STATIC_ASSERT_VECTOR_ONLY(ComplexDerived)
EIGEN_STATIC_ASSERT_SAME_VECTOR_SIZE(ComplexDerived,OutputDerived) // size at compile-time
EIGEN_STATIC_ASSERT((internal::is_same<src_type, Complex>::value),
YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
EIGEN_STATIC_ASSERT(int(OutputDerived::Flags)&int(ComplexDerived::Flags)&DirectAccessBit,
THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_WITH_DIRECT_MEMORY_ACCESS_SUCH_AS_MAP_OR_PLAIN_MATRICES)
if (nfft<1) { //automatic FFT size determination
if ( realfft && HasFlag(HalfSpectrum) )
nfft = 2*(src.size()-1); //assume even fft size
else
nfft = src.size();
}
dst.derived().resize( nfft );
// check for nfft that does not fit the input data size
Index resize_input= ( realfft && HasFlag(HalfSpectrum) )
? ( (nfft/2+1) - src.size() )
: ( nfft - src.size() );
if ( src.innerStride() != 1 || resize_input ) {
// if the vector is strided, then we need to copy it to a packed temporary
Matrix<src_type,1,Dynamic> tmp;
if ( resize_input ) {
size_t ncopy = (std::min)(src.size(),src.size() + resize_input);
tmp.setZero(src.size() + resize_input);
if ( realfft && HasFlag(HalfSpectrum) ) {
// pad at the Nyquist bin
tmp.head(ncopy) = src.head(ncopy);
tmp(ncopy-1) = real(tmp(ncopy-1)); // enforce real-only Nyquist bin
}else{
size_t nhead,ntail;
nhead = 1+ncopy/2-1; // range [0:pi)
ntail = ncopy/2-1; // range (-pi:0)
tmp.head(nhead) = src.head(nhead);
tmp.tail(ntail) = src.tail(ntail);
if (resize_input<0) { //shrinking -- create the Nyquist bin as the average of the two bins that fold into it
tmp(nhead) = ( src(nfft/2) + src( src.size() - nfft/2 ) )*src_type(.5);
}else{ // expanding -- split the old Nyquist bin into two halves
tmp(nhead) = src(nhead) * src_type(.5);
tmp(tmp.size()-nhead) = tmp(nhead);
}
}
}else{
tmp = src;
}
inv( &dst[0],&tmp[0], nfft);
}else{
inv( &dst[0],&src[0], nfft);
}
}
template <typename _Output>
inline
void inv( std::vector<_Output> & dst, const std::vector<Complex> & src,Index nfft=-1)
{
if (nfft<1)
nfft = ( NumTraits<_Output>::IsComplex == 0 && HasFlag(HalfSpectrum) ) ? 2*(src.size()-1) : src.size();
dst.resize( nfft );
inv( &dst[0],&src[0],nfft);
}
/*
// TODO: multi-dimensional FFTs
inline
void inv2(Complex * dst, const Complex * src, int n0,int n1)
{
m_impl.inv2(dst,src,n0,n1);
if ( HasFlag( Unscaled ) == false)
scale(dst,1./(n0*n1),n0*n1);
}
*/
inline
impl_type & impl() {return m_impl;}
private:
template <typename T_Data>
inline
void scale(T_Data * x,Scalar s,Index nx)
{
#if 1
for (int k=0;k<nx;++k)
*x++ *= s;
#else
if ( ((ptrdiff_t)x) & 15 )
Matrix<T_Data, Dynamic, 1>::Map(x,nx) *= s;
else
Matrix<T_Data, Dynamic, 1>::MapAligned(x,nx) *= s;
//Matrix<T_Data, Dynamic, Dynamic>::Map(x,nx) * s;
#endif
}
inline
void ReflectSpectrum(Complex * freq, Index nfft)
{
// create the implicit right-half spectrum (conjugate-mirror of the left-half)
Index nhbins=(nfft>>1)+1;
for (Index k=nhbins;k < nfft; ++k )
freq[k] = conj(freq[nfft-k]);
}
impl_type m_impl;
int m_flag;
};
template<typename T_SrcMat,typename T_FftIfc>
template<typename T_DestMat> inline
void fft_fwd_proxy<T_SrcMat,T_FftIfc>::evalTo(T_DestMat& dst) const
{
m_ifc.fwd( dst, m_src, m_nfft);
}
template<typename T_SrcMat,typename T_FftIfc>
template<typename T_DestMat> inline
void fft_inv_proxy<T_SrcMat,T_FftIfc>::evalTo(T_DestMat& dst) const
{
m_ifc.inv( dst, m_src, m_nfft);
}
}
#endif
/* vim: set filetype=cpp et sw=2 ts=2 ai: */

View File

@@ -0,0 +1,45 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <g.gael@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_ITERATIVE_SOLVERS_MODULE_H
#define EIGEN_ITERATIVE_SOLVERS_MODULE_H
#include <Eigen/Sparse>
/**
* \defgroup IterativeSolvers_Module Iterative solvers module
* This module aims to provide various iterative linear and non linear solver algorithms.
* It currently provides:
* - a constrained conjugate gradient
* - a Householder GMRES implementation
* \code
* #include <unsupported/Eigen/IterativeSolvers>
* \endcode
*/
//@{
#include "../../Eigen/src/misc/Solve.h"
#include "../../Eigen/src/misc/SparseSolve.h"
#ifndef EIGEN_MPL2_ONLY
#include "src/IterativeSolvers/IterationController.h"
#include "src/IterativeSolvers/ConstrainedConjGrad.h"
#endif
#include "src/IterativeSolvers/IncompleteLU.h"
#include "../../Eigen/Jacobi"
#include "../../Eigen/Householder"
#include "src/IterativeSolvers/GMRES.h"
#include "src/IterativeSolvers/IncompleteCholesky.h"
//#include "src/IterativeSolvers/SSORPreconditioner.h"
#include "src/IterativeSolvers/MINRES.h"
//@}
#endif // EIGEN_ITERATIVE_SOLVERS_MODULE_H

View File

@@ -0,0 +1,34 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_KRONECKER_PRODUCT_MODULE_H
#define EIGEN_KRONECKER_PRODUCT_MODULE_H
#include "../../Eigen/Core"
#include "../../Eigen/src/Core/util/DisableStupidWarnings.h"
namespace Eigen {
/**
* \defgroup KroneckerProduct_Module KroneckerProduct module
*
* This module contains an experimental Kronecker product implementation.
*
* \code
* #include <Eigen/KroneckerProduct>
* \endcode
*/
} // namespace Eigen
#include "src/KroneckerProduct/KroneckerTensorProduct.h"
#include "../../Eigen/src/Core/util/ReenableStupidWarnings.h"
#endif // EIGEN_KRONECKER_PRODUCT_MODULE_H

View File

@@ -0,0 +1,45 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Thomas Capricelli <orzel@freehackers.org>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_LEVENBERGMARQUARDT_MODULE
#define EIGEN_LEVENBERGMARQUARDT_MODULE
// #include <vector>
#include <Eigen/Core>
#include <Eigen/Jacobi>
#include <Eigen/QR>
#include <unsupported/Eigen/NumericalDiff>
#include <Eigen/SparseQR>
/**
* \defgroup LevenbergMarquardt_Module Levenberg-Marquardt module
*
* \code
* #include </Eigen/LevenbergMarquardt>
* \endcode
*
*
*/
#include "Eigen/SparseCore"
#ifndef EIGEN_PARSED_BY_DOXYGEN
#include "src/LevenbergMarquardt/LMqrsolv.h"
#include "src/LevenbergMarquardt/LMcovar.h"
#include "src/LevenbergMarquardt/LMpar.h"
#endif
#include "src/LevenbergMarquardt/LevenbergMarquardt.h"
#include "src/LevenbergMarquardt/LMonestep.h"
#endif // EIGEN_LEVENBERGMARQUARDT_MODULE

View File

@@ -0,0 +1,203 @@
// This file is part of a joint effort between Eigen, a lightweight C++ template library
// for linear algebra, and MPFR C++, a C++ interface to MPFR library (http://www.holoborodko.com/pavel/)
//
// Copyright (C) 2010-2012 Pavel Holoborodko <pavel@holoborodko.com>
// Copyright (C) 2010 Konstantin Holoborodko <konstantin@holoborodko.com>
// Copyright (C) 2010 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_MPREALSUPPORT_MODULE_H
#define EIGEN_MPREALSUPPORT_MODULE_H
#include <Eigen/Core>
#include <mpreal.h>
namespace Eigen {
/**
* \defgroup MPRealSupport_Module MPFRC++ Support module
* \code
* #include <Eigen/MPRealSupport>
* \endcode
*
* This module provides support for multi precision floating point numbers
* via the <a href="http://www.holoborodko.com/pavel/mpfr">MPFR C++</a>
* library which itself is built upon <a href="http://www.mpfr.org/">MPFR</a>/<a href="http://gmplib.org/">GMP</a>.
*
* You can find a copy of MPFR C++ that is known to be compatible in the unsupported/test/mpreal folder.
*
* Here is an example:
*
\code
#include <iostream>
#include <Eigen/MPRealSupport>
#include <Eigen/LU>
using namespace mpfr;
using namespace Eigen;
int main()
{
// set precision to 256 bits (double has only 53 bits)
mpreal::set_default_prec(256);
// Declare matrix and vector types with multi-precision scalar type
typedef Matrix<mpreal,Dynamic,Dynamic> MatrixXmp;
typedef Matrix<mpreal,Dynamic,1> VectorXmp;
MatrixXmp A = MatrixXmp::Random(100,100);
VectorXmp b = VectorXmp::Random(100);
// Solve Ax=b using LU
VectorXmp x = A.lu().solve(b);
std::cout << "relative error: " << (A*x - b).norm() / b.norm() << std::endl;
return 0;
}
\endcode
*
*/
template<> struct NumTraits<mpfr::mpreal>
: GenericNumTraits<mpfr::mpreal>
{
enum {
IsInteger = 0,
IsSigned = 1,
IsComplex = 0,
RequireInitialization = 1,
ReadCost = 10,
AddCost = 10,
MulCost = 40
};
typedef mpfr::mpreal Real;
typedef mpfr::mpreal NonInteger;
inline static Real highest (long Precision = mpfr::mpreal::get_default_prec()) { return mpfr::maxval(Precision); }
inline static Real lowest (long Precision = mpfr::mpreal::get_default_prec()) { return -mpfr::maxval(Precision); }
// Constants
inline static Real Pi (long Precision = mpfr::mpreal::get_default_prec()) { return mpfr::const_pi(Precision); }
inline static Real Euler (long Precision = mpfr::mpreal::get_default_prec()) { return mpfr::const_euler(Precision); }
inline static Real Log2 (long Precision = mpfr::mpreal::get_default_prec()) { return mpfr::const_log2(Precision); }
inline static Real Catalan (long Precision = mpfr::mpreal::get_default_prec()) { return mpfr::const_catalan(Precision); }
inline static Real epsilon (long Precision = mpfr::mpreal::get_default_prec()) { return mpfr::machine_epsilon(Precision); }
inline static Real epsilon (const Real& x) { return mpfr::machine_epsilon(x); }
inline static Real dummy_precision()
{
unsigned int weak_prec = ((mpfr::mpreal::get_default_prec()-1) * 90) / 100;
return mpfr::machine_epsilon(weak_prec);
}
};
namespace internal {
template<> inline mpfr::mpreal random<mpfr::mpreal>()
{
return mpfr::random();
}
template<> inline mpfr::mpreal random<mpfr::mpreal>(const mpfr::mpreal& a, const mpfr::mpreal& b)
{
return a + (b-a) * random<mpfr::mpreal>();
}
inline bool isMuchSmallerThan(const mpfr::mpreal& a, const mpfr::mpreal& b, const mpfr::mpreal& eps)
{
return mpfr::abs(a) <= mpfr::abs(b) * eps;
}
inline bool isApprox(const mpfr::mpreal& a, const mpfr::mpreal& b, const mpfr::mpreal& eps)
{
return mpfr::isEqualFuzzy(a,b,eps);
}
inline bool isApproxOrLessThan(const mpfr::mpreal& a, const mpfr::mpreal& b, const mpfr::mpreal& eps)
{
return a <= b || mpfr::isEqualFuzzy(a,b,eps);
}
template<> inline long double cast<mpfr::mpreal,long double>(const mpfr::mpreal& x)
{ return x.toLDouble(); }
template<> inline double cast<mpfr::mpreal,double>(const mpfr::mpreal& x)
{ return x.toDouble(); }
template<> inline long cast<mpfr::mpreal,long>(const mpfr::mpreal& x)
{ return x.toLong(); }
template<> inline int cast<mpfr::mpreal,int>(const mpfr::mpreal& x)
{ return int(x.toLong()); }
// Specialize GEBP kernel and traits for mpreal (no need for peeling, nor complicated stuff)
// This also permits to directly call mpfr's routines and avoid many temporaries produced by mpreal
template<>
class gebp_traits<mpfr::mpreal, mpfr::mpreal, false, false>
{
public:
typedef mpfr::mpreal ResScalar;
enum {
nr = 2, // must be 2 for proper packing...
mr = 1,
WorkSpaceFactor = nr,
LhsProgress = 1,
RhsProgress = 1
};
};
template<typename Index, int mr, int nr, bool ConjugateLhs, bool ConjugateRhs>
struct gebp_kernel<mpfr::mpreal,mpfr::mpreal,Index,mr,nr,ConjugateLhs,ConjugateRhs>
{
typedef mpfr::mpreal mpreal;
EIGEN_DONT_INLINE
void operator()(mpreal* res, Index resStride, const mpreal* blockA, const mpreal* blockB, Index rows, Index depth, Index cols, mpreal alpha,
Index strideA=-1, Index strideB=-1, Index offsetA=0, Index offsetB=0, mpreal* /*unpackedB*/ = 0)
{
mpreal acc1, acc2, tmp;
if(strideA==-1) strideA = depth;
if(strideB==-1) strideB = depth;
for(Index j=0; j<cols; j+=nr)
{
Index actual_nr = (std::min<Index>)(nr,cols-j);
mpreal *C1 = res + j*resStride;
mpreal *C2 = res + (j+1)*resStride;
for(Index i=0; i<rows; i++)
{
mpreal *B = const_cast<mpreal*>(blockB) + j*strideB + offsetB*actual_nr;
mpreal *A = const_cast<mpreal*>(blockA) + i*strideA + offsetA;
acc1 = 0;
acc2 = 0;
for(Index k=0; k<depth; k++)
{
mpfr_mul(tmp.mpfr_ptr(), A[k].mpfr_ptr(), B[0].mpfr_ptr(), mpreal::get_default_rnd());
mpfr_add(acc1.mpfr_ptr(), acc1.mpfr_ptr(), tmp.mpfr_ptr(), mpreal::get_default_rnd());
if(actual_nr==2) {
mpfr_mul(tmp.mpfr_ptr(), A[k].mpfr_ptr(), B[1].mpfr_ptr(), mpreal::get_default_rnd());
mpfr_add(acc2.mpfr_ptr(), acc2.mpfr_ptr(), tmp.mpfr_ptr(), mpreal::get_default_rnd());
}
B+=actual_nr;
}
mpfr_mul(acc1.mpfr_ptr(), acc1.mpfr_ptr(), alpha.mpfr_ptr(), mpreal::get_default_rnd());
mpfr_add(C1[i].mpfr_ptr(), C1[i].mpfr_ptr(), acc1.mpfr_ptr(), mpreal::get_default_rnd());
if(actual_nr==2) {
mpfr_mul(acc2.mpfr_ptr(), acc2.mpfr_ptr(), alpha.mpfr_ptr(), mpreal::get_default_rnd());
mpfr_add(C2[i].mpfr_ptr(), C2[i].mpfr_ptr(), acc2.mpfr_ptr(), mpreal::get_default_rnd());
}
}
}
}
};
} // end namespace internal
}
#endif // EIGEN_MPREALSUPPORT_MODULE_H

View File

@@ -0,0 +1,447 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Jitse Niesen <jitse@maths.leeds.ac.uk>
// Copyright (C) 2012 Chen-Pang He <jdh8@ms63.hinet.net>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_MATRIX_FUNCTIONS
#define EIGEN_MATRIX_FUNCTIONS
#include <cfloat>
#include <list>
#include <functional>
#include <iterator>
#include <Eigen/Core>
#include <Eigen/LU>
#include <Eigen/Eigenvalues>
/**
* \defgroup MatrixFunctions_Module Matrix functions module
* \brief This module aims to provide various methods for the computation of
* matrix functions.
*
* To use this module, add
* \code
* #include <unsupported/Eigen/MatrixFunctions>
* \endcode
* at the start of your source file.
*
* This module defines the following MatrixBase methods.
* - \ref matrixbase_cos "MatrixBase::cos()", for computing the matrix cosine
* - \ref matrixbase_cosh "MatrixBase::cosh()", for computing the matrix hyperbolic cosine
* - \ref matrixbase_exp "MatrixBase::exp()", for computing the matrix exponential
* - \ref matrixbase_log "MatrixBase::log()", for computing the matrix logarithm
* - \ref matrixbase_pow "MatrixBase::pow()", for computing the matrix power
* - \ref matrixbase_matrixfunction "MatrixBase::matrixFunction()", for computing general matrix functions
* - \ref matrixbase_sin "MatrixBase::sin()", for computing the matrix sine
* - \ref matrixbase_sinh "MatrixBase::sinh()", for computing the matrix hyperbolic sine
* - \ref matrixbase_sqrt "MatrixBase::sqrt()", for computing the matrix square root
*
* These methods are the main entry points to this module.
*
* %Matrix functions are defined as follows. Suppose that \f$ f \f$
* is an entire function (that is, a function on the complex plane
* that is everywhere complex differentiable). Then its Taylor
* series
* \f[ f(0) + f'(0) x + \frac{f''(0)}{2} x^2 + \frac{f'''(0)}{3!} x^3 + \cdots \f]
* converges to \f$ f(x) \f$. In this case, we can define the matrix
* function by the same series:
* \f[ f(M) = f(0) + f'(0) M + \frac{f''(0)}{2} M^2 + \frac{f'''(0)}{3!} M^3 + \cdots \f]
*
*/
#include "src/MatrixFunctions/MatrixExponential.h"
#include "src/MatrixFunctions/MatrixFunction.h"
#include "src/MatrixFunctions/MatrixSquareRoot.h"
#include "src/MatrixFunctions/MatrixLogarithm.h"
#include "src/MatrixFunctions/MatrixPower.h"
/**
\page matrixbaseextra_page
\ingroup MatrixFunctions_Module
\section matrixbaseextra MatrixBase methods defined in the MatrixFunctions module
The remainder of the page documents the following MatrixBase methods
which are defined in the MatrixFunctions module.
\subsection matrixbase_cos MatrixBase::cos()
Compute the matrix cosine.
\code
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cos() const
\endcode
\param[in] M a square matrix.
\returns expression representing \f$ \cos(M) \f$.
This function calls \ref matrixbase_matrixfunction "matrixFunction()" with StdStemFunctions::cos().
\sa \ref matrixbase_sin "sin()" for an example.
\subsection matrixbase_cosh MatrixBase::cosh()
Compute the matrix hyberbolic cosine.
\code
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cosh() const
\endcode
\param[in] M a square matrix.
\returns expression representing \f$ \cosh(M) \f$
This function calls \ref matrixbase_matrixfunction "matrixFunction()" with StdStemFunctions::cosh().
\sa \ref matrixbase_sinh "sinh()" for an example.
\subsection matrixbase_exp MatrixBase::exp()
Compute the matrix exponential.
\code
const MatrixExponentialReturnValue<Derived> MatrixBase<Derived>::exp() const
\endcode
\param[in] M matrix whose exponential is to be computed.
\returns expression representing the matrix exponential of \p M.
The matrix exponential of \f$ M \f$ is defined by
\f[ \exp(M) = \sum_{k=0}^\infty \frac{M^k}{k!}. \f]
The matrix exponential can be used to solve linear ordinary
differential equations: the solution of \f$ y' = My \f$ with the
initial condition \f$ y(0) = y_0 \f$ is given by
\f$ y(t) = \exp(M) y_0 \f$.
The cost of the computation is approximately \f$ 20 n^3 \f$ for
matrices of size \f$ n \f$. The number 20 depends weakly on the
norm of the matrix.
The matrix exponential is computed using the scaling-and-squaring
method combined with Pad&eacute; approximation. The matrix is first
rescaled, then the exponential of the reduced matrix is computed
approximant, and then the rescaling is undone by repeated
squaring. The degree of the Pad&eacute; approximant is chosen such
that the approximation error is less than the round-off
error. However, errors may accumulate during the squaring phase.
Details of the algorithm can be found in: Nicholas J. Higham, "The
scaling and squaring method for the matrix exponential revisited,"
<em>SIAM J. %Matrix Anal. Applic.</em>, <b>26</b>:1179&ndash;1193,
2005.
Example: The following program checks that
\f[ \exp \left[ \begin{array}{ccc}
0 & \frac14\pi & 0 \\
-\frac14\pi & 0 & 0 \\
0 & 0 & 0
\end{array} \right] = \left[ \begin{array}{ccc}
\frac12\sqrt2 & -\frac12\sqrt2 & 0 \\
\frac12\sqrt2 & \frac12\sqrt2 & 0 \\
0 & 0 & 1
\end{array} \right]. \f]
This corresponds to a rotation of \f$ \frac14\pi \f$ radians around
the z-axis.
\include MatrixExponential.cpp
Output: \verbinclude MatrixExponential.out
\note \p M has to be a matrix of \c float, \c double, \c long double
\c complex<float>, \c complex<double>, or \c complex<long double> .
\subsection matrixbase_log MatrixBase::log()
Compute the matrix logarithm.
\code
const MatrixLogarithmReturnValue<Derived> MatrixBase<Derived>::log() const
\endcode
\param[in] M invertible matrix whose logarithm is to be computed.
\returns expression representing the matrix logarithm root of \p M.
The matrix logarithm of \f$ M \f$ is a matrix \f$ X \f$ such that
\f$ \exp(X) = M \f$ where exp denotes the matrix exponential. As for
the scalar logarithm, the equation \f$ \exp(X) = M \f$ may have
multiple solutions; this function returns a matrix whose eigenvalues
have imaginary part in the interval \f$ (-\pi,\pi] \f$.
In the real case, the matrix \f$ M \f$ should be invertible and
it should have no eigenvalues which are real and negative (pairs of
complex conjugate eigenvalues are allowed). In the complex case, it
only needs to be invertible.
This function computes the matrix logarithm using the Schur-Parlett
algorithm as implemented by MatrixBase::matrixFunction(). The
logarithm of an atomic block is computed by MatrixLogarithmAtomic,
which uses direct computation for 1-by-1 and 2-by-2 blocks and an
inverse scaling-and-squaring algorithm for bigger blocks, with the
square roots computed by MatrixBase::sqrt().
Details of the algorithm can be found in Section 11.6.2 of:
Nicholas J. Higham,
<em>Functions of Matrices: Theory and Computation</em>,
SIAM 2008. ISBN 978-0-898716-46-7.
Example: The following program checks that
\f[ \log \left[ \begin{array}{ccc}
\frac12\sqrt2 & -\frac12\sqrt2 & 0 \\
\frac12\sqrt2 & \frac12\sqrt2 & 0 \\
0 & 0 & 1
\end{array} \right] = \left[ \begin{array}{ccc}
0 & \frac14\pi & 0 \\
-\frac14\pi & 0 & 0 \\
0 & 0 & 0
\end{array} \right]. \f]
This corresponds to a rotation of \f$ \frac14\pi \f$ radians around
the z-axis. This is the inverse of the example used in the
documentation of \ref matrixbase_exp "exp()".
\include MatrixLogarithm.cpp
Output: \verbinclude MatrixLogarithm.out
\note \p M has to be a matrix of \c float, \c double, <tt>long
double</tt>, \c complex<float>, \c complex<double>, or \c complex<long
double> .
\sa MatrixBase::exp(), MatrixBase::matrixFunction(),
class MatrixLogarithmAtomic, MatrixBase::sqrt().
\subsection matrixbase_pow MatrixBase::pow()
Compute the matrix raised to arbitrary real power.
\code
const MatrixPowerReturnValue<Derived> MatrixBase<Derived>::pow(RealScalar p) const
\endcode
\param[in] M base of the matrix power, should be a square matrix.
\param[in] p exponent of the matrix power, should be real.
The matrix power \f$ M^p \f$ is defined as \f$ \exp(p \log(M)) \f$,
where exp denotes the matrix exponential, and log denotes the matrix
logarithm.
The matrix \f$ M \f$ should meet the conditions to be an argument of
matrix logarithm. If \p p is not of the real scalar type of \p M, it
is casted into the real scalar type of \p M.
This function computes the matrix power using the Schur-Pad&eacute;
algorithm as implemented by class MatrixPower. The exponent is split
into integral part and fractional part, where the fractional part is
in the interval \f$ (-1, 1) \f$. The main diagonal and the first
super-diagonal is directly computed.
Details of the algorithm can be found in: Nicholas J. Higham and
Lijing Lin, "A Schur-Pad&eacute; algorithm for fractional powers of a
matrix," <em>SIAM J. %Matrix Anal. Applic.</em>,
<b>32(3)</b>:1056&ndash;1078, 2011.
Example: The following program checks that
\f[ \left[ \begin{array}{ccc}
\cos1 & -\sin1 & 0 \\
\sin1 & \cos1 & 0 \\
0 & 0 & 1
\end{array} \right]^{\frac14\pi} = \left[ \begin{array}{ccc}
\frac12\sqrt2 & -\frac12\sqrt2 & 0 \\
\frac12\sqrt2 & \frac12\sqrt2 & 0 \\
0 & 0 & 1
\end{array} \right]. \f]
This corresponds to \f$ \frac14\pi \f$ rotations of 1 radian around
the z-axis.
\include MatrixPower.cpp
Output: \verbinclude MatrixPower.out
MatrixBase::pow() is user-friendly. However, there are some
circumstances under which you should use class MatrixPower directly.
MatrixPower can save the result of Schur decomposition, so it's
better for computing various powers for the same matrix.
Example:
\include MatrixPower_optimal.cpp
Output: \verbinclude MatrixPower_optimal.out
\note \p M has to be a matrix of \c float, \c double, <tt>long
double</tt>, \c complex<float>, \c complex<double>, or \c complex<long
double> .
\sa MatrixBase::exp(), MatrixBase::log(), class MatrixPower.
\subsection matrixbase_matrixfunction MatrixBase::matrixFunction()
Compute a matrix function.
\code
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::matrixFunction(typename internal::stem_function<typename internal::traits<Derived>::Scalar>::type f) const
\endcode
\param[in] M argument of matrix function, should be a square matrix.
\param[in] f an entire function; \c f(x,n) should compute the n-th
derivative of f at x.
\returns expression representing \p f applied to \p M.
Suppose that \p M is a matrix whose entries have type \c Scalar.
Then, the second argument, \p f, should be a function with prototype
\code
ComplexScalar f(ComplexScalar, int)
\endcode
where \c ComplexScalar = \c std::complex<Scalar> if \c Scalar is
real (e.g., \c float or \c double) and \c ComplexScalar =
\c Scalar if \c Scalar is complex. The return value of \c f(x,n)
should be \f$ f^{(n)}(x) \f$, the n-th derivative of f at x.
This routine uses the algorithm described in:
Philip Davies and Nicholas J. Higham,
"A Schur-Parlett algorithm for computing matrix functions",
<em>SIAM J. %Matrix Anal. Applic.</em>, <b>25</b>:464&ndash;485, 2003.
The actual work is done by the MatrixFunction class.
Example: The following program checks that
\f[ \exp \left[ \begin{array}{ccc}
0 & \frac14\pi & 0 \\
-\frac14\pi & 0 & 0 \\
0 & 0 & 0
\end{array} \right] = \left[ \begin{array}{ccc}
\frac12\sqrt2 & -\frac12\sqrt2 & 0 \\
\frac12\sqrt2 & \frac12\sqrt2 & 0 \\
0 & 0 & 1
\end{array} \right]. \f]
This corresponds to a rotation of \f$ \frac14\pi \f$ radians around
the z-axis. This is the same example as used in the documentation
of \ref matrixbase_exp "exp()".
\include MatrixFunction.cpp
Output: \verbinclude MatrixFunction.out
Note that the function \c expfn is defined for complex numbers
\c x, even though the matrix \c A is over the reals. Instead of
\c expfn, we could also have used StdStemFunctions::exp:
\code
A.matrixFunction(StdStemFunctions<std::complex<double> >::exp, &B);
\endcode
\subsection matrixbase_sin MatrixBase::sin()
Compute the matrix sine.
\code
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sin() const
\endcode
\param[in] M a square matrix.
\returns expression representing \f$ \sin(M) \f$.
This function calls \ref matrixbase_matrixfunction "matrixFunction()" with StdStemFunctions::sin().
Example: \include MatrixSine.cpp
Output: \verbinclude MatrixSine.out
\subsection matrixbase_sinh MatrixBase::sinh()
Compute the matrix hyperbolic sine.
\code
MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sinh() const
\endcode
\param[in] M a square matrix.
\returns expression representing \f$ \sinh(M) \f$
This function calls \ref matrixbase_matrixfunction "matrixFunction()" with StdStemFunctions::sinh().
Example: \include MatrixSinh.cpp
Output: \verbinclude MatrixSinh.out
\subsection matrixbase_sqrt MatrixBase::sqrt()
Compute the matrix square root.
\code
const MatrixSquareRootReturnValue<Derived> MatrixBase<Derived>::sqrt() const
\endcode
\param[in] M invertible matrix whose square root is to be computed.
\returns expression representing the matrix square root of \p M.
The matrix square root of \f$ M \f$ is the matrix \f$ M^{1/2} \f$
whose square is the original matrix; so if \f$ S = M^{1/2} \f$ then
\f$ S^2 = M \f$.
In the <b>real case</b>, the matrix \f$ M \f$ should be invertible and
it should have no eigenvalues which are real and negative (pairs of
complex conjugate eigenvalues are allowed). In that case, the matrix
has a square root which is also real, and this is the square root
computed by this function.
The matrix square root is computed by first reducing the matrix to
quasi-triangular form with the real Schur decomposition. The square
root of the quasi-triangular matrix can then be computed directly. The
cost is approximately \f$ 25 n^3 \f$ real flops for the real Schur
decomposition and \f$ 3\frac13 n^3 \f$ real flops for the remainder
(though the computation time in practice is likely more than this
indicates).
Details of the algorithm can be found in: Nicholas J. Highan,
"Computing real square roots of a real matrix", <em>Linear Algebra
Appl.</em>, 88/89:405&ndash;430, 1987.
If the matrix is <b>positive-definite symmetric</b>, then the square
root is also positive-definite symmetric. In this case, it is best to
use SelfAdjointEigenSolver::operatorSqrt() to compute it.
In the <b>complex case</b>, the matrix \f$ M \f$ should be invertible;
this is a restriction of the algorithm. The square root computed by
this algorithm is the one whose eigenvalues have an argument in the
interval \f$ (-\frac12\pi, \frac12\pi] \f$. This is the usual branch
cut.
The computation is the same as in the real case, except that the
complex Schur decomposition is used to reduce the matrix to a
triangular matrix. The theoretical cost is the same. Details are in:
&Aring;ke Bj&ouml;rck and Sven Hammarling, "A Schur method for the
square root of a matrix", <em>Linear Algebra Appl.</em>,
52/53:127&ndash;140, 1983.
Example: The following program checks that the square root of
\f[ \left[ \begin{array}{cc}
\cos(\frac13\pi) & -\sin(\frac13\pi) \\
\sin(\frac13\pi) & \cos(\frac13\pi)
\end{array} \right], \f]
corresponding to a rotation over 60 degrees, is a rotation over 30 degrees:
\f[ \left[ \begin{array}{cc}
\cos(\frac16\pi) & -\sin(\frac16\pi) \\
\sin(\frac16\pi) & \cos(\frac16\pi)
\end{array} \right]. \f]
\include MatrixSquareRoot.cpp
Output: \verbinclude MatrixSquareRoot.out
\sa class RealSchur, class ComplexSchur, class MatrixSquareRoot,
SelfAdjointEigenSolver::operatorSqrt().
*/
#endif // EIGEN_MATRIX_FUNCTIONS

View File

@@ -0,0 +1,24 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_MOREVECTORIZATION_MODULE_H
#define EIGEN_MOREVECTORIZATION_MODULE_H
#include <Eigen/Core>
namespace Eigen {
/**
* \defgroup MoreVectorization More vectorization module
*/
}
#include "src/MoreVectorization/MathFunctions.h"
#endif // EIGEN_MOREVECTORIZATION_MODULE_H

View File

@@ -0,0 +1,134 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Thomas Capricelli <orzel@freehackers.org>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_NONLINEAROPTIMIZATION_MODULE
#define EIGEN_NONLINEAROPTIMIZATION_MODULE
#include <vector>
#include <Eigen/Core>
#include <Eigen/Jacobi>
#include <Eigen/QR>
#include <unsupported/Eigen/NumericalDiff>
/**
* \defgroup NonLinearOptimization_Module Non linear optimization module
*
* \code
* #include <unsupported/Eigen/NonLinearOptimization>
* \endcode
*
* This module provides implementation of two important algorithms in non linear
* optimization. In both cases, we consider a system of non linear functions. Of
* course, this should work, and even work very well if those functions are
* actually linear. But if this is so, you should probably better use other
* methods more fitted to this special case.
*
* One algorithm allows to find an extremum of such a system (Levenberg
* Marquardt algorithm) and the second one is used to find
* a zero for the system (Powell hybrid "dogleg" method).
*
* This code is a port of minpack (http://en.wikipedia.org/wiki/MINPACK).
* Minpack is a very famous, old, robust and well-reknown package, written in
* fortran. Those implementations have been carefully tuned, tested, and used
* for several decades.
*
* The original fortran code was automatically translated using f2c (http://en.wikipedia.org/wiki/F2c) in C,
* then c++, and then cleaned by several different authors.
* The last one of those cleanings being our starting point :
* http://devernay.free.fr/hacks/cminpack.html
*
* Finally, we ported this code to Eigen, creating classes and API
* coherent with Eigen. When possible, we switched to Eigen
* implementation, such as most linear algebra (vectors, matrices, stable norms).
*
* Doing so, we were very careful to check the tests we setup at the very
* beginning, which ensure that the same results are found.
*
* \section Tests Tests
*
* The tests are placed in the file unsupported/test/NonLinear.cpp.
*
* There are two kinds of tests : those that come from examples bundled with cminpack.
* They guaranty we get the same results as the original algorithms (value for 'x',
* for the number of evaluations of the function, and for the number of evaluations
* of the jacobian if ever).
*
* Other tests were added by myself at the very beginning of the
* process and check the results for levenberg-marquardt using the reference data
* on http://www.itl.nist.gov/div898/strd/nls/nls_main.shtml. Since then i've
* carefully checked that the same results were obtained when modifiying the
* code. Please note that we do not always get the exact same decimals as they do,
* but this is ok : they use 128bits float, and we do the tests using the C type 'double',
* which is 64 bits on most platforms (x86 and amd64, at least).
* I've performed those tests on several other implementations of levenberg-marquardt, and
* (c)minpack performs VERY well compared to those, both in accuracy and speed.
*
* The documentation for running the tests is on the wiki
* http://eigen.tuxfamily.org/index.php?title=Tests
*
* \section API API : overview of methods
*
* Both algorithms can use either the jacobian (provided by the user) or compute
* an approximation by themselves (actually using Eigen \ref NumericalDiff_Module).
* The part of API referring to the latter use 'NumericalDiff' in the method names
* (exemple: LevenbergMarquardt.minimizeNumericalDiff() )
*
* The methods LevenbergMarquardt.lmder1()/lmdif1()/lmstr1() and
* HybridNonLinearSolver.hybrj1()/hybrd1() are specific methods from the original
* minpack package that you probably should NOT use until you are porting a code that
* was previously using minpack. They just define a 'simple' API with default values
* for some parameters.
*
* All algorithms are provided using Two APIs :
* - one where the user inits the algorithm, and uses '*OneStep()' as much as he wants :
* this way the caller have control over the steps
* - one where the user just calls a method (optimize() or solve()) which will
* handle the loop: init + loop until a stop condition is met. Those are provided for
* convenience.
*
* As an example, the method LevenbergMarquardt::minimize() is
* implemented as follow :
* \code
* Status LevenbergMarquardt<FunctorType,Scalar>::minimize(FVectorType &x, const int mode)
* {
* Status status = minimizeInit(x, mode);
* do {
* status = minimizeOneStep(x, mode);
* } while (status==Running);
* return status;
* }
* \endcode
*
* \section examples Examples
*
* The easiest way to understand how to use this module is by looking at the many examples in the file
* unsupported/test/NonLinearOptimization.cpp.
*/
#ifndef EIGEN_PARSED_BY_DOXYGEN
#include "src/NonLinearOptimization/qrsolv.h"
#include "src/NonLinearOptimization/r1updt.h"
#include "src/NonLinearOptimization/r1mpyq.h"
#include "src/NonLinearOptimization/rwupdt.h"
#include "src/NonLinearOptimization/fdjac1.h"
#include "src/NonLinearOptimization/lmpar.h"
#include "src/NonLinearOptimization/dogleg.h"
#include "src/NonLinearOptimization/covar.h"
#include "src/NonLinearOptimization/chkder.h"
#endif
#include "src/NonLinearOptimization/HybridNonLinearSolver.h"
#include "src/NonLinearOptimization/LevenbergMarquardt.h"
#endif // EIGEN_NONLINEAROPTIMIZATION_MODULE

View File

@@ -0,0 +1,56 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Thomas Capricelli <orzel@freehackers.org>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_NUMERICALDIFF_MODULE
#define EIGEN_NUMERICALDIFF_MODULE
#include <Eigen/Core>
namespace Eigen {
/**
* \defgroup NumericalDiff_Module Numerical differentiation module
*
* \code
* #include <unsupported/Eigen/NumericalDiff>
* \endcode
*
* See http://en.wikipedia.org/wiki/Numerical_differentiation
*
* Warning : this should NOT be confused with automatic differentiation, which
* is a different method and has its own module in Eigen : \ref
* AutoDiff_Module.
*
* Currently only "Forward" and "Central" schemes are implemented. Those
* are basic methods, and there exist some more elaborated way of
* computing such approximates. They are implemented using both
* proprietary and free software, and usually requires linking to an
* external library. It is very easy for you to write a functor
* using such software, and the purpose is quite orthogonal to what we
* want to achieve with Eigen.
*
* This is why we will not provide wrappers for every great numerical
* differentiation software that exist, but should rather stick with those
* basic ones, that still are useful for testing.
*
* Also, the \ref NonLinearOptimization_Module needs this in order to
* provide full features compatibility with the original (c)minpack
* package.
*
*/
}
//@{
#include "src/NumericalDiff/NumericalDiff.h"
//@}
#endif // EIGEN_NUMERICALDIFF_MODULE

View File

@@ -0,0 +1,322 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2010 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_OPENGL_MODULE
#define EIGEN_OPENGL_MODULE
#include <Eigen/Geometry>
#if defined(__APPLE_CC__)
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
namespace Eigen {
/**
* \defgroup OpenGLSUpport_Module OpenGL Support module
*
* This module provides wrapper functions for a couple of OpenGL functions
* which simplify the way to pass Eigen's object to openGL.
* Here is an exmaple:
*
* \code
* // You need to add path_to_eigen/unsupported to your include path.
* #include <Eigen/OpenGLSupport>
* // ...
* Vector3f x, y;
* Matrix3f rot;
*
* glVertex(y + x * rot);
*
* Quaternion q;
* glRotate(q);
*
* // ...
* \endcode
*
*/
//@{
#define EIGEN_GL_FUNC_DECLARATION(FUNC) \
namespace internal { \
template< typename XprType, \
typename Scalar = typename XprType::Scalar, \
int Rows = XprType::RowsAtCompileTime, \
int Cols = XprType::ColsAtCompileTime, \
bool IsGLCompatible = bool(XprType::Flags&LinearAccessBit) \
&& bool(XprType::Flags&DirectAccessBit) \
&& (XprType::IsVectorAtCompileTime || (XprType::Flags&RowMajorBit)==0)> \
struct EIGEN_CAT(EIGEN_CAT(gl_,FUNC),_impl); \
\
template<typename XprType, typename Scalar, int Rows, int Cols> \
struct EIGEN_CAT(EIGEN_CAT(gl_,FUNC),_impl)<XprType,Scalar,Rows,Cols,false> { \
inline static void run(const XprType& p) { \
EIGEN_CAT(EIGEN_CAT(gl_,FUNC),_impl)<typename plain_matrix_type_column_major<XprType>::type>::run(p); } \
}; \
} \
\
template<typename Derived> inline void FUNC(const Eigen::DenseBase<Derived>& p) { \
EIGEN_CAT(EIGEN_CAT(internal::gl_,FUNC),_impl)<Derived>::run(p.derived()); \
}
#define EIGEN_GL_FUNC_SPECIALIZATION_MAT(FUNC,SCALAR,ROWS,COLS,SUFFIX) \
namespace internal { \
template< typename XprType> struct EIGEN_CAT(EIGEN_CAT(gl_,FUNC),_impl)<XprType, SCALAR, ROWS, COLS, true> { \
inline static void run(const XprType& p) { FUNC##SUFFIX(p.data()); } \
}; \
}
#define EIGEN_GL_FUNC_SPECIALIZATION_VEC(FUNC,SCALAR,SIZE,SUFFIX) \
namespace internal { \
template< typename XprType> struct EIGEN_CAT(EIGEN_CAT(gl_,FUNC),_impl)<XprType, SCALAR, SIZE, 1, true> { \
inline static void run(const XprType& p) { FUNC##SUFFIX(p.data()); } \
}; \
template< typename XprType> struct EIGEN_CAT(EIGEN_CAT(gl_,FUNC),_impl)<XprType, SCALAR, 1, SIZE, true> { \
inline static void run(const XprType& p) { FUNC##SUFFIX(p.data()); } \
}; \
}
EIGEN_GL_FUNC_DECLARATION (glVertex)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glVertex,int, 2,2iv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glVertex,short, 2,2sv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glVertex,float, 2,2fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glVertex,double, 2,2dv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glVertex,int, 3,3iv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glVertex,short, 3,3sv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glVertex,float, 3,3fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glVertex,double, 3,3dv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glVertex,int, 4,4iv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glVertex,short, 4,4sv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glVertex,float, 4,4fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glVertex,double, 4,4dv)
EIGEN_GL_FUNC_DECLARATION (glTexCoord)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTexCoord,int, 2,2iv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTexCoord,short, 2,2sv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTexCoord,float, 2,2fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTexCoord,double, 2,2dv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTexCoord,int, 3,3iv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTexCoord,short, 3,3sv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTexCoord,float, 3,3fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTexCoord,double, 3,3dv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTexCoord,int, 4,4iv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTexCoord,short, 4,4sv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTexCoord,float, 4,4fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTexCoord,double, 4,4dv)
EIGEN_GL_FUNC_DECLARATION (glColor)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glColor,int, 2,2iv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glColor,short, 2,2sv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glColor,float, 2,2fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glColor,double, 2,2dv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glColor,int, 3,3iv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glColor,short, 3,3sv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glColor,float, 3,3fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glColor,double, 3,3dv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glColor,int, 4,4iv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glColor,short, 4,4sv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glColor,float, 4,4fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glColor,double, 4,4dv)
EIGEN_GL_FUNC_DECLARATION (glNormal)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glNormal,int, 3,3iv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glNormal,short, 3,3sv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glNormal,float, 3,3fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glNormal,double, 3,3dv)
inline void glScale2fv(const float* v) { glScalef(v[0], v[1], 1.f); }
inline void glScale2dv(const double* v) { glScaled(v[0], v[1], 1.0); }
inline void glScale3fv(const float* v) { glScalef(v[0], v[1], v[2]); }
inline void glScale3dv(const double* v) { glScaled(v[0], v[1], v[2]); }
EIGEN_GL_FUNC_DECLARATION (glScale)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glScale,float, 2,2fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glScale,double, 2,2dv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glScale,float, 3,3fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glScale,double, 3,3dv)
template<typename Scalar> void glScale(const UniformScaling<Scalar>& s) { glScale(Matrix<Scalar,3,1>::Constant(s.factor())); }
inline void glTranslate2fv(const float* v) { glTranslatef(v[0], v[1], 0.f); }
inline void glTranslate2dv(const double* v) { glTranslated(v[0], v[1], 0.0); }
inline void glTranslate3fv(const float* v) { glTranslatef(v[0], v[1], v[2]); }
inline void glTranslate3dv(const double* v) { glTranslated(v[0], v[1], v[2]); }
EIGEN_GL_FUNC_DECLARATION (glTranslate)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTranslate,float, 2,2fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTranslate,double, 2,2dv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTranslate,float, 3,3fv)
EIGEN_GL_FUNC_SPECIALIZATION_VEC(glTranslate,double, 3,3dv)
template<typename Scalar> void glTranslate(const Translation<Scalar,2>& t) { glTranslate(t.vector()); }
template<typename Scalar> void glTranslate(const Translation<Scalar,3>& t) { glTranslate(t.vector()); }
EIGEN_GL_FUNC_DECLARATION (glMultMatrix)
EIGEN_GL_FUNC_SPECIALIZATION_MAT(glMultMatrix,float, 4,4,f)
EIGEN_GL_FUNC_SPECIALIZATION_MAT(glMultMatrix,double, 4,4,d)
template<typename Scalar> void glMultMatrix(const Transform<Scalar,3,Affine>& t) { glMultMatrix(t.matrix()); }
template<typename Scalar> void glMultMatrix(const Transform<Scalar,3,Projective>& t) { glMultMatrix(t.matrix()); }
template<typename Scalar> void glMultMatrix(const Transform<Scalar,3,AffineCompact>& t) { glMultMatrix(Transform<Scalar,3,Affine>(t).matrix()); }
EIGEN_GL_FUNC_DECLARATION (glLoadMatrix)
EIGEN_GL_FUNC_SPECIALIZATION_MAT(glLoadMatrix,float, 4,4,f)
EIGEN_GL_FUNC_SPECIALIZATION_MAT(glLoadMatrix,double, 4,4,d)
template<typename Scalar> void glLoadMatrix(const Transform<Scalar,3,Affine>& t) { glLoadMatrix(t.matrix()); }
template<typename Scalar> void glLoadMatrix(const Transform<Scalar,3,Projective>& t) { glLoadMatrix(t.matrix()); }
template<typename Scalar> void glLoadMatrix(const Transform<Scalar,3,AffineCompact>& t) { glLoadMatrix(Transform<Scalar,3,Affine>(t).matrix()); }
inline void glRotate(const Rotation2D<float>& rot)
{
glRotatef(rot.angle()*180.f/float(M_PI), 0.f, 0.f, 1.f);
}
inline void glRotate(const Rotation2D<double>& rot)
{
glRotated(rot.angle()*180.0/M_PI, 0.0, 0.0, 1.0);
}
template<typename Derived> void glRotate(const RotationBase<Derived,3>& rot)
{
Transform<typename Derived::Scalar,3,Projective> tr(rot);
glMultMatrix(tr.matrix());
}
#define EIGEN_GL_MAKE_CONST_const const
#define EIGEN_GL_MAKE_CONST__
#define EIGEN_GL_EVAL(X) X
#define EIGEN_GL_FUNC1_DECLARATION(FUNC,ARG1,CONST) \
namespace internal { \
template< typename XprType, \
typename Scalar = typename XprType::Scalar, \
int Rows = XprType::RowsAtCompileTime, \
int Cols = XprType::ColsAtCompileTime, \
bool IsGLCompatible = bool(XprType::Flags&LinearAccessBit) \
&& bool(XprType::Flags&DirectAccessBit) \
&& (XprType::IsVectorAtCompileTime || (XprType::Flags&RowMajorBit)==0)> \
struct EIGEN_CAT(EIGEN_CAT(gl_,FUNC),_impl); \
\
template<typename XprType, typename Scalar, int Rows, int Cols> \
struct EIGEN_CAT(EIGEN_CAT(gl_,FUNC),_impl)<XprType,Scalar,Rows,Cols,false> { \
inline static void run(ARG1 a,EIGEN_GL_EVAL(EIGEN_GL_MAKE_CONST_##CONST) XprType& p) { \
EIGEN_CAT(EIGEN_CAT(gl_,FUNC),_impl)<typename plain_matrix_type_column_major<XprType>::type>::run(a,p); } \
}; \
} \
\
template<typename Derived> inline void FUNC(ARG1 a,EIGEN_GL_EVAL(EIGEN_GL_MAKE_CONST_##CONST) Eigen::DenseBase<Derived>& p) { \
EIGEN_CAT(EIGEN_CAT(internal::gl_,FUNC),_impl)<Derived>::run(a,p.derived()); \
}
#define EIGEN_GL_FUNC1_SPECIALIZATION_MAT(FUNC,ARG1,CONST,SCALAR,ROWS,COLS,SUFFIX) \
namespace internal { \
template< typename XprType> struct EIGEN_CAT(EIGEN_CAT(gl_,FUNC),_impl)<XprType, SCALAR, ROWS, COLS, true> { \
inline static void run(ARG1 a, EIGEN_GL_EVAL(EIGEN_GL_MAKE_CONST_##CONST) XprType& p) { FUNC##SUFFIX(a,p.data()); } \
}; \
}
#define EIGEN_GL_FUNC1_SPECIALIZATION_VEC(FUNC,ARG1,CONST,SCALAR,SIZE,SUFFIX) \
namespace internal { \
template< typename XprType> struct EIGEN_CAT(EIGEN_CAT(gl_,FUNC),_impl)<XprType, SCALAR, SIZE, 1, true> { \
inline static void run(ARG1 a, EIGEN_GL_EVAL(EIGEN_GL_MAKE_CONST_##CONST) XprType& p) { FUNC##SUFFIX(a,p.data()); } \
}; \
template< typename XprType> struct EIGEN_CAT(EIGEN_CAT(gl_,FUNC),_impl)<XprType, SCALAR, 1, SIZE, true> { \
inline static void run(ARG1 a, EIGEN_GL_EVAL(EIGEN_GL_MAKE_CONST_##CONST) XprType& p) { FUNC##SUFFIX(a,p.data()); } \
}; \
}
EIGEN_GL_FUNC1_DECLARATION (glGet,GLenum,_)
EIGEN_GL_FUNC1_SPECIALIZATION_MAT(glGet,GLenum,_,float, 4,4,Floatv)
EIGEN_GL_FUNC1_SPECIALIZATION_MAT(glGet,GLenum,_,double, 4,4,Doublev)
// glUniform API
#ifdef GL_VERSION_2_0
inline void glUniform2fv_ei (GLint loc, const float* v) { glUniform2fv(loc,1,v); }
inline void glUniform2iv_ei (GLint loc, const int* v) { glUniform2iv(loc,1,v); }
inline void glUniform3fv_ei (GLint loc, const float* v) { glUniform3fv(loc,1,v); }
inline void glUniform3iv_ei (GLint loc, const int* v) { glUniform3iv(loc,1,v); }
inline void glUniform4fv_ei (GLint loc, const float* v) { glUniform4fv(loc,1,v); }
inline void glUniform4iv_ei (GLint loc, const int* v) { glUniform4iv(loc,1,v); }
inline void glUniformMatrix2fv_ei (GLint loc, const float* v) { glUniformMatrix2fv(loc,1,false,v); }
inline void glUniformMatrix3fv_ei (GLint loc, const float* v) { glUniformMatrix3fv(loc,1,false,v); }
inline void glUniformMatrix4fv_ei (GLint loc, const float* v) { glUniformMatrix4fv(loc,1,false,v); }
EIGEN_GL_FUNC1_DECLARATION (glUniform,GLint,const)
EIGEN_GL_FUNC1_SPECIALIZATION_VEC(glUniform,GLint,const,float, 2,2fv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_VEC(glUniform,GLint,const,int, 2,2iv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_VEC(glUniform,GLint,const,float, 3,3fv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_VEC(glUniform,GLint,const,int, 3,3iv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_VEC(glUniform,GLint,const,float, 4,4fv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_VEC(glUniform,GLint,const,int, 4,4iv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_MAT(glUniform,GLint,const,float, 2,2,Matrix2fv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_MAT(glUniform,GLint,const,float, 3,3,Matrix3fv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_MAT(glUniform,GLint,const,float, 4,4,Matrix4fv_ei)
#endif
#ifdef GL_VERSION_2_1
static void glUniformMatrix2x3fv_ei(GLint loc, const float* v) { glUniformMatrix2x3fv(loc,1,false,v); }
static void glUniformMatrix3x2fv_ei(GLint loc, const float* v) { glUniformMatrix3x2fv(loc,1,false,v); }
static void glUniformMatrix2x4fv_ei(GLint loc, const float* v) { glUniformMatrix2x4fv(loc,1,false,v); }
static void glUniformMatrix4x2fv_ei(GLint loc, const float* v) { glUniformMatrix4x2fv(loc,1,false,v); }
static void glUniformMatrix3x4fv_ei(GLint loc, const float* v) { glUniformMatrix3x4fv(loc,1,false,v); }
static void glUniformMatrix4x3fv_ei(GLint loc, const float* v) { glUniformMatrix4x3fv(loc,1,false,v); }
EIGEN_GL_FUNC1_SPECIALIZATION_MAT(glUniform,GLint,const,float, 2,3,Matrix2x3fv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_MAT(glUniform,GLint,const,float, 3,2,Matrix3x2fv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_MAT(glUniform,GLint,const,float, 2,4,Matrix2x4fv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_MAT(glUniform,GLint,const,float, 4,2,Matrix4x2fv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_MAT(glUniform,GLint,const,float, 3,4,Matrix3x4fv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_MAT(glUniform,GLint,const,float, 4,3,Matrix4x3fv_ei)
#endif
#ifdef GL_VERSION_3_0
inline void glUniform2uiv_ei (GLint loc, const unsigned int* v) { glUniform2uiv(loc,1,v); }
inline void glUniform3uiv_ei (GLint loc, const unsigned int* v) { glUniform3uiv(loc,1,v); }
inline void glUniform4uiv_ei (GLint loc, const unsigned int* v) { glUniform4uiv(loc,1,v); }
EIGEN_GL_FUNC1_SPECIALIZATION_VEC(glUniform,GLint,const,unsigned int, 2,2uiv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_VEC(glUniform,GLint,const,unsigned int, 3,3uiv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_VEC(glUniform,GLint,const,unsigned int, 4,4uiv_ei)
#endif
#ifdef GL_ARB_gpu_shader_fp64
inline void glUniform2dv_ei (GLint loc, const double* v) { glUniform2dv(loc,1,v); }
inline void glUniform3dv_ei (GLint loc, const double* v) { glUniform3dv(loc,1,v); }
inline void glUniform4dv_ei (GLint loc, const double* v) { glUniform4dv(loc,1,v); }
EIGEN_GL_FUNC1_SPECIALIZATION_VEC(glUniform,GLint,const,double, 2,2dv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_VEC(glUniform,GLint,const,double, 3,3dv_ei)
EIGEN_GL_FUNC1_SPECIALIZATION_VEC(glUniform,GLint,const,double, 4,4dv_ei)
#endif
//@}
}
#endif // EIGEN_OPENGL_MODULE

View File

@@ -0,0 +1,138 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_POLYNOMIALS_MODULE_H
#define EIGEN_POLYNOMIALS_MODULE_H
#include <Eigen/Core>
#include <Eigen/src/Core/util/DisableStupidWarnings.h>
#include <Eigen/Eigenvalues>
// Note that EIGEN_HIDE_HEAVY_CODE has to be defined per module
#if (defined EIGEN_EXTERN_INSTANTIATIONS) && (EIGEN_EXTERN_INSTANTIATIONS>=2)
#ifndef EIGEN_HIDE_HEAVY_CODE
#define EIGEN_HIDE_HEAVY_CODE
#endif
#elif defined EIGEN_HIDE_HEAVY_CODE
#undef EIGEN_HIDE_HEAVY_CODE
#endif
/**
* \defgroup Polynomials_Module Polynomials module
* \brief This module provides a QR based polynomial solver.
*
* To use this module, add
* \code
* #include <unsupported/Eigen/Polynomials>
* \endcode
* at the start of your source file.
*/
#include "src/Polynomials/PolynomialUtils.h"
#include "src/Polynomials/Companion.h"
#include "src/Polynomials/PolynomialSolver.h"
/**
\page polynomials Polynomials defines functions for dealing with polynomials
and a QR based polynomial solver.
\ingroup Polynomials_Module
The remainder of the page documents first the functions for evaluating, computing
polynomials, computing estimates about polynomials and next the QR based polynomial
solver.
\section polynomialUtils convenient functions to deal with polynomials
\subsection roots_to_monicPolynomial
The function
\code
void roots_to_monicPolynomial( const RootVector& rv, Polynomial& poly )
\endcode
computes the coefficients \f$ a_i \f$ of
\f$ p(x) = a_0 + a_{1}x + ... + a_{n-1}x^{n-1} + x^n \f$
where \f$ p \f$ is known through its roots i.e. \f$ p(x) = (x-r_1)(x-r_2)...(x-r_n) \f$.
\subsection poly_eval
The function
\code
T poly_eval( const Polynomials& poly, const T& x )
\endcode
evaluates a polynomial at a given point using stabilized H&ouml;rner method.
The following code: first computes the coefficients in the monomial basis of the monic polynomial that has the provided roots;
then, it evaluates the computed polynomial, using a stabilized H&ouml;rner method.
\include PolynomialUtils1.cpp
Output: \verbinclude PolynomialUtils1.out
\subsection Cauchy bounds
The function
\code
Real cauchy_max_bound( const Polynomial& poly )
\endcode
provides a maximum bound (the Cauchy one: \f$C(p)\f$) for the absolute value of a root of the given polynomial i.e.
\f$ \forall r_i \f$ root of \f$ p(x) = \sum_{k=0}^d a_k x^k \f$,
\f$ |r_i| \le C(p) = \sum_{k=0}^{d} \left | \frac{a_k}{a_d} \right | \f$
The leading coefficient \f$ p \f$: should be non zero \f$a_d \neq 0\f$.
The function
\code
Real cauchy_min_bound( const Polynomial& poly )
\endcode
provides a minimum bound (the Cauchy one: \f$c(p)\f$) for the absolute value of a non zero root of the given polynomial i.e.
\f$ \forall r_i \neq 0 \f$ root of \f$ p(x) = \sum_{k=0}^d a_k x^k \f$,
\f$ |r_i| \ge c(p) = \left( \sum_{k=0}^{d} \left | \frac{a_k}{a_0} \right | \right)^{-1} \f$
\section QR polynomial solver class
Computes the complex roots of a polynomial by computing the eigenvalues of the associated companion matrix with the QR algorithm.
The roots of \f$ p(x) = a_0 + a_1 x + a_2 x^2 + a_{3} x^3 + x^4 \f$ are the eigenvalues of
\f$
\left [
\begin{array}{cccc}
0 & 0 & 0 & a_0 \\
1 & 0 & 0 & a_1 \\
0 & 1 & 0 & a_2 \\
0 & 0 & 1 & a_3
\end{array} \right ]
\f$
However, the QR algorithm is not guaranteed to converge when there are several eigenvalues with same modulus.
Therefore the current polynomial solver is guaranteed to provide a correct result only when the complex roots \f$r_1,r_2,...,r_d\f$ have distinct moduli i.e.
\f$ \forall i,j \in [1;d],~ \| r_i \| \neq \| r_j \| \f$.
With 32bit (float) floating types this problem shows up frequently.
However, almost always, correct accuracy is reached even in these cases for 64bit
(double) floating types and small polynomial degree (<20).
\include PolynomialSolver1.cpp
In the above example:
-# a simple use of the polynomial solver is shown;
-# the accuracy problem with the QR algorithm is presented: a polynomial with almost conjugate roots is provided to the solver.
Those roots have almost same module therefore the QR algorithm failed to converge: the accuracy
of the last root is bad;
-# a simple way to circumvent the problem is shown: use doubles instead of floats.
Output: \verbinclude PolynomialSolver1.out
*/
#include <Eigen/src/Core/util/ReenableStupidWarnings.h>
#endif // EIGEN_POLYNOMIALS_MODULE_H
/* vim: set filetype=cpp et sw=2 ts=2 ai: */

39
extern/Eigen3/unsupported/Eigen/SVD vendored Normal file
View File

@@ -0,0 +1,39 @@
#ifndef EIGEN_SVD_MODULE_H
#define EIGEN_SVD_MODULE_H
#include <Eigen/QR>
#include <Eigen/Householder>
#include <Eigen/Jacobi>
#include "../../Eigen/src/Core/util/DisableStupidWarnings.h"
/** \defgroup SVD_Module SVD module
*
*
*
* This module provides SVD decomposition for matrices (both real and complex).
* This decomposition is accessible via the following MatrixBase method:
* - MatrixBase::jacobiSvd()
*
* \code
* #include <Eigen/SVD>
* \endcode
*/
#include "../../Eigen/src/misc/Solve.h"
#include "../../Eigen/src/SVD/UpperBidiagonalization.h"
#include "src/SVD/SVDBase.h"
#include "src/SVD/JacobiSVD.h"
#include "src/SVD/BDCSVD.h"
#if defined(EIGEN_USE_LAPACKE) && !defined(EIGEN_USE_LAPACKE_STRICT)
#include "../../Eigen/src/SVD/JacobiSVD_MKL.h"
#endif
#ifdef EIGEN2_SUPPORT
#include "../../Eigen/src/Eigen2Support/SVD.h"
#endif
#include "../../Eigen/src/Core/util/ReenableStupidWarnings.h"
#endif // EIGEN_SVD_MODULE_H
/* vim: set filetype=cpp et sw=2 ts=2 ai: */

39
extern/Eigen3/unsupported/Eigen/Skyline vendored Normal file
View File

@@ -0,0 +1,39 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SKYLINE_MODULE_H
#define EIGEN_SKYLINE_MODULE_H
#include "Eigen/Core"
#include "Eigen/src/Core/util/DisableStupidWarnings.h"
#include <map>
#include <cstdlib>
#include <cstring>
#include <algorithm>
/**
* \defgroup Skyline_Module Skyline module
*
*
*
*
*/
#include "src/Skyline/SkylineUtil.h"
#include "src/Skyline/SkylineMatrixBase.h"
#include "src/Skyline/SkylineStorage.h"
#include "src/Skyline/SkylineMatrix.h"
#include "src/Skyline/SkylineInplaceLU.h"
#include "src/Skyline/SkylineProduct.h"
#include "Eigen/src/Core/util/ReenableStupidWarnings.h"
#endif // EIGEN_SKYLINE_MODULE_H

View File

@@ -0,0 +1,56 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <g.gael@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SPARSE_EXTRA_MODULE_H
#define EIGEN_SPARSE_EXTRA_MODULE_H
#include "../../Eigen/Sparse"
#include "../../Eigen/src/Core/util/DisableStupidWarnings.h"
#include <vector>
#include <map>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <fstream>
#include <sstream>
#ifdef EIGEN_GOOGLEHASH_SUPPORT
#include <google/dense_hash_map>
#endif
/**
* \defgroup SparseExtra_Module SparseExtra module
*
* This module contains some experimental features extending the sparse module.
*
* \code
* #include <Eigen/SparseExtra>
* \endcode
*/
#include "../../Eigen/src/misc/Solve.h"
#include "../../Eigen/src/misc/SparseSolve.h"
#include "src/SparseExtra/DynamicSparseMatrix.h"
#include "src/SparseExtra/BlockOfDynamicSparseMatrix.h"
#include "src/SparseExtra/RandomSetter.h"
#include "src/SparseExtra/MarketIO.h"
#if !defined(_WIN32)
#include <dirent.h>
#include "src/SparseExtra/MatrixMarketIterator.h"
#endif
#include "../../Eigen/src/Core/util/ReenableStupidWarnings.h"
#endif // EIGEN_SPARSE_EXTRA_MODULE_H

31
extern/Eigen3/unsupported/Eigen/Splines vendored Normal file
View File

@@ -0,0 +1,31 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 20010-2011 Hauke Heibel <hauke.heibel@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SPLINES_MODULE_H
#define EIGEN_SPLINES_MODULE_H
namespace Eigen
{
/**
* \defgroup Splines_Module Spline and spline fitting module
*
* This module provides a simple multi-dimensional spline class while
* offering most basic functionality to fit a spline to point sets.
*
* \code
* #include <unsupported/Eigen/Splines>
* \endcode
*/
}
#include "src/Splines/SplineFwd.h"
#include "src/Splines/Spline.h"
#include "src/Splines/SplineFitting.h"
#endif // EIGEN_SPLINES_MODULE_H

View File

@@ -0,0 +1,83 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_AUTODIFF_JACOBIAN_H
#define EIGEN_AUTODIFF_JACOBIAN_H
namespace Eigen
{
template<typename Functor> class AutoDiffJacobian : public Functor
{
public:
AutoDiffJacobian() : Functor() {}
AutoDiffJacobian(const Functor& f) : Functor(f) {}
// forward constructors
template<typename T0>
AutoDiffJacobian(const T0& a0) : Functor(a0) {}
template<typename T0, typename T1>
AutoDiffJacobian(const T0& a0, const T1& a1) : Functor(a0, a1) {}
template<typename T0, typename T1, typename T2>
AutoDiffJacobian(const T0& a0, const T1& a1, const T2& a2) : Functor(a0, a1, a2) {}
enum {
InputsAtCompileTime = Functor::InputsAtCompileTime,
ValuesAtCompileTime = Functor::ValuesAtCompileTime
};
typedef typename Functor::InputType InputType;
typedef typename Functor::ValueType ValueType;
typedef typename Functor::JacobianType JacobianType;
typedef typename JacobianType::Scalar Scalar;
typedef typename JacobianType::Index Index;
typedef Matrix<Scalar,InputsAtCompileTime,1> DerivativeType;
typedef AutoDiffScalar<DerivativeType> ActiveScalar;
typedef Matrix<ActiveScalar, InputsAtCompileTime, 1> ActiveInput;
typedef Matrix<ActiveScalar, ValuesAtCompileTime, 1> ActiveValue;
void operator() (const InputType& x, ValueType* v, JacobianType* _jac=0) const
{
eigen_assert(v!=0);
if (!_jac)
{
Functor::operator()(x, v);
return;
}
JacobianType& jac = *_jac;
ActiveInput ax = x.template cast<ActiveScalar>();
ActiveValue av(jac.rows());
if(InputsAtCompileTime==Dynamic)
for (Index j=0; j<jac.rows(); j++)
av[j].derivatives().resize(this->inputs());
for (Index i=0; i<jac.cols(); i++)
ax[i].derivatives() = DerivativeType::Unit(this->inputs(),i);
Functor::operator()(ax, &av);
for (Index i=0; i<jac.rows(); i++)
{
(*v)[i] = av[i].value();
jac.row(i) = av[i].derivatives();
}
}
protected:
};
}
#endif // EIGEN_AUTODIFF_JACOBIAN_H

View File

@@ -0,0 +1,642 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_AUTODIFF_SCALAR_H
#define EIGEN_AUTODIFF_SCALAR_H
namespace Eigen {
namespace internal {
template<typename A, typename B>
struct make_coherent_impl {
static void run(A&, B&) {}
};
// resize a to match b is a.size()==0, and conversely.
template<typename A, typename B>
void make_coherent(const A& a, const B&b)
{
make_coherent_impl<A,B>::run(a.const_cast_derived(), b.const_cast_derived());
}
template<typename _DerType, bool Enable> struct auto_diff_special_op;
} // end namespace internal
/** \class AutoDiffScalar
* \brief A scalar type replacement with automatic differentation capability
*
* \param _DerType the vector type used to store/represent the derivatives. The base scalar type
* as well as the number of derivatives to compute are determined from this type.
* Typical choices include, e.g., \c Vector4f for 4 derivatives, or \c VectorXf
* if the number of derivatives is not known at compile time, and/or, the number
* of derivatives is large.
* Note that _DerType can also be a reference (e.g., \c VectorXf&) to wrap a
* existing vector into an AutoDiffScalar.
* Finally, _DerType can also be any Eigen compatible expression.
*
* This class represents a scalar value while tracking its respective derivatives using Eigen's expression
* template mechanism.
*
* It supports the following list of global math function:
* - std::abs, std::sqrt, std::pow, std::exp, std::log, std::sin, std::cos,
* - internal::abs, internal::sqrt, numext::pow, internal::exp, internal::log, internal::sin, internal::cos,
* - internal::conj, internal::real, internal::imag, numext::abs2.
*
* AutoDiffScalar can be used as the scalar type of an Eigen::Matrix object. However,
* in that case, the expression template mechanism only occurs at the top Matrix level,
* while derivatives are computed right away.
*
*/
template<typename _DerType>
class AutoDiffScalar
: public internal::auto_diff_special_op
<_DerType, !internal::is_same<typename internal::traits<typename internal::remove_all<_DerType>::type>::Scalar,
typename NumTraits<typename internal::traits<typename internal::remove_all<_DerType>::type>::Scalar>::Real>::value>
{
public:
typedef internal::auto_diff_special_op
<_DerType, !internal::is_same<typename internal::traits<typename internal::remove_all<_DerType>::type>::Scalar,
typename NumTraits<typename internal::traits<typename internal::remove_all<_DerType>::type>::Scalar>::Real>::value> Base;
typedef typename internal::remove_all<_DerType>::type DerType;
typedef typename internal::traits<DerType>::Scalar Scalar;
typedef typename NumTraits<Scalar>::Real Real;
using Base::operator+;
using Base::operator*;
/** Default constructor without any initialization. */
AutoDiffScalar() {}
/** Constructs an active scalar from its \a value,
and initializes the \a nbDer derivatives such that it corresponds to the \a derNumber -th variable */
AutoDiffScalar(const Scalar& value, int nbDer, int derNumber)
: m_value(value), m_derivatives(DerType::Zero(nbDer))
{
m_derivatives.coeffRef(derNumber) = Scalar(1);
}
/** Conversion from a scalar constant to an active scalar.
* The derivatives are set to zero. */
/*explicit*/ AutoDiffScalar(const Real& value)
: m_value(value)
{
if(m_derivatives.size()>0)
m_derivatives.setZero();
}
/** Constructs an active scalar from its \a value and derivatives \a der */
AutoDiffScalar(const Scalar& value, const DerType& der)
: m_value(value), m_derivatives(der)
{}
template<typename OtherDerType>
AutoDiffScalar(const AutoDiffScalar<OtherDerType>& other)
: m_value(other.value()), m_derivatives(other.derivatives())
{}
friend std::ostream & operator << (std::ostream & s, const AutoDiffScalar& a)
{
return s << a.value();
}
AutoDiffScalar(const AutoDiffScalar& other)
: m_value(other.value()), m_derivatives(other.derivatives())
{}
template<typename OtherDerType>
inline AutoDiffScalar& operator=(const AutoDiffScalar<OtherDerType>& other)
{
m_value = other.value();
m_derivatives = other.derivatives();
return *this;
}
inline AutoDiffScalar& operator=(const AutoDiffScalar& other)
{
m_value = other.value();
m_derivatives = other.derivatives();
return *this;
}
// inline operator const Scalar& () const { return m_value; }
// inline operator Scalar& () { return m_value; }
inline const Scalar& value() const { return m_value; }
inline Scalar& value() { return m_value; }
inline const DerType& derivatives() const { return m_derivatives; }
inline DerType& derivatives() { return m_derivatives; }
inline bool operator< (const Scalar& other) const { return m_value < other; }
inline bool operator<=(const Scalar& other) const { return m_value <= other; }
inline bool operator> (const Scalar& other) const { return m_value > other; }
inline bool operator>=(const Scalar& other) const { return m_value >= other; }
inline bool operator==(const Scalar& other) const { return m_value == other; }
inline bool operator!=(const Scalar& other) const { return m_value != other; }
friend inline bool operator< (const Scalar& a, const AutoDiffScalar& b) { return a < b.value(); }
friend inline bool operator<=(const Scalar& a, const AutoDiffScalar& b) { return a <= b.value(); }
friend inline bool operator> (const Scalar& a, const AutoDiffScalar& b) { return a > b.value(); }
friend inline bool operator>=(const Scalar& a, const AutoDiffScalar& b) { return a >= b.value(); }
friend inline bool operator==(const Scalar& a, const AutoDiffScalar& b) { return a == b.value(); }
friend inline bool operator!=(const Scalar& a, const AutoDiffScalar& b) { return a != b.value(); }
template<typename OtherDerType> inline bool operator< (const AutoDiffScalar<OtherDerType>& b) const { return m_value < b.value(); }
template<typename OtherDerType> inline bool operator<=(const AutoDiffScalar<OtherDerType>& b) const { return m_value <= b.value(); }
template<typename OtherDerType> inline bool operator> (const AutoDiffScalar<OtherDerType>& b) const { return m_value > b.value(); }
template<typename OtherDerType> inline bool operator>=(const AutoDiffScalar<OtherDerType>& b) const { return m_value >= b.value(); }
template<typename OtherDerType> inline bool operator==(const AutoDiffScalar<OtherDerType>& b) const { return m_value == b.value(); }
template<typename OtherDerType> inline bool operator!=(const AutoDiffScalar<OtherDerType>& b) const { return m_value != b.value(); }
inline const AutoDiffScalar<DerType&> operator+(const Scalar& other) const
{
return AutoDiffScalar<DerType&>(m_value + other, m_derivatives);
}
friend inline const AutoDiffScalar<DerType&> operator+(const Scalar& a, const AutoDiffScalar& b)
{
return AutoDiffScalar<DerType&>(a + b.value(), b.derivatives());
}
// inline const AutoDiffScalar<DerType&> operator+(const Real& other) const
// {
// return AutoDiffScalar<DerType&>(m_value + other, m_derivatives);
// }
// friend inline const AutoDiffScalar<DerType&> operator+(const Real& a, const AutoDiffScalar& b)
// {
// return AutoDiffScalar<DerType&>(a + b.value(), b.derivatives());
// }
inline AutoDiffScalar& operator+=(const Scalar& other)
{
value() += other;
return *this;
}
template<typename OtherDerType>
inline const AutoDiffScalar<CwiseBinaryOp<internal::scalar_sum_op<Scalar>,const DerType,const typename internal::remove_all<OtherDerType>::type> >
operator+(const AutoDiffScalar<OtherDerType>& other) const
{
internal::make_coherent(m_derivatives, other.derivatives());
return AutoDiffScalar<CwiseBinaryOp<internal::scalar_sum_op<Scalar>,const DerType,const typename internal::remove_all<OtherDerType>::type> >(
m_value + other.value(),
m_derivatives + other.derivatives());
}
template<typename OtherDerType>
inline AutoDiffScalar&
operator+=(const AutoDiffScalar<OtherDerType>& other)
{
(*this) = (*this) + other;
return *this;
}
inline const AutoDiffScalar<DerType&> operator-(const Scalar& b) const
{
return AutoDiffScalar<DerType&>(m_value - b, m_derivatives);
}
friend inline const AutoDiffScalar<CwiseUnaryOp<internal::scalar_opposite_op<Scalar>, const DerType> >
operator-(const Scalar& a, const AutoDiffScalar& b)
{
return AutoDiffScalar<CwiseUnaryOp<internal::scalar_opposite_op<Scalar>, const DerType> >
(a - b.value(), -b.derivatives());
}
inline AutoDiffScalar& operator-=(const Scalar& other)
{
value() -= other;
return *this;
}
template<typename OtherDerType>
inline const AutoDiffScalar<CwiseBinaryOp<internal::scalar_difference_op<Scalar>, const DerType,const typename internal::remove_all<OtherDerType>::type> >
operator-(const AutoDiffScalar<OtherDerType>& other) const
{
internal::make_coherent(m_derivatives, other.derivatives());
return AutoDiffScalar<CwiseBinaryOp<internal::scalar_difference_op<Scalar>, const DerType,const typename internal::remove_all<OtherDerType>::type> >(
m_value - other.value(),
m_derivatives - other.derivatives());
}
template<typename OtherDerType>
inline AutoDiffScalar&
operator-=(const AutoDiffScalar<OtherDerType>& other)
{
*this = *this - other;
return *this;
}
inline const AutoDiffScalar<CwiseUnaryOp<internal::scalar_opposite_op<Scalar>, const DerType> >
operator-() const
{
return AutoDiffScalar<CwiseUnaryOp<internal::scalar_opposite_op<Scalar>, const DerType> >(
-m_value,
-m_derivatives);
}
inline const AutoDiffScalar<CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const DerType> >
operator*(const Scalar& other) const
{
return AutoDiffScalar<CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const DerType> >(
m_value * other,
(m_derivatives * other));
}
friend inline const AutoDiffScalar<CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const DerType> >
operator*(const Scalar& other, const AutoDiffScalar& a)
{
return AutoDiffScalar<CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const DerType> >(
a.value() * other,
a.derivatives() * other);
}
// inline const AutoDiffScalar<typename CwiseUnaryOp<internal::scalar_multiple_op<Real>, DerType>::Type >
// operator*(const Real& other) const
// {
// return AutoDiffScalar<typename CwiseUnaryOp<internal::scalar_multiple_op<Real>, DerType>::Type >(
// m_value * other,
// (m_derivatives * other));
// }
//
// friend inline const AutoDiffScalar<typename CwiseUnaryOp<internal::scalar_multiple_op<Real>, DerType>::Type >
// operator*(const Real& other, const AutoDiffScalar& a)
// {
// return AutoDiffScalar<typename CwiseUnaryOp<internal::scalar_multiple_op<Real>, DerType>::Type >(
// a.value() * other,
// a.derivatives() * other);
// }
inline const AutoDiffScalar<CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const DerType> >
operator/(const Scalar& other) const
{
return AutoDiffScalar<CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const DerType> >(
m_value / other,
(m_derivatives * (Scalar(1)/other)));
}
friend inline const AutoDiffScalar<CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const DerType> >
operator/(const Scalar& other, const AutoDiffScalar& a)
{
return AutoDiffScalar<CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const DerType> >(
other / a.value(),
a.derivatives() * (Scalar(-other) / (a.value()*a.value())));
}
// inline const AutoDiffScalar<typename CwiseUnaryOp<internal::scalar_multiple_op<Real>, DerType>::Type >
// operator/(const Real& other) const
// {
// return AutoDiffScalar<typename CwiseUnaryOp<internal::scalar_multiple_op<Real>, DerType>::Type >(
// m_value / other,
// (m_derivatives * (Real(1)/other)));
// }
//
// friend inline const AutoDiffScalar<typename CwiseUnaryOp<internal::scalar_multiple_op<Real>, DerType>::Type >
// operator/(const Real& other, const AutoDiffScalar& a)
// {
// return AutoDiffScalar<typename CwiseUnaryOp<internal::scalar_multiple_op<Real>, DerType>::Type >(
// other / a.value(),
// a.derivatives() * (-Real(1)/other));
// }
template<typename OtherDerType>
inline const AutoDiffScalar<CwiseUnaryOp<internal::scalar_multiple_op<Scalar>,
const CwiseBinaryOp<internal::scalar_difference_op<Scalar>,
const CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const DerType>,
const CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const typename internal::remove_all<OtherDerType>::type > > > >
operator/(const AutoDiffScalar<OtherDerType>& other) const
{
internal::make_coherent(m_derivatives, other.derivatives());
return AutoDiffScalar<CwiseUnaryOp<internal::scalar_multiple_op<Scalar>,
const CwiseBinaryOp<internal::scalar_difference_op<Scalar>,
const CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const DerType>,
const CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const typename internal::remove_all<OtherDerType>::type > > > >(
m_value / other.value(),
((m_derivatives * other.value()) - (m_value * other.derivatives()))
* (Scalar(1)/(other.value()*other.value())));
}
template<typename OtherDerType>
inline const AutoDiffScalar<CwiseBinaryOp<internal::scalar_sum_op<Scalar>,
const CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const DerType>,
const CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const typename internal::remove_all<OtherDerType>::type> > >
operator*(const AutoDiffScalar<OtherDerType>& other) const
{
internal::make_coherent(m_derivatives, other.derivatives());
return AutoDiffScalar<const CwiseBinaryOp<internal::scalar_sum_op<Scalar>,
const CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const DerType>,
const CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, const typename internal::remove_all<OtherDerType>::type > > >(
m_value * other.value(),
(m_derivatives * other.value()) + (m_value * other.derivatives()));
}
inline AutoDiffScalar& operator*=(const Scalar& other)
{
*this = *this * other;
return *this;
}
template<typename OtherDerType>
inline AutoDiffScalar& operator*=(const AutoDiffScalar<OtherDerType>& other)
{
*this = *this * other;
return *this;
}
inline AutoDiffScalar& operator/=(const Scalar& other)
{
*this = *this / other;
return *this;
}
template<typename OtherDerType>
inline AutoDiffScalar& operator/=(const AutoDiffScalar<OtherDerType>& other)
{
*this = *this / other;
return *this;
}
protected:
Scalar m_value;
DerType m_derivatives;
};
namespace internal {
template<typename _DerType>
struct auto_diff_special_op<_DerType, true>
// : auto_diff_scalar_op<_DerType, typename NumTraits<Scalar>::Real,
// is_same<Scalar,typename NumTraits<Scalar>::Real>::value>
{
typedef typename remove_all<_DerType>::type DerType;
typedef typename traits<DerType>::Scalar Scalar;
typedef typename NumTraits<Scalar>::Real Real;
// typedef auto_diff_scalar_op<_DerType, typename NumTraits<Scalar>::Real,
// is_same<Scalar,typename NumTraits<Scalar>::Real>::value> Base;
// using Base::operator+;
// using Base::operator+=;
// using Base::operator-;
// using Base::operator-=;
// using Base::operator*;
// using Base::operator*=;
const AutoDiffScalar<_DerType>& derived() const { return *static_cast<const AutoDiffScalar<_DerType>*>(this); }
AutoDiffScalar<_DerType>& derived() { return *static_cast<AutoDiffScalar<_DerType>*>(this); }
inline const AutoDiffScalar<DerType&> operator+(const Real& other) const
{
return AutoDiffScalar<DerType&>(derived().value() + other, derived().derivatives());
}
friend inline const AutoDiffScalar<DerType&> operator+(const Real& a, const AutoDiffScalar<_DerType>& b)
{
return AutoDiffScalar<DerType&>(a + b.value(), b.derivatives());
}
inline AutoDiffScalar<_DerType>& operator+=(const Real& other)
{
derived().value() += other;
return derived();
}
inline const AutoDiffScalar<typename CwiseUnaryOp<scalar_multiple2_op<Scalar,Real>, DerType>::Type >
operator*(const Real& other) const
{
return AutoDiffScalar<typename CwiseUnaryOp<scalar_multiple2_op<Scalar,Real>, DerType>::Type >(
derived().value() * other,
derived().derivatives() * other);
}
friend inline const AutoDiffScalar<typename CwiseUnaryOp<scalar_multiple2_op<Scalar,Real>, DerType>::Type >
operator*(const Real& other, const AutoDiffScalar<_DerType>& a)
{
return AutoDiffScalar<typename CwiseUnaryOp<scalar_multiple2_op<Scalar,Real>, DerType>::Type >(
a.value() * other,
a.derivatives() * other);
}
inline AutoDiffScalar<_DerType>& operator*=(const Scalar& other)
{
*this = *this * other;
return derived();
}
};
template<typename _DerType>
struct auto_diff_special_op<_DerType, false>
{
void operator*() const;
void operator-() const;
void operator+() const;
};
template<typename A_Scalar, int A_Rows, int A_Cols, int A_Options, int A_MaxRows, int A_MaxCols, typename B>
struct make_coherent_impl<Matrix<A_Scalar, A_Rows, A_Cols, A_Options, A_MaxRows, A_MaxCols>, B> {
typedef Matrix<A_Scalar, A_Rows, A_Cols, A_Options, A_MaxRows, A_MaxCols> A;
static void run(A& a, B& b) {
if((A_Rows==Dynamic || A_Cols==Dynamic) && (a.size()==0))
{
a.resize(b.size());
a.setZero();
}
}
};
template<typename A, typename B_Scalar, int B_Rows, int B_Cols, int B_Options, int B_MaxRows, int B_MaxCols>
struct make_coherent_impl<A, Matrix<B_Scalar, B_Rows, B_Cols, B_Options, B_MaxRows, B_MaxCols> > {
typedef Matrix<B_Scalar, B_Rows, B_Cols, B_Options, B_MaxRows, B_MaxCols> B;
static void run(A& a, B& b) {
if((B_Rows==Dynamic || B_Cols==Dynamic) && (b.size()==0))
{
b.resize(a.size());
b.setZero();
}
}
};
template<typename A_Scalar, int A_Rows, int A_Cols, int A_Options, int A_MaxRows, int A_MaxCols,
typename B_Scalar, int B_Rows, int B_Cols, int B_Options, int B_MaxRows, int B_MaxCols>
struct make_coherent_impl<Matrix<A_Scalar, A_Rows, A_Cols, A_Options, A_MaxRows, A_MaxCols>,
Matrix<B_Scalar, B_Rows, B_Cols, B_Options, B_MaxRows, B_MaxCols> > {
typedef Matrix<A_Scalar, A_Rows, A_Cols, A_Options, A_MaxRows, A_MaxCols> A;
typedef Matrix<B_Scalar, B_Rows, B_Cols, B_Options, B_MaxRows, B_MaxCols> B;
static void run(A& a, B& b) {
if((A_Rows==Dynamic || A_Cols==Dynamic) && (a.size()==0))
{
a.resize(b.size());
a.setZero();
}
else if((B_Rows==Dynamic || B_Cols==Dynamic) && (b.size()==0))
{
b.resize(a.size());
b.setZero();
}
}
};
template<typename A_Scalar, int A_Rows, int A_Cols, int A_Options, int A_MaxRows, int A_MaxCols>
struct scalar_product_traits<Matrix<A_Scalar, A_Rows, A_Cols, A_Options, A_MaxRows, A_MaxCols>,A_Scalar>
{
enum { Defined = 1 };
typedef Matrix<A_Scalar, A_Rows, A_Cols, A_Options, A_MaxRows, A_MaxCols> ReturnType;
};
template<typename A_Scalar, int A_Rows, int A_Cols, int A_Options, int A_MaxRows, int A_MaxCols>
struct scalar_product_traits<A_Scalar, Matrix<A_Scalar, A_Rows, A_Cols, A_Options, A_MaxRows, A_MaxCols> >
{
enum { Defined = 1 };
typedef Matrix<A_Scalar, A_Rows, A_Cols, A_Options, A_MaxRows, A_MaxCols> ReturnType;
};
template<typename DerType>
struct scalar_product_traits<AutoDiffScalar<DerType>,typename DerType::Scalar>
{
enum { Defined = 1 };
typedef AutoDiffScalar<DerType> ReturnType;
};
template<typename DerType>
struct scalar_product_traits<typename DerType::Scalar,AutoDiffScalar<DerType> >
{
enum { Defined = 1 };
typedef AutoDiffScalar<DerType> ReturnType;
};
} // end namespace internal
#define EIGEN_AUTODIFF_DECLARE_GLOBAL_UNARY(FUNC,CODE) \
template<typename DerType> \
inline const Eigen::AutoDiffScalar<Eigen::CwiseUnaryOp<Eigen::internal::scalar_multiple_op<typename Eigen::internal::traits<typename Eigen::internal::remove_all<DerType>::type>::Scalar>, const typename Eigen::internal::remove_all<DerType>::type> > \
FUNC(const Eigen::AutoDiffScalar<DerType>& x) { \
using namespace Eigen; \
typedef typename Eigen::internal::traits<typename Eigen::internal::remove_all<DerType>::type>::Scalar Scalar; \
typedef AutoDiffScalar<CwiseUnaryOp<Eigen::internal::scalar_multiple_op<Scalar>, const typename Eigen::internal::remove_all<DerType>::type> > ReturnType; \
CODE; \
}
template<typename DerType>
inline const AutoDiffScalar<DerType>& conj(const AutoDiffScalar<DerType>& x) { return x; }
template<typename DerType>
inline const AutoDiffScalar<DerType>& real(const AutoDiffScalar<DerType>& x) { return x; }
template<typename DerType>
inline typename DerType::Scalar imag(const AutoDiffScalar<DerType>&) { return 0.; }
template<typename DerType, typename T>
inline AutoDiffScalar<DerType> (min)(const AutoDiffScalar<DerType>& x, const T& y) { return (x <= y ? x : y); }
template<typename DerType, typename T>
inline AutoDiffScalar<DerType> (max)(const AutoDiffScalar<DerType>& x, const T& y) { return (x >= y ? x : y); }
template<typename DerType, typename T>
inline AutoDiffScalar<DerType> (min)(const T& x, const AutoDiffScalar<DerType>& y) { return (x < y ? x : y); }
template<typename DerType, typename T>
inline AutoDiffScalar<DerType> (max)(const T& x, const AutoDiffScalar<DerType>& y) { return (x > y ? x : y); }
EIGEN_AUTODIFF_DECLARE_GLOBAL_UNARY(abs,
using std::abs;
return ReturnType(abs(x.value()), x.derivatives() * (x.value()<0 ? -1 : 1) );)
EIGEN_AUTODIFF_DECLARE_GLOBAL_UNARY(abs2,
using numext::abs2;
return ReturnType(abs2(x.value()), x.derivatives() * (Scalar(2)*x.value()));)
EIGEN_AUTODIFF_DECLARE_GLOBAL_UNARY(sqrt,
using std::sqrt;
Scalar sqrtx = sqrt(x.value());
return ReturnType(sqrtx,x.derivatives() * (Scalar(0.5) / sqrtx));)
EIGEN_AUTODIFF_DECLARE_GLOBAL_UNARY(cos,
using std::cos;
using std::sin;
return ReturnType(cos(x.value()), x.derivatives() * (-sin(x.value())));)
EIGEN_AUTODIFF_DECLARE_GLOBAL_UNARY(sin,
using std::sin;
using std::cos;
return ReturnType(sin(x.value()),x.derivatives() * cos(x.value()));)
EIGEN_AUTODIFF_DECLARE_GLOBAL_UNARY(exp,
using std::exp;
Scalar expx = exp(x.value());
return ReturnType(expx,x.derivatives() * expx);)
EIGEN_AUTODIFF_DECLARE_GLOBAL_UNARY(log,
using std::log;
return ReturnType(log(x.value()),x.derivatives() * (Scalar(1)/x.value()));)
template<typename DerType>
inline const Eigen::AutoDiffScalar<Eigen::CwiseUnaryOp<Eigen::internal::scalar_multiple_op<typename Eigen::internal::traits<DerType>::Scalar>, const DerType> >
pow(const Eigen::AutoDiffScalar<DerType>& x, typename Eigen::internal::traits<DerType>::Scalar y)
{
using namespace Eigen;
typedef typename Eigen::internal::traits<DerType>::Scalar Scalar;
return AutoDiffScalar<CwiseUnaryOp<Eigen::internal::scalar_multiple_op<Scalar>, const DerType> >(
std::pow(x.value(),y),
x.derivatives() * (y * std::pow(x.value(),y-1)));
}
template<typename DerTypeA,typename DerTypeB>
inline const AutoDiffScalar<Matrix<typename internal::traits<DerTypeA>::Scalar,Dynamic,1> >
atan2(const AutoDiffScalar<DerTypeA>& a, const AutoDiffScalar<DerTypeB>& b)
{
using std::atan2;
using std::max;
typedef typename internal::traits<DerTypeA>::Scalar Scalar;
typedef AutoDiffScalar<Matrix<Scalar,Dynamic,1> > PlainADS;
PlainADS ret;
ret.value() = atan2(a.value(), b.value());
Scalar tmp2 = a.value() * a.value();
Scalar tmp3 = b.value() * b.value();
Scalar tmp4 = tmp3/(tmp2+tmp3);
if (tmp4!=0)
ret.derivatives() = (a.derivatives() * b.value() - a.value() * b.derivatives()) * (tmp2+tmp3);
return ret;
}
EIGEN_AUTODIFF_DECLARE_GLOBAL_UNARY(tan,
using std::tan;
using std::cos;
return ReturnType(tan(x.value()),x.derivatives() * (Scalar(1)/numext::abs2(cos(x.value()))));)
EIGEN_AUTODIFF_DECLARE_GLOBAL_UNARY(asin,
using std::sqrt;
using std::asin;
return ReturnType(asin(x.value()),x.derivatives() * (Scalar(1)/sqrt(1-numext::abs2(x.value()))));)
EIGEN_AUTODIFF_DECLARE_GLOBAL_UNARY(acos,
using std::sqrt;
using std::acos;
return ReturnType(acos(x.value()),x.derivatives() * (Scalar(-1)/sqrt(1-numext::abs2(x.value()))));)
#undef EIGEN_AUTODIFF_DECLARE_GLOBAL_UNARY
template<typename DerType> struct NumTraits<AutoDiffScalar<DerType> >
: NumTraits< typename NumTraits<typename DerType::Scalar>::Real >
{
typedef AutoDiffScalar<Matrix<typename NumTraits<typename DerType::Scalar>::Real,DerType::RowsAtCompileTime,DerType::ColsAtCompileTime> > Real;
typedef AutoDiffScalar<DerType> NonInteger;
typedef AutoDiffScalar<DerType> Nested;
enum{
RequireInitialization = 1
};
};
}
#endif // EIGEN_AUTODIFF_SCALAR_H

View File

@@ -0,0 +1,220 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_AUTODIFF_VECTOR_H
#define EIGEN_AUTODIFF_VECTOR_H
namespace Eigen {
/* \class AutoDiffScalar
* \brief A scalar type replacement with automatic differentation capability
*
* \param DerType the vector type used to store/represent the derivatives (e.g. Vector3f)
*
* This class represents a scalar value while tracking its respective derivatives.
*
* It supports the following list of global math function:
* - std::abs, std::sqrt, std::pow, std::exp, std::log, std::sin, std::cos,
* - internal::abs, internal::sqrt, numext::pow, internal::exp, internal::log, internal::sin, internal::cos,
* - internal::conj, internal::real, internal::imag, numext::abs2.
*
* AutoDiffScalar can be used as the scalar type of an Eigen::Matrix object. However,
* in that case, the expression template mechanism only occurs at the top Matrix level,
* while derivatives are computed right away.
*
*/
template<typename ValueType, typename JacobianType>
class AutoDiffVector
{
public:
//typedef typename internal::traits<ValueType>::Scalar Scalar;
typedef typename internal::traits<ValueType>::Scalar BaseScalar;
typedef AutoDiffScalar<Matrix<BaseScalar,JacobianType::RowsAtCompileTime,1> > ActiveScalar;
typedef ActiveScalar Scalar;
typedef AutoDiffScalar<typename JacobianType::ColXpr> CoeffType;
typedef typename JacobianType::Index Index;
inline AutoDiffVector() {}
inline AutoDiffVector(const ValueType& values)
: m_values(values)
{
m_jacobian.setZero();
}
CoeffType operator[] (Index i) { return CoeffType(m_values[i], m_jacobian.col(i)); }
const CoeffType operator[] (Index i) const { return CoeffType(m_values[i], m_jacobian.col(i)); }
CoeffType operator() (Index i) { return CoeffType(m_values[i], m_jacobian.col(i)); }
const CoeffType operator() (Index i) const { return CoeffType(m_values[i], m_jacobian.col(i)); }
CoeffType coeffRef(Index i) { return CoeffType(m_values[i], m_jacobian.col(i)); }
const CoeffType coeffRef(Index i) const { return CoeffType(m_values[i], m_jacobian.col(i)); }
Index size() const { return m_values.size(); }
// FIXME here we could return an expression of the sum
Scalar sum() const { /*std::cerr << "sum \n\n";*/ /*std::cerr << m_jacobian.rowwise().sum() << "\n\n";*/ return Scalar(m_values.sum(), m_jacobian.rowwise().sum()); }
inline AutoDiffVector(const ValueType& values, const JacobianType& jac)
: m_values(values), m_jacobian(jac)
{}
template<typename OtherValueType, typename OtherJacobianType>
inline AutoDiffVector(const AutoDiffVector<OtherValueType, OtherJacobianType>& other)
: m_values(other.values()), m_jacobian(other.jacobian())
{}
inline AutoDiffVector(const AutoDiffVector& other)
: m_values(other.values()), m_jacobian(other.jacobian())
{}
template<typename OtherValueType, typename OtherJacobianType>
inline AutoDiffVector& operator=(const AutoDiffVector<OtherValueType, OtherJacobianType>& other)
{
m_values = other.values();
m_jacobian = other.jacobian();
return *this;
}
inline AutoDiffVector& operator=(const AutoDiffVector& other)
{
m_values = other.values();
m_jacobian = other.jacobian();
return *this;
}
inline const ValueType& values() const { return m_values; }
inline ValueType& values() { return m_values; }
inline const JacobianType& jacobian() const { return m_jacobian; }
inline JacobianType& jacobian() { return m_jacobian; }
template<typename OtherValueType,typename OtherJacobianType>
inline const AutoDiffVector<
typename MakeCwiseBinaryOp<internal::scalar_sum_op<BaseScalar>,ValueType,OtherValueType>::Type,
typename MakeCwiseBinaryOp<internal::scalar_sum_op<BaseScalar>,JacobianType,OtherJacobianType>::Type >
operator+(const AutoDiffVector<OtherValueType,OtherJacobianType>& other) const
{
return AutoDiffVector<
typename MakeCwiseBinaryOp<internal::scalar_sum_op<BaseScalar>,ValueType,OtherValueType>::Type,
typename MakeCwiseBinaryOp<internal::scalar_sum_op<BaseScalar>,JacobianType,OtherJacobianType>::Type >(
m_values + other.values(),
m_jacobian + other.jacobian());
}
template<typename OtherValueType, typename OtherJacobianType>
inline AutoDiffVector&
operator+=(const AutoDiffVector<OtherValueType,OtherJacobianType>& other)
{
m_values += other.values();
m_jacobian += other.jacobian();
return *this;
}
template<typename OtherValueType,typename OtherJacobianType>
inline const AutoDiffVector<
typename MakeCwiseBinaryOp<internal::scalar_difference_op<Scalar>,ValueType,OtherValueType>::Type,
typename MakeCwiseBinaryOp<internal::scalar_difference_op<Scalar>,JacobianType,OtherJacobianType>::Type >
operator-(const AutoDiffVector<OtherValueType,OtherJacobianType>& other) const
{
return AutoDiffVector<
typename MakeCwiseBinaryOp<internal::scalar_difference_op<Scalar>,ValueType,OtherValueType>::Type,
typename MakeCwiseBinaryOp<internal::scalar_difference_op<Scalar>,JacobianType,OtherJacobianType>::Type >(
m_values - other.values(),
m_jacobian - other.jacobian());
}
template<typename OtherValueType, typename OtherJacobianType>
inline AutoDiffVector&
operator-=(const AutoDiffVector<OtherValueType,OtherJacobianType>& other)
{
m_values -= other.values();
m_jacobian -= other.jacobian();
return *this;
}
inline const AutoDiffVector<
typename MakeCwiseUnaryOp<internal::scalar_opposite_op<Scalar>, ValueType>::Type,
typename MakeCwiseUnaryOp<internal::scalar_opposite_op<Scalar>, JacobianType>::Type >
operator-() const
{
return AutoDiffVector<
typename MakeCwiseUnaryOp<internal::scalar_opposite_op<Scalar>, ValueType>::Type,
typename MakeCwiseUnaryOp<internal::scalar_opposite_op<Scalar>, JacobianType>::Type >(
-m_values,
-m_jacobian);
}
inline const AutoDiffVector<
typename MakeCwiseUnaryOp<internal::scalar_multiple_op<Scalar>, ValueType>::Type,
typename MakeCwiseUnaryOp<internal::scalar_multiple_op<Scalar>, JacobianType>::Type>
operator*(const BaseScalar& other) const
{
return AutoDiffVector<
typename MakeCwiseUnaryOp<internal::scalar_multiple_op<Scalar>, ValueType>::Type,
typename MakeCwiseUnaryOp<internal::scalar_multiple_op<Scalar>, JacobianType>::Type >(
m_values * other,
m_jacobian * other);
}
friend inline const AutoDiffVector<
typename MakeCwiseUnaryOp<internal::scalar_multiple_op<Scalar>, ValueType>::Type,
typename MakeCwiseUnaryOp<internal::scalar_multiple_op<Scalar>, JacobianType>::Type >
operator*(const Scalar& other, const AutoDiffVector& v)
{
return AutoDiffVector<
typename MakeCwiseUnaryOp<internal::scalar_multiple_op<Scalar>, ValueType>::Type,
typename MakeCwiseUnaryOp<internal::scalar_multiple_op<Scalar>, JacobianType>::Type >(
v.values() * other,
v.jacobian() * other);
}
// template<typename OtherValueType,typename OtherJacobianType>
// inline const AutoDiffVector<
// CwiseBinaryOp<internal::scalar_multiple_op<Scalar>, ValueType, OtherValueType>
// CwiseBinaryOp<internal::scalar_sum_op<Scalar>,
// CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, JacobianType>,
// CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, OtherJacobianType> > >
// operator*(const AutoDiffVector<OtherValueType,OtherJacobianType>& other) const
// {
// return AutoDiffVector<
// CwiseBinaryOp<internal::scalar_multiple_op<Scalar>, ValueType, OtherValueType>
// CwiseBinaryOp<internal::scalar_sum_op<Scalar>,
// CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, JacobianType>,
// CwiseUnaryOp<internal::scalar_multiple_op<Scalar>, OtherJacobianType> > >(
// m_values.cwise() * other.values(),
// (m_jacobian * other.values()) + (m_values * other.jacobian()));
// }
inline AutoDiffVector& operator*=(const Scalar& other)
{
m_values *= other;
m_jacobian *= other;
return *this;
}
template<typename OtherValueType,typename OtherJacobianType>
inline AutoDiffVector& operator*=(const AutoDiffVector<OtherValueType,OtherJacobianType>& other)
{
*this = *this * other;
return *this;
}
protected:
ValueType m_values;
JacobianType m_jacobian;
};
}
#endif // EIGEN_AUTODIFF_VECTOR_H

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_AutoDiff_SRCS "*.h")
INSTALL(FILES
${Eigen_AutoDiff_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/AutoDiff COMPONENT Devel
)

View File

@@ -0,0 +1,293 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Ilya Baran <ibaran@mit.edu>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_BVALGORITHMS_H
#define EIGEN_BVALGORITHMS_H
namespace Eigen {
namespace internal {
#ifndef EIGEN_PARSED_BY_DOXYGEN
template<typename BVH, typename Intersector>
bool intersect_helper(const BVH &tree, Intersector &intersector, typename BVH::Index root)
{
typedef typename BVH::Index Index;
typedef typename BVH::VolumeIterator VolIter;
typedef typename BVH::ObjectIterator ObjIter;
VolIter vBegin = VolIter(), vEnd = VolIter();
ObjIter oBegin = ObjIter(), oEnd = ObjIter();
std::vector<Index> todo(1, root);
while(!todo.empty()) {
tree.getChildren(todo.back(), vBegin, vEnd, oBegin, oEnd);
todo.pop_back();
for(; vBegin != vEnd; ++vBegin) //go through child volumes
if(intersector.intersectVolume(tree.getVolume(*vBegin)))
todo.push_back(*vBegin);
for(; oBegin != oEnd; ++oBegin) //go through child objects
if(intersector.intersectObject(*oBegin))
return true; //intersector said to stop query
}
return false;
}
#endif //not EIGEN_PARSED_BY_DOXYGEN
template<typename Volume1, typename Object1, typename Object2, typename Intersector>
struct intersector_helper1
{
intersector_helper1(const Object2 &inStored, Intersector &in) : stored(inStored), intersector(in) {}
bool intersectVolume(const Volume1 &vol) { return intersector.intersectVolumeObject(vol, stored); }
bool intersectObject(const Object1 &obj) { return intersector.intersectObjectObject(obj, stored); }
Object2 stored;
Intersector &intersector;
private:
intersector_helper1& operator=(const intersector_helper1&);
};
template<typename Volume2, typename Object2, typename Object1, typename Intersector>
struct intersector_helper2
{
intersector_helper2(const Object1 &inStored, Intersector &in) : stored(inStored), intersector(in) {}
bool intersectVolume(const Volume2 &vol) { return intersector.intersectObjectVolume(stored, vol); }
bool intersectObject(const Object2 &obj) { return intersector.intersectObjectObject(stored, obj); }
Object1 stored;
Intersector &intersector;
private:
intersector_helper2& operator=(const intersector_helper2&);
};
} // end namespace internal
/** Given a BVH, runs the query encapsulated by \a intersector.
* The Intersector type must provide the following members: \code
bool intersectVolume(const BVH::Volume &volume) //returns true if volume intersects the query
bool intersectObject(const BVH::Object &object) //returns true if the search should terminate immediately
\endcode
*/
template<typename BVH, typename Intersector>
void BVIntersect(const BVH &tree, Intersector &intersector)
{
internal::intersect_helper(tree, intersector, tree.getRootIndex());
}
/** Given two BVH's, runs the query on their Cartesian product encapsulated by \a intersector.
* The Intersector type must provide the following members: \code
bool intersectVolumeVolume(const BVH1::Volume &v1, const BVH2::Volume &v2) //returns true if product of volumes intersects the query
bool intersectVolumeObject(const BVH1::Volume &v1, const BVH2::Object &o2) //returns true if the volume-object product intersects the query
bool intersectObjectVolume(const BVH1::Object &o1, const BVH2::Volume &v2) //returns true if the volume-object product intersects the query
bool intersectObjectObject(const BVH1::Object &o1, const BVH2::Object &o2) //returns true if the search should terminate immediately
\endcode
*/
template<typename BVH1, typename BVH2, typename Intersector>
void BVIntersect(const BVH1 &tree1, const BVH2 &tree2, Intersector &intersector) //TODO: tandem descent when it makes sense
{
typedef typename BVH1::Index Index1;
typedef typename BVH2::Index Index2;
typedef internal::intersector_helper1<typename BVH1::Volume, typename BVH1::Object, typename BVH2::Object, Intersector> Helper1;
typedef internal::intersector_helper2<typename BVH2::Volume, typename BVH2::Object, typename BVH1::Object, Intersector> Helper2;
typedef typename BVH1::VolumeIterator VolIter1;
typedef typename BVH1::ObjectIterator ObjIter1;
typedef typename BVH2::VolumeIterator VolIter2;
typedef typename BVH2::ObjectIterator ObjIter2;
VolIter1 vBegin1 = VolIter1(), vEnd1 = VolIter1();
ObjIter1 oBegin1 = ObjIter1(), oEnd1 = ObjIter1();
VolIter2 vBegin2 = VolIter2(), vEnd2 = VolIter2(), vCur2 = VolIter2();
ObjIter2 oBegin2 = ObjIter2(), oEnd2 = ObjIter2(), oCur2 = ObjIter2();
std::vector<std::pair<Index1, Index2> > todo(1, std::make_pair(tree1.getRootIndex(), tree2.getRootIndex()));
while(!todo.empty()) {
tree1.getChildren(todo.back().first, vBegin1, vEnd1, oBegin1, oEnd1);
tree2.getChildren(todo.back().second, vBegin2, vEnd2, oBegin2, oEnd2);
todo.pop_back();
for(; vBegin1 != vEnd1; ++vBegin1) { //go through child volumes of first tree
const typename BVH1::Volume &vol1 = tree1.getVolume(*vBegin1);
for(vCur2 = vBegin2; vCur2 != vEnd2; ++vCur2) { //go through child volumes of second tree
if(intersector.intersectVolumeVolume(vol1, tree2.getVolume(*vCur2)))
todo.push_back(std::make_pair(*vBegin1, *vCur2));
}
for(oCur2 = oBegin2; oCur2 != oEnd2; ++oCur2) {//go through child objects of second tree
Helper1 helper(*oCur2, intersector);
if(internal::intersect_helper(tree1, helper, *vBegin1))
return; //intersector said to stop query
}
}
for(; oBegin1 != oEnd1; ++oBegin1) { //go through child objects of first tree
for(vCur2 = vBegin2; vCur2 != vEnd2; ++vCur2) { //go through child volumes of second tree
Helper2 helper(*oBegin1, intersector);
if(internal::intersect_helper(tree2, helper, *vCur2))
return; //intersector said to stop query
}
for(oCur2 = oBegin2; oCur2 != oEnd2; ++oCur2) {//go through child objects of second tree
if(intersector.intersectObjectObject(*oBegin1, *oCur2))
return; //intersector said to stop query
}
}
}
}
namespace internal {
#ifndef EIGEN_PARSED_BY_DOXYGEN
template<typename BVH, typename Minimizer>
typename Minimizer::Scalar minimize_helper(const BVH &tree, Minimizer &minimizer, typename BVH::Index root, typename Minimizer::Scalar minimum)
{
typedef typename Minimizer::Scalar Scalar;
typedef typename BVH::Index Index;
typedef std::pair<Scalar, Index> QueueElement; //first element is priority
typedef typename BVH::VolumeIterator VolIter;
typedef typename BVH::ObjectIterator ObjIter;
VolIter vBegin = VolIter(), vEnd = VolIter();
ObjIter oBegin = ObjIter(), oEnd = ObjIter();
std::priority_queue<QueueElement, std::vector<QueueElement>, std::greater<QueueElement> > todo; //smallest is at the top
todo.push(std::make_pair(Scalar(), root));
while(!todo.empty()) {
tree.getChildren(todo.top().second, vBegin, vEnd, oBegin, oEnd);
todo.pop();
for(; oBegin != oEnd; ++oBegin) //go through child objects
minimum = (std::min)(minimum, minimizer.minimumOnObject(*oBegin));
for(; vBegin != vEnd; ++vBegin) { //go through child volumes
Scalar val = minimizer.minimumOnVolume(tree.getVolume(*vBegin));
if(val < minimum)
todo.push(std::make_pair(val, *vBegin));
}
}
return minimum;
}
#endif //not EIGEN_PARSED_BY_DOXYGEN
template<typename Volume1, typename Object1, typename Object2, typename Minimizer>
struct minimizer_helper1
{
typedef typename Minimizer::Scalar Scalar;
minimizer_helper1(const Object2 &inStored, Minimizer &m) : stored(inStored), minimizer(m) {}
Scalar minimumOnVolume(const Volume1 &vol) { return minimizer.minimumOnVolumeObject(vol, stored); }
Scalar minimumOnObject(const Object1 &obj) { return minimizer.minimumOnObjectObject(obj, stored); }
Object2 stored;
Minimizer &minimizer;
private:
minimizer_helper1& operator=(const minimizer_helper1&);
};
template<typename Volume2, typename Object2, typename Object1, typename Minimizer>
struct minimizer_helper2
{
typedef typename Minimizer::Scalar Scalar;
minimizer_helper2(const Object1 &inStored, Minimizer &m) : stored(inStored), minimizer(m) {}
Scalar minimumOnVolume(const Volume2 &vol) { return minimizer.minimumOnObjectVolume(stored, vol); }
Scalar minimumOnObject(const Object2 &obj) { return minimizer.minimumOnObjectObject(stored, obj); }
Object1 stored;
Minimizer &minimizer;
private:
minimizer_helper2& operator=(const minimizer_helper2&);
};
} // end namespace internal
/** Given a BVH, runs the query encapsulated by \a minimizer.
* \returns the minimum value.
* The Minimizer type must provide the following members: \code
typedef Scalar //the numeric type of what is being minimized--not necessarily the Scalar type of the BVH (if it has one)
Scalar minimumOnVolume(const BVH::Volume &volume)
Scalar minimumOnObject(const BVH::Object &object)
\endcode
*/
template<typename BVH, typename Minimizer>
typename Minimizer::Scalar BVMinimize(const BVH &tree, Minimizer &minimizer)
{
return internal::minimize_helper(tree, minimizer, tree.getRootIndex(), (std::numeric_limits<typename Minimizer::Scalar>::max)());
}
/** Given two BVH's, runs the query on their cartesian product encapsulated by \a minimizer.
* \returns the minimum value.
* The Minimizer type must provide the following members: \code
typedef Scalar //the numeric type of what is being minimized--not necessarily the Scalar type of the BVH (if it has one)
Scalar minimumOnVolumeVolume(const BVH1::Volume &v1, const BVH2::Volume &v2)
Scalar minimumOnVolumeObject(const BVH1::Volume &v1, const BVH2::Object &o2)
Scalar minimumOnObjectVolume(const BVH1::Object &o1, const BVH2::Volume &v2)
Scalar minimumOnObjectObject(const BVH1::Object &o1, const BVH2::Object &o2)
\endcode
*/
template<typename BVH1, typename BVH2, typename Minimizer>
typename Minimizer::Scalar BVMinimize(const BVH1 &tree1, const BVH2 &tree2, Minimizer &minimizer)
{
typedef typename Minimizer::Scalar Scalar;
typedef typename BVH1::Index Index1;
typedef typename BVH2::Index Index2;
typedef internal::minimizer_helper1<typename BVH1::Volume, typename BVH1::Object, typename BVH2::Object, Minimizer> Helper1;
typedef internal::minimizer_helper2<typename BVH2::Volume, typename BVH2::Object, typename BVH1::Object, Minimizer> Helper2;
typedef std::pair<Scalar, std::pair<Index1, Index2> > QueueElement; //first element is priority
typedef typename BVH1::VolumeIterator VolIter1;
typedef typename BVH1::ObjectIterator ObjIter1;
typedef typename BVH2::VolumeIterator VolIter2;
typedef typename BVH2::ObjectIterator ObjIter2;
VolIter1 vBegin1 = VolIter1(), vEnd1 = VolIter1();
ObjIter1 oBegin1 = ObjIter1(), oEnd1 = ObjIter1();
VolIter2 vBegin2 = VolIter2(), vEnd2 = VolIter2(), vCur2 = VolIter2();
ObjIter2 oBegin2 = ObjIter2(), oEnd2 = ObjIter2(), oCur2 = ObjIter2();
std::priority_queue<QueueElement, std::vector<QueueElement>, std::greater<QueueElement> > todo; //smallest is at the top
Scalar minimum = (std::numeric_limits<Scalar>::max)();
todo.push(std::make_pair(Scalar(), std::make_pair(tree1.getRootIndex(), tree2.getRootIndex())));
while(!todo.empty()) {
tree1.getChildren(todo.top().second.first, vBegin1, vEnd1, oBegin1, oEnd1);
tree2.getChildren(todo.top().second.second, vBegin2, vEnd2, oBegin2, oEnd2);
todo.pop();
for(; oBegin1 != oEnd1; ++oBegin1) { //go through child objects of first tree
for(oCur2 = oBegin2; oCur2 != oEnd2; ++oCur2) {//go through child objects of second tree
minimum = (std::min)(minimum, minimizer.minimumOnObjectObject(*oBegin1, *oCur2));
}
for(vCur2 = vBegin2; vCur2 != vEnd2; ++vCur2) { //go through child volumes of second tree
Helper2 helper(*oBegin1, minimizer);
minimum = (std::min)(minimum, internal::minimize_helper(tree2, helper, *vCur2, minimum));
}
}
for(; vBegin1 != vEnd1; ++vBegin1) { //go through child volumes of first tree
const typename BVH1::Volume &vol1 = tree1.getVolume(*vBegin1);
for(oCur2 = oBegin2; oCur2 != oEnd2; ++oCur2) {//go through child objects of second tree
Helper1 helper(*oCur2, minimizer);
minimum = (std::min)(minimum, internal::minimize_helper(tree1, helper, *vBegin1, minimum));
}
for(vCur2 = vBegin2; vCur2 != vEnd2; ++vCur2) { //go through child volumes of second tree
Scalar val = minimizer.minimumOnVolumeVolume(vol1, tree2.getVolume(*vCur2));
if(val < minimum)
todo.push(std::make_pair(val, std::make_pair(*vBegin1, *vCur2)));
}
}
}
return minimum;
}
} // end namespace Eigen
#endif // EIGEN_BVALGORITHMS_H

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_BVH_SRCS "*.h")
INSTALL(FILES
${Eigen_BVH_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/BVH COMPONENT Devel
)

View File

@@ -0,0 +1,222 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Ilya Baran <ibaran@mit.edu>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef KDBVH_H_INCLUDED
#define KDBVH_H_INCLUDED
namespace Eigen {
namespace internal {
//internal pair class for the BVH--used instead of std::pair because of alignment
template<typename Scalar, int Dim>
struct vector_int_pair
{
EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(Scalar, Dim)
typedef Matrix<Scalar, Dim, 1> VectorType;
vector_int_pair(const VectorType &v, int i) : first(v), second(i) {}
VectorType first;
int second;
};
//these templates help the tree initializer get the bounding boxes either from a provided
//iterator range or using bounding_box in a unified way
template<typename ObjectList, typename VolumeList, typename BoxIter>
struct get_boxes_helper {
void operator()(const ObjectList &objects, BoxIter boxBegin, BoxIter boxEnd, VolumeList &outBoxes)
{
outBoxes.insert(outBoxes.end(), boxBegin, boxEnd);
eigen_assert(outBoxes.size() == objects.size());
}
};
template<typename ObjectList, typename VolumeList>
struct get_boxes_helper<ObjectList, VolumeList, int> {
void operator()(const ObjectList &objects, int, int, VolumeList &outBoxes)
{
outBoxes.reserve(objects.size());
for(int i = 0; i < (int)objects.size(); ++i)
outBoxes.push_back(bounding_box(objects[i]));
}
};
} // end namespace internal
/** \class KdBVH
* \brief A simple bounding volume hierarchy based on AlignedBox
*
* \param _Scalar The underlying scalar type of the bounding boxes
* \param _Dim The dimension of the space in which the hierarchy lives
* \param _Object The object type that lives in the hierarchy. It must have value semantics. Either bounding_box(_Object) must
* be defined and return an AlignedBox<_Scalar, _Dim> or bounding boxes must be provided to the tree initializer.
*
* This class provides a simple (as opposed to optimized) implementation of a bounding volume hierarchy analogous to a Kd-tree.
* Given a sequence of objects, it computes their bounding boxes, constructs a Kd-tree of their centers
* and builds a BVH with the structure of that Kd-tree. When the elements of the tree are too expensive to be copied around,
* it is useful for _Object to be a pointer.
*/
template<typename _Scalar, int _Dim, typename _Object> class KdBVH
{
public:
enum { Dim = _Dim };
typedef _Object Object;
typedef std::vector<Object, aligned_allocator<Object> > ObjectList;
typedef _Scalar Scalar;
typedef AlignedBox<Scalar, Dim> Volume;
typedef std::vector<Volume, aligned_allocator<Volume> > VolumeList;
typedef int Index;
typedef const int *VolumeIterator; //the iterators are just pointers into the tree's vectors
typedef const Object *ObjectIterator;
KdBVH() {}
/** Given an iterator range over \a Object references, constructs the BVH. Requires that bounding_box(Object) return a Volume. */
template<typename Iter> KdBVH(Iter begin, Iter end) { init(begin, end, 0, 0); } //int is recognized by init as not being an iterator type
/** Given an iterator range over \a Object references and an iterator range over their bounding boxes, constructs the BVH */
template<typename OIter, typename BIter> KdBVH(OIter begin, OIter end, BIter boxBegin, BIter boxEnd) { init(begin, end, boxBegin, boxEnd); }
/** Given an iterator range over \a Object references, constructs the BVH, overwriting whatever is in there currently.
* Requires that bounding_box(Object) return a Volume. */
template<typename Iter> void init(Iter begin, Iter end) { init(begin, end, 0, 0); }
/** Given an iterator range over \a Object references and an iterator range over their bounding boxes,
* constructs the BVH, overwriting whatever is in there currently. */
template<typename OIter, typename BIter> void init(OIter begin, OIter end, BIter boxBegin, BIter boxEnd)
{
objects.clear();
boxes.clear();
children.clear();
objects.insert(objects.end(), begin, end);
int n = static_cast<int>(objects.size());
if(n < 2)
return; //if we have at most one object, we don't need any internal nodes
VolumeList objBoxes;
VIPairList objCenters;
//compute the bounding boxes depending on BIter type
internal::get_boxes_helper<ObjectList, VolumeList, BIter>()(objects, boxBegin, boxEnd, objBoxes);
objCenters.reserve(n);
boxes.reserve(n - 1);
children.reserve(2 * n - 2);
for(int i = 0; i < n; ++i)
objCenters.push_back(VIPair(objBoxes[i].center(), i));
build(objCenters, 0, n, objBoxes, 0); //the recursive part of the algorithm
ObjectList tmp(n);
tmp.swap(objects);
for(int i = 0; i < n; ++i)
objects[i] = tmp[objCenters[i].second];
}
/** \returns the index of the root of the hierarchy */
inline Index getRootIndex() const { return (int)boxes.size() - 1; }
/** Given an \a index of a node, on exit, \a outVBegin and \a outVEnd range over the indices of the volume children of the node
* and \a outOBegin and \a outOEnd range over the object children of the node */
EIGEN_STRONG_INLINE void getChildren(Index index, VolumeIterator &outVBegin, VolumeIterator &outVEnd,
ObjectIterator &outOBegin, ObjectIterator &outOEnd) const
{ //inlining this function should open lots of optimization opportunities to the compiler
if(index < 0) {
outVBegin = outVEnd;
if(!objects.empty())
outOBegin = &(objects[0]);
outOEnd = outOBegin + objects.size(); //output all objects--necessary when the tree has only one object
return;
}
int numBoxes = static_cast<int>(boxes.size());
int idx = index * 2;
if(children[idx + 1] < numBoxes) { //second index is always bigger
outVBegin = &(children[idx]);
outVEnd = outVBegin + 2;
outOBegin = outOEnd;
}
else if(children[idx] >= numBoxes) { //if both children are objects
outVBegin = outVEnd;
outOBegin = &(objects[children[idx] - numBoxes]);
outOEnd = outOBegin + 2;
} else { //if the first child is a volume and the second is an object
outVBegin = &(children[idx]);
outVEnd = outVBegin + 1;
outOBegin = &(objects[children[idx + 1] - numBoxes]);
outOEnd = outOBegin + 1;
}
}
/** \returns the bounding box of the node at \a index */
inline const Volume &getVolume(Index index) const
{
return boxes[index];
}
private:
typedef internal::vector_int_pair<Scalar, Dim> VIPair;
typedef std::vector<VIPair, aligned_allocator<VIPair> > VIPairList;
typedef Matrix<Scalar, Dim, 1> VectorType;
struct VectorComparator //compares vectors, or, more specificall, VIPairs along a particular dimension
{
VectorComparator(int inDim) : dim(inDim) {}
inline bool operator()(const VIPair &v1, const VIPair &v2) const { return v1.first[dim] < v2.first[dim]; }
int dim;
};
//Build the part of the tree between objects[from] and objects[to] (not including objects[to]).
//This routine partitions the objCenters in [from, to) along the dimension dim, recursively constructs
//the two halves, and adds their parent node. TODO: a cache-friendlier layout
void build(VIPairList &objCenters, int from, int to, const VolumeList &objBoxes, int dim)
{
eigen_assert(to - from > 1);
if(to - from == 2) {
boxes.push_back(objBoxes[objCenters[from].second].merged(objBoxes[objCenters[from + 1].second]));
children.push_back(from + (int)objects.size() - 1); //there are objects.size() - 1 tree nodes
children.push_back(from + (int)objects.size());
}
else if(to - from == 3) {
int mid = from + 2;
std::nth_element(objCenters.begin() + from, objCenters.begin() + mid,
objCenters.begin() + to, VectorComparator(dim)); //partition
build(objCenters, from, mid, objBoxes, (dim + 1) % Dim);
int idx1 = (int)boxes.size() - 1;
boxes.push_back(boxes[idx1].merged(objBoxes[objCenters[mid].second]));
children.push_back(idx1);
children.push_back(mid + (int)objects.size() - 1);
}
else {
int mid = from + (to - from) / 2;
nth_element(objCenters.begin() + from, objCenters.begin() + mid,
objCenters.begin() + to, VectorComparator(dim)); //partition
build(objCenters, from, mid, objBoxes, (dim + 1) % Dim);
int idx1 = (int)boxes.size() - 1;
build(objCenters, mid, to, objBoxes, (dim + 1) % Dim);
int idx2 = (int)boxes.size() - 1;
boxes.push_back(boxes[idx1].merged(boxes[idx2]));
children.push_back(idx1);
children.push_back(idx2);
}
}
std::vector<int> children; //children of x are children[2x] and children[2x+1], indices bigger than boxes.size() index into objects.
VolumeList boxes;
ObjectList objects;
};
} // end namespace Eigen
#endif //KDBVH_H_INCLUDED

View File

@@ -0,0 +1,15 @@
ADD_SUBDIRECTORY(AutoDiff)
ADD_SUBDIRECTORY(BVH)
ADD_SUBDIRECTORY(Eigenvalues)
ADD_SUBDIRECTORY(FFT)
ADD_SUBDIRECTORY(IterativeSolvers)
ADD_SUBDIRECTORY(KroneckerProduct)
ADD_SUBDIRECTORY(LevenbergMarquardt)
ADD_SUBDIRECTORY(MatrixFunctions)
ADD_SUBDIRECTORY(MoreVectorization)
ADD_SUBDIRECTORY(NonLinearOptimization)
ADD_SUBDIRECTORY(NumericalDiff)
ADD_SUBDIRECTORY(Polynomials)
ADD_SUBDIRECTORY(Skyline)
ADD_SUBDIRECTORY(SparseExtra)
ADD_SUBDIRECTORY(Splines)

View File

@@ -0,0 +1,805 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2012 David Harmon <dharmon@gmail.com>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.
#ifndef EIGEN_ARPACKGENERALIZEDSELFADJOINTEIGENSOLVER_H
#define EIGEN_ARPACKGENERALIZEDSELFADJOINTEIGENSOLVER_H
#include <Eigen/Dense>
namespace Eigen {
namespace internal {
template<typename Scalar, typename RealScalar> struct arpack_wrapper;
template<typename MatrixSolver, typename MatrixType, typename Scalar, bool BisSPD> struct OP;
}
template<typename MatrixType, typename MatrixSolver=SimplicialLLT<MatrixType>, bool BisSPD=false>
class ArpackGeneralizedSelfAdjointEigenSolver
{
public:
//typedef typename MatrixSolver::MatrixType MatrixType;
/** \brief Scalar type for matrices of type \p MatrixType. */
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::Index Index;
/** \brief Real scalar type for \p MatrixType.
*
* This is just \c Scalar if #Scalar is real (e.g., \c float or
* \c Scalar), and the type of the real part of \c Scalar if #Scalar is
* complex.
*/
typedef typename NumTraits<Scalar>::Real RealScalar;
/** \brief Type for vector of eigenvalues as returned by eigenvalues().
*
* This is a column vector with entries of type #RealScalar.
* The length of the vector is the size of \p nbrEigenvalues.
*/
typedef typename internal::plain_col_type<MatrixType, RealScalar>::type RealVectorType;
/** \brief Default constructor.
*
* The default constructor is for cases in which the user intends to
* perform decompositions via compute().
*
*/
ArpackGeneralizedSelfAdjointEigenSolver()
: m_eivec(),
m_eivalues(),
m_isInitialized(false),
m_eigenvectorsOk(false),
m_nbrConverged(0),
m_nbrIterations(0)
{ }
/** \brief Constructor; computes generalized eigenvalues of given matrix with respect to another matrix.
*
* \param[in] A Self-adjoint matrix whose eigenvalues / eigenvectors will
* computed. By default, the upper triangular part is used, but can be changed
* through the template parameter.
* \param[in] B Self-adjoint matrix for the generalized eigenvalue problem.
* \param[in] nbrEigenvalues The number of eigenvalues / eigenvectors to compute.
* Must be less than the size of the input matrix, or an error is returned.
* \param[in] eigs_sigma String containing either "LM", "SM", "LA", or "SA", with
* respective meanings to find the largest magnitude , smallest magnitude,
* largest algebraic, or smallest algebraic eigenvalues. Alternatively, this
* value can contain floating point value in string form, in which case the
* eigenvalues closest to this value will be found.
* \param[in] options Can be #ComputeEigenvectors (default) or #EigenvaluesOnly.
* \param[in] tol What tolerance to find the eigenvalues to. Default is 0, which
* means machine precision.
*
* This constructor calls compute(const MatrixType&, const MatrixType&, Index, string, int, RealScalar)
* to compute the eigenvalues of the matrix \p A with respect to \p B. The eigenvectors are computed if
* \p options equals #ComputeEigenvectors.
*
*/
ArpackGeneralizedSelfAdjointEigenSolver(const MatrixType& A, const MatrixType& B,
Index nbrEigenvalues, std::string eigs_sigma="LM",
int options=ComputeEigenvectors, RealScalar tol=0.0)
: m_eivec(),
m_eivalues(),
m_isInitialized(false),
m_eigenvectorsOk(false),
m_nbrConverged(0),
m_nbrIterations(0)
{
compute(A, B, nbrEigenvalues, eigs_sigma, options, tol);
}
/** \brief Constructor; computes eigenvalues of given matrix.
*
* \param[in] A Self-adjoint matrix whose eigenvalues / eigenvectors will
* computed. By default, the upper triangular part is used, but can be changed
* through the template parameter.
* \param[in] nbrEigenvalues The number of eigenvalues / eigenvectors to compute.
* Must be less than the size of the input matrix, or an error is returned.
* \param[in] eigs_sigma String containing either "LM", "SM", "LA", or "SA", with
* respective meanings to find the largest magnitude , smallest magnitude,
* largest algebraic, or smallest algebraic eigenvalues. Alternatively, this
* value can contain floating point value in string form, in which case the
* eigenvalues closest to this value will be found.
* \param[in] options Can be #ComputeEigenvectors (default) or #EigenvaluesOnly.
* \param[in] tol What tolerance to find the eigenvalues to. Default is 0, which
* means machine precision.
*
* This constructor calls compute(const MatrixType&, Index, string, int, RealScalar)
* to compute the eigenvalues of the matrix \p A. The eigenvectors are computed if
* \p options equals #ComputeEigenvectors.
*
*/
ArpackGeneralizedSelfAdjointEigenSolver(const MatrixType& A,
Index nbrEigenvalues, std::string eigs_sigma="LM",
int options=ComputeEigenvectors, RealScalar tol=0.0)
: m_eivec(),
m_eivalues(),
m_isInitialized(false),
m_eigenvectorsOk(false),
m_nbrConverged(0),
m_nbrIterations(0)
{
compute(A, nbrEigenvalues, eigs_sigma, options, tol);
}
/** \brief Computes generalized eigenvalues / eigenvectors of given matrix using the external ARPACK library.
*
* \param[in] A Selfadjoint matrix whose eigendecomposition is to be computed.
* \param[in] B Selfadjoint matrix for generalized eigenvalues.
* \param[in] nbrEigenvalues The number of eigenvalues / eigenvectors to compute.
* Must be less than the size of the input matrix, or an error is returned.
* \param[in] eigs_sigma String containing either "LM", "SM", "LA", or "SA", with
* respective meanings to find the largest magnitude , smallest magnitude,
* largest algebraic, or smallest algebraic eigenvalues. Alternatively, this
* value can contain floating point value in string form, in which case the
* eigenvalues closest to this value will be found.
* \param[in] options Can be #ComputeEigenvectors (default) or #EigenvaluesOnly.
* \param[in] tol What tolerance to find the eigenvalues to. Default is 0, which
* means machine precision.
*
* \returns Reference to \c *this
*
* This function computes the generalized eigenvalues of \p A with respect to \p B using ARPACK. The eigenvalues()
* function can be used to retrieve them. If \p options equals #ComputeEigenvectors,
* then the eigenvectors are also computed and can be retrieved by
* calling eigenvectors().
*
*/
ArpackGeneralizedSelfAdjointEigenSolver& compute(const MatrixType& A, const MatrixType& B,
Index nbrEigenvalues, std::string eigs_sigma="LM",
int options=ComputeEigenvectors, RealScalar tol=0.0);
/** \brief Computes eigenvalues / eigenvectors of given matrix using the external ARPACK library.
*
* \param[in] A Selfadjoint matrix whose eigendecomposition is to be computed.
* \param[in] nbrEigenvalues The number of eigenvalues / eigenvectors to compute.
* Must be less than the size of the input matrix, or an error is returned.
* \param[in] eigs_sigma String containing either "LM", "SM", "LA", or "SA", with
* respective meanings to find the largest magnitude , smallest magnitude,
* largest algebraic, or smallest algebraic eigenvalues. Alternatively, this
* value can contain floating point value in string form, in which case the
* eigenvalues closest to this value will be found.
* \param[in] options Can be #ComputeEigenvectors (default) or #EigenvaluesOnly.
* \param[in] tol What tolerance to find the eigenvalues to. Default is 0, which
* means machine precision.
*
* \returns Reference to \c *this
*
* This function computes the eigenvalues of \p A using ARPACK. The eigenvalues()
* function can be used to retrieve them. If \p options equals #ComputeEigenvectors,
* then the eigenvectors are also computed and can be retrieved by
* calling eigenvectors().
*
*/
ArpackGeneralizedSelfAdjointEigenSolver& compute(const MatrixType& A,
Index nbrEigenvalues, std::string eigs_sigma="LM",
int options=ComputeEigenvectors, RealScalar tol=0.0);
/** \brief Returns the eigenvectors of given matrix.
*
* \returns A const reference to the matrix whose columns are the eigenvectors.
*
* \pre The eigenvectors have been computed before.
*
* Column \f$ k \f$ of the returned matrix is an eigenvector corresponding
* to eigenvalue number \f$ k \f$ as returned by eigenvalues(). The
* eigenvectors are normalized to have (Euclidean) norm equal to one. If
* this object was used to solve the eigenproblem for the selfadjoint
* matrix \f$ A \f$, then the matrix returned by this function is the
* matrix \f$ V \f$ in the eigendecomposition \f$ A V = D V \f$.
* For the generalized eigenproblem, the matrix returned is the solution \f$ A V = D B V \f$
*
* Example: \include SelfAdjointEigenSolver_eigenvectors.cpp
* Output: \verbinclude SelfAdjointEigenSolver_eigenvectors.out
*
* \sa eigenvalues()
*/
const Matrix<Scalar, Dynamic, Dynamic>& eigenvectors() const
{
eigen_assert(m_isInitialized && "ArpackGeneralizedSelfAdjointEigenSolver is not initialized.");
eigen_assert(m_eigenvectorsOk && "The eigenvectors have not been computed together with the eigenvalues.");
return m_eivec;
}
/** \brief Returns the eigenvalues of given matrix.
*
* \returns A const reference to the column vector containing the eigenvalues.
*
* \pre The eigenvalues have been computed before.
*
* The eigenvalues are repeated according to their algebraic multiplicity,
* so there are as many eigenvalues as rows in the matrix. The eigenvalues
* are sorted in increasing order.
*
* Example: \include SelfAdjointEigenSolver_eigenvalues.cpp
* Output: \verbinclude SelfAdjointEigenSolver_eigenvalues.out
*
* \sa eigenvectors(), MatrixBase::eigenvalues()
*/
const Matrix<Scalar, Dynamic, 1>& eigenvalues() const
{
eigen_assert(m_isInitialized && "ArpackGeneralizedSelfAdjointEigenSolver is not initialized.");
return m_eivalues;
}
/** \brief Computes the positive-definite square root of the matrix.
*
* \returns the positive-definite square root of the matrix
*
* \pre The eigenvalues and eigenvectors of a positive-definite matrix
* have been computed before.
*
* The square root of a positive-definite matrix \f$ A \f$ is the
* positive-definite matrix whose square equals \f$ A \f$. This function
* uses the eigendecomposition \f$ A = V D V^{-1} \f$ to compute the
* square root as \f$ A^{1/2} = V D^{1/2} V^{-1} \f$.
*
* Example: \include SelfAdjointEigenSolver_operatorSqrt.cpp
* Output: \verbinclude SelfAdjointEigenSolver_operatorSqrt.out
*
* \sa operatorInverseSqrt(),
* \ref MatrixFunctions_Module "MatrixFunctions Module"
*/
Matrix<Scalar, Dynamic, Dynamic> operatorSqrt() const
{
eigen_assert(m_isInitialized && "SelfAdjointEigenSolver is not initialized.");
eigen_assert(m_eigenvectorsOk && "The eigenvectors have not been computed together with the eigenvalues.");
return m_eivec * m_eivalues.cwiseSqrt().asDiagonal() * m_eivec.adjoint();
}
/** \brief Computes the inverse square root of the matrix.
*
* \returns the inverse positive-definite square root of the matrix
*
* \pre The eigenvalues and eigenvectors of a positive-definite matrix
* have been computed before.
*
* This function uses the eigendecomposition \f$ A = V D V^{-1} \f$ to
* compute the inverse square root as \f$ V D^{-1/2} V^{-1} \f$. This is
* cheaper than first computing the square root with operatorSqrt() and
* then its inverse with MatrixBase::inverse().
*
* Example: \include SelfAdjointEigenSolver_operatorInverseSqrt.cpp
* Output: \verbinclude SelfAdjointEigenSolver_operatorInverseSqrt.out
*
* \sa operatorSqrt(), MatrixBase::inverse(),
* \ref MatrixFunctions_Module "MatrixFunctions Module"
*/
Matrix<Scalar, Dynamic, Dynamic> operatorInverseSqrt() const
{
eigen_assert(m_isInitialized && "SelfAdjointEigenSolver is not initialized.");
eigen_assert(m_eigenvectorsOk && "The eigenvectors have not been computed together with the eigenvalues.");
return m_eivec * m_eivalues.cwiseInverse().cwiseSqrt().asDiagonal() * m_eivec.adjoint();
}
/** \brief Reports whether previous computation was successful.
*
* \returns \c Success if computation was succesful, \c NoConvergence otherwise.
*/
ComputationInfo info() const
{
eigen_assert(m_isInitialized && "ArpackGeneralizedSelfAdjointEigenSolver is not initialized.");
return m_info;
}
size_t getNbrConvergedEigenValues() const
{ return m_nbrConverged; }
size_t getNbrIterations() const
{ return m_nbrIterations; }
protected:
Matrix<Scalar, Dynamic, Dynamic> m_eivec;
Matrix<Scalar, Dynamic, 1> m_eivalues;
ComputationInfo m_info;
bool m_isInitialized;
bool m_eigenvectorsOk;
size_t m_nbrConverged;
size_t m_nbrIterations;
};
template<typename MatrixType, typename MatrixSolver, bool BisSPD>
ArpackGeneralizedSelfAdjointEigenSolver<MatrixType, MatrixSolver, BisSPD>&
ArpackGeneralizedSelfAdjointEigenSolver<MatrixType, MatrixSolver, BisSPD>
::compute(const MatrixType& A, Index nbrEigenvalues,
std::string eigs_sigma, int options, RealScalar tol)
{
MatrixType B(0,0);
compute(A, B, nbrEigenvalues, eigs_sigma, options, tol);
return *this;
}
template<typename MatrixType, typename MatrixSolver, bool BisSPD>
ArpackGeneralizedSelfAdjointEigenSolver<MatrixType, MatrixSolver, BisSPD>&
ArpackGeneralizedSelfAdjointEigenSolver<MatrixType, MatrixSolver, BisSPD>
::compute(const MatrixType& A, const MatrixType& B, Index nbrEigenvalues,
std::string eigs_sigma, int options, RealScalar tol)
{
eigen_assert(A.cols() == A.rows());
eigen_assert(B.cols() == B.rows());
eigen_assert(B.rows() == 0 || A.cols() == B.rows());
eigen_assert((options &~ (EigVecMask | GenEigMask)) == 0
&& (options & EigVecMask) != EigVecMask
&& "invalid option parameter");
bool isBempty = (B.rows() == 0) || (B.cols() == 0);
// For clarity, all parameters match their ARPACK name
//
// Always 0 on the first call
//
int ido = 0;
int n = (int)A.cols();
// User options: "LA", "SA", "SM", "LM", "BE"
//
char whch[3] = "LM";
// Specifies the shift if iparam[6] = { 3, 4, 5 }, not used if iparam[6] = { 1, 2 }
//
RealScalar sigma = 0.0;
if (eigs_sigma.length() >= 2 && isalpha(eigs_sigma[0]) && isalpha(eigs_sigma[1]))
{
eigs_sigma[0] = toupper(eigs_sigma[0]);
eigs_sigma[1] = toupper(eigs_sigma[1]);
// In the following special case we're going to invert the problem, since solving
// for larger magnitude is much much faster
// i.e., if 'SM' is specified, we're going to really use 'LM', the default
//
if (eigs_sigma.substr(0,2) != "SM")
{
whch[0] = eigs_sigma[0];
whch[1] = eigs_sigma[1];
}
}
else
{
eigen_assert(false && "Specifying clustered eigenvalues is not yet supported!");
// If it's not scalar values, then the user may be explicitly
// specifying the sigma value to cluster the evs around
//
sigma = atof(eigs_sigma.c_str());
// If atof fails, it returns 0.0, which is a fine default
//
}
// "I" means normal eigenvalue problem, "G" means generalized
//
char bmat[2] = "I";
if (eigs_sigma.substr(0,2) == "SM" || !(isalpha(eigs_sigma[0]) && isalpha(eigs_sigma[1])) || (!isBempty && !BisSPD))
bmat[0] = 'G';
// Now we determine the mode to use
//
int mode = (bmat[0] == 'G') + 1;
if (eigs_sigma.substr(0,2) == "SM" || !(isalpha(eigs_sigma[0]) && isalpha(eigs_sigma[1])))
{
// We're going to use shift-and-invert mode, and basically find
// the largest eigenvalues of the inverse operator
//
mode = 3;
}
// The user-specified number of eigenvalues/vectors to compute
//
int nev = (int)nbrEigenvalues;
// Allocate space for ARPACK to store the residual
//
Scalar *resid = new Scalar[n];
// Number of Lanczos vectors, must satisfy nev < ncv <= n
// Note that this indicates that nev != n, and we cannot compute
// all eigenvalues of a mtrix
//
int ncv = std::min(std::max(2*nev, 20), n);
// The working n x ncv matrix, also store the final eigenvectors (if computed)
//
Scalar *v = new Scalar[n*ncv];
int ldv = n;
// Working space
//
Scalar *workd = new Scalar[3*n];
int lworkl = ncv*ncv+8*ncv; // Must be at least this length
Scalar *workl = new Scalar[lworkl];
int *iparam= new int[11];
iparam[0] = 1; // 1 means we let ARPACK perform the shifts, 0 means we'd have to do it
iparam[2] = std::max(300, (int)std::ceil(2*n/std::max(ncv,1)));
iparam[6] = mode; // The mode, 1 is standard ev problem, 2 for generalized ev, 3 for shift-and-invert
// Used during reverse communicate to notify where arrays start
//
int *ipntr = new int[11];
// Error codes are returned in here, initial value of 0 indicates a random initial
// residual vector is used, any other values means resid contains the initial residual
// vector, possibly from a previous run
//
int info = 0;
Scalar scale = 1.0;
//if (!isBempty)
//{
//Scalar scale = B.norm() / std::sqrt(n);
//scale = std::pow(2, std::floor(std::log(scale+1)));
////M /= scale;
//for (size_t i=0; i<(size_t)B.outerSize(); i++)
// for (typename MatrixType::InnerIterator it(B, i); it; ++it)
// it.valueRef() /= scale;
//}
MatrixSolver OP;
if (mode == 1 || mode == 2)
{
if (!isBempty)
OP.compute(B);
}
else if (mode == 3)
{
if (sigma == 0.0)
{
OP.compute(A);
}
else
{
// Note: We will never enter here because sigma must be 0.0
//
if (isBempty)
{
MatrixType AminusSigmaB(A);
for (Index i=0; i<A.rows(); ++i)
AminusSigmaB.coeffRef(i,i) -= sigma;
OP.compute(AminusSigmaB);
}
else
{
MatrixType AminusSigmaB = A - sigma * B;
OP.compute(AminusSigmaB);
}
}
}
if (!(mode == 1 && isBempty) && !(mode == 2 && isBempty) && OP.info() != Success)
std::cout << "Error factoring matrix" << std::endl;
do
{
internal::arpack_wrapper<Scalar, RealScalar>::saupd(&ido, bmat, &n, whch, &nev, &tol, resid,
&ncv, v, &ldv, iparam, ipntr, workd, workl,
&lworkl, &info);
if (ido == -1 || ido == 1)
{
Scalar *in = workd + ipntr[0] - 1;
Scalar *out = workd + ipntr[1] - 1;
if (ido == 1 && mode != 2)
{
Scalar *out2 = workd + ipntr[2] - 1;
if (isBempty || mode == 1)
Matrix<Scalar, Dynamic, 1>::Map(out2, n) = Matrix<Scalar, Dynamic, 1>::Map(in, n);
else
Matrix<Scalar, Dynamic, 1>::Map(out2, n) = B * Matrix<Scalar, Dynamic, 1>::Map(in, n);
in = workd + ipntr[2] - 1;
}
if (mode == 1)
{
if (isBempty)
{
// OP = A
//
Matrix<Scalar, Dynamic, 1>::Map(out, n) = A * Matrix<Scalar, Dynamic, 1>::Map(in, n);
}
else
{
// OP = L^{-1}AL^{-T}
//
internal::OP<MatrixSolver, MatrixType, Scalar, BisSPD>::applyOP(OP, A, n, in, out);
}
}
else if (mode == 2)
{
if (ido == 1)
Matrix<Scalar, Dynamic, 1>::Map(in, n) = A * Matrix<Scalar, Dynamic, 1>::Map(in, n);
// OP = B^{-1} A
//
Matrix<Scalar, Dynamic, 1>::Map(out, n) = OP.solve(Matrix<Scalar, Dynamic, 1>::Map(in, n));
}
else if (mode == 3)
{
// OP = (A-\sigmaB)B (\sigma could be 0, and B could be I)
// The B * in is already computed and stored at in if ido == 1
//
if (ido == 1 || isBempty)
Matrix<Scalar, Dynamic, 1>::Map(out, n) = OP.solve(Matrix<Scalar, Dynamic, 1>::Map(in, n));
else
Matrix<Scalar, Dynamic, 1>::Map(out, n) = OP.solve(B * Matrix<Scalar, Dynamic, 1>::Map(in, n));
}
}
else if (ido == 2)
{
Scalar *in = workd + ipntr[0] - 1;
Scalar *out = workd + ipntr[1] - 1;
if (isBempty || mode == 1)
Matrix<Scalar, Dynamic, 1>::Map(out, n) = Matrix<Scalar, Dynamic, 1>::Map(in, n);
else
Matrix<Scalar, Dynamic, 1>::Map(out, n) = B * Matrix<Scalar, Dynamic, 1>::Map(in, n);
}
} while (ido != 99);
if (info == 1)
m_info = NoConvergence;
else if (info == 3)
m_info = NumericalIssue;
else if (info < 0)
m_info = InvalidInput;
else if (info != 0)
eigen_assert(false && "Unknown ARPACK return value!");
else
{
// Do we compute eigenvectors or not?
//
int rvec = (options & ComputeEigenvectors) == ComputeEigenvectors;
// "A" means "All", use "S" to choose specific eigenvalues (not yet supported in ARPACK))
//
char howmny[2] = "A";
// if howmny == "S", specifies the eigenvalues to compute (not implemented in ARPACK)
//
int *select = new int[ncv];
// Final eigenvalues
//
m_eivalues.resize(nev, 1);
internal::arpack_wrapper<Scalar, RealScalar>::seupd(&rvec, howmny, select, m_eivalues.data(), v, &ldv,
&sigma, bmat, &n, whch, &nev, &tol, resid, &ncv,
v, &ldv, iparam, ipntr, workd, workl, &lworkl, &info);
if (info == -14)
m_info = NoConvergence;
else if (info != 0)
m_info = InvalidInput;
else
{
if (rvec)
{
m_eivec.resize(A.rows(), nev);
for (int i=0; i<nev; i++)
for (int j=0; j<n; j++)
m_eivec(j,i) = v[i*n+j] / scale;
if (mode == 1 && !isBempty && BisSPD)
internal::OP<MatrixSolver, MatrixType, Scalar, BisSPD>::project(OP, n, nev, m_eivec.data());
m_eigenvectorsOk = true;
}
m_nbrIterations = iparam[2];
m_nbrConverged = iparam[4];
m_info = Success;
}
delete select;
}
delete v;
delete iparam;
delete ipntr;
delete workd;
delete workl;
delete resid;
m_isInitialized = true;
return *this;
}
// Single precision
//
extern "C" void ssaupd_(int *ido, char *bmat, int *n, char *which,
int *nev, float *tol, float *resid, int *ncv,
float *v, int *ldv, int *iparam, int *ipntr,
float *workd, float *workl, int *lworkl,
int *info);
extern "C" void sseupd_(int *rvec, char *All, int *select, float *d,
float *z, int *ldz, float *sigma,
char *bmat, int *n, char *which, int *nev,
float *tol, float *resid, int *ncv, float *v,
int *ldv, int *iparam, int *ipntr, float *workd,
float *workl, int *lworkl, int *ierr);
// Double precision
//
extern "C" void dsaupd_(int *ido, char *bmat, int *n, char *which,
int *nev, double *tol, double *resid, int *ncv,
double *v, int *ldv, int *iparam, int *ipntr,
double *workd, double *workl, int *lworkl,
int *info);
extern "C" void dseupd_(int *rvec, char *All, int *select, double *d,
double *z, int *ldz, double *sigma,
char *bmat, int *n, char *which, int *nev,
double *tol, double *resid, int *ncv, double *v,
int *ldv, int *iparam, int *ipntr, double *workd,
double *workl, int *lworkl, int *ierr);
namespace internal {
template<typename Scalar, typename RealScalar> struct arpack_wrapper
{
static inline void saupd(int *ido, char *bmat, int *n, char *which,
int *nev, RealScalar *tol, Scalar *resid, int *ncv,
Scalar *v, int *ldv, int *iparam, int *ipntr,
Scalar *workd, Scalar *workl, int *lworkl, int *info)
{
EIGEN_STATIC_ASSERT(!NumTraits<Scalar>::IsComplex, NUMERIC_TYPE_MUST_BE_REAL)
}
static inline void seupd(int *rvec, char *All, int *select, Scalar *d,
Scalar *z, int *ldz, RealScalar *sigma,
char *bmat, int *n, char *which, int *nev,
RealScalar *tol, Scalar *resid, int *ncv, Scalar *v,
int *ldv, int *iparam, int *ipntr, Scalar *workd,
Scalar *workl, int *lworkl, int *ierr)
{
EIGEN_STATIC_ASSERT(!NumTraits<Scalar>::IsComplex, NUMERIC_TYPE_MUST_BE_REAL)
}
};
template <> struct arpack_wrapper<float, float>
{
static inline void saupd(int *ido, char *bmat, int *n, char *which,
int *nev, float *tol, float *resid, int *ncv,
float *v, int *ldv, int *iparam, int *ipntr,
float *workd, float *workl, int *lworkl, int *info)
{
ssaupd_(ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, ipntr, workd, workl, lworkl, info);
}
static inline void seupd(int *rvec, char *All, int *select, float *d,
float *z, int *ldz, float *sigma,
char *bmat, int *n, char *which, int *nev,
float *tol, float *resid, int *ncv, float *v,
int *ldv, int *iparam, int *ipntr, float *workd,
float *workl, int *lworkl, int *ierr)
{
sseupd_(rvec, All, select, d, z, ldz, sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, ipntr,
workd, workl, lworkl, ierr);
}
};
template <> struct arpack_wrapper<double, double>
{
static inline void saupd(int *ido, char *bmat, int *n, char *which,
int *nev, double *tol, double *resid, int *ncv,
double *v, int *ldv, int *iparam, int *ipntr,
double *workd, double *workl, int *lworkl, int *info)
{
dsaupd_(ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, ipntr, workd, workl, lworkl, info);
}
static inline void seupd(int *rvec, char *All, int *select, double *d,
double *z, int *ldz, double *sigma,
char *bmat, int *n, char *which, int *nev,
double *tol, double *resid, int *ncv, double *v,
int *ldv, int *iparam, int *ipntr, double *workd,
double *workl, int *lworkl, int *ierr)
{
dseupd_(rvec, All, select, d, v, ldv, sigma, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, ipntr,
workd, workl, lworkl, ierr);
}
};
template<typename MatrixSolver, typename MatrixType, typename Scalar, bool BisSPD>
struct OP
{
static inline void applyOP(MatrixSolver &OP, const MatrixType &A, int n, Scalar *in, Scalar *out);
static inline void project(MatrixSolver &OP, int n, int k, Scalar *vecs);
};
template<typename MatrixSolver, typename MatrixType, typename Scalar>
struct OP<MatrixSolver, MatrixType, Scalar, true>
{
static inline void applyOP(MatrixSolver &OP, const MatrixType &A, int n, Scalar *in, Scalar *out)
{
// OP = L^{-1} A L^{-T} (B = LL^T)
//
// First solve L^T out = in
//
Matrix<Scalar, Dynamic, 1>::Map(out, n) = OP.matrixU().solve(Matrix<Scalar, Dynamic, 1>::Map(in, n));
Matrix<Scalar, Dynamic, 1>::Map(out, n) = OP.permutationPinv() * Matrix<Scalar, Dynamic, 1>::Map(out, n);
// Then compute out = A out
//
Matrix<Scalar, Dynamic, 1>::Map(out, n) = A * Matrix<Scalar, Dynamic, 1>::Map(out, n);
// Then solve L out = out
//
Matrix<Scalar, Dynamic, 1>::Map(out, n) = OP.permutationP() * Matrix<Scalar, Dynamic, 1>::Map(out, n);
Matrix<Scalar, Dynamic, 1>::Map(out, n) = OP.matrixL().solve(Matrix<Scalar, Dynamic, 1>::Map(out, n));
}
static inline void project(MatrixSolver &OP, int n, int k, Scalar *vecs)
{
// Solve L^T out = in
//
Matrix<Scalar, Dynamic, Dynamic>::Map(vecs, n, k) = OP.matrixU().solve(Matrix<Scalar, Dynamic, Dynamic>::Map(vecs, n, k));
Matrix<Scalar, Dynamic, Dynamic>::Map(vecs, n, k) = OP.permutationPinv() * Matrix<Scalar, Dynamic, Dynamic>::Map(vecs, n, k);
}
};
template<typename MatrixSolver, typename MatrixType, typename Scalar>
struct OP<MatrixSolver, MatrixType, Scalar, false>
{
static inline void applyOP(MatrixSolver &OP, const MatrixType &A, int n, Scalar *in, Scalar *out)
{
eigen_assert(false && "Should never be in here...");
}
static inline void project(MatrixSolver &OP, int n, int k, Scalar *vecs)
{
eigen_assert(false && "Should never be in here...");
}
};
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_ARPACKSELFADJOINTEIGENSOLVER_H

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_Eigenvalues_SRCS "*.h")
INSTALL(FILES
${Eigen_Eigenvalues_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/Eigenvalues COMPONENT Devel
)

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_FFT_SRCS "*.h")
INSTALL(FILES
${Eigen_FFT_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/FFT COMPONENT Devel
)

View File

@@ -0,0 +1,261 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Mark Borgerding mark a borgerding net
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
namespace Eigen {
namespace internal {
// FFTW uses non-const arguments
// so we must use ugly const_cast calls for all the args it uses
//
// This should be safe as long as
// 1. we use FFTW_ESTIMATE for all our planning
// see the FFTW docs section 4.3.2 "Planner Flags"
// 2. fftw_complex is compatible with std::complex
// This assumes std::complex<T> layout is array of size 2 with real,imag
template <typename T>
inline
T * fftw_cast(const T* p)
{
return const_cast<T*>( p);
}
inline
fftw_complex * fftw_cast( const std::complex<double> * p)
{
return const_cast<fftw_complex*>( reinterpret_cast<const fftw_complex*>(p) );
}
inline
fftwf_complex * fftw_cast( const std::complex<float> * p)
{
return const_cast<fftwf_complex*>( reinterpret_cast<const fftwf_complex*>(p) );
}
inline
fftwl_complex * fftw_cast( const std::complex<long double> * p)
{
return const_cast<fftwl_complex*>( reinterpret_cast<const fftwl_complex*>(p) );
}
template <typename T>
struct fftw_plan {};
template <>
struct fftw_plan<float>
{
typedef float scalar_type;
typedef fftwf_complex complex_type;
fftwf_plan m_plan;
fftw_plan() :m_plan(NULL) {}
~fftw_plan() {if (m_plan) fftwf_destroy_plan(m_plan);}
inline
void fwd(complex_type * dst,complex_type * src,int nfft) {
if (m_plan==NULL) m_plan = fftwf_plan_dft_1d(nfft,src,dst, FFTW_FORWARD, FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftwf_execute_dft( m_plan, src,dst);
}
inline
void inv(complex_type * dst,complex_type * src,int nfft) {
if (m_plan==NULL) m_plan = fftwf_plan_dft_1d(nfft,src,dst, FFTW_BACKWARD , FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftwf_execute_dft( m_plan, src,dst);
}
inline
void fwd(complex_type * dst,scalar_type * src,int nfft) {
if (m_plan==NULL) m_plan = fftwf_plan_dft_r2c_1d(nfft,src,dst,FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftwf_execute_dft_r2c( m_plan,src,dst);
}
inline
void inv(scalar_type * dst,complex_type * src,int nfft) {
if (m_plan==NULL)
m_plan = fftwf_plan_dft_c2r_1d(nfft,src,dst,FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftwf_execute_dft_c2r( m_plan, src,dst);
}
inline
void fwd2( complex_type * dst,complex_type * src,int n0,int n1) {
if (m_plan==NULL) m_plan = fftwf_plan_dft_2d(n0,n1,src,dst,FFTW_FORWARD,FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftwf_execute_dft( m_plan, src,dst);
}
inline
void inv2( complex_type * dst,complex_type * src,int n0,int n1) {
if (m_plan==NULL) m_plan = fftwf_plan_dft_2d(n0,n1,src,dst,FFTW_BACKWARD,FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftwf_execute_dft( m_plan, src,dst);
}
};
template <>
struct fftw_plan<double>
{
typedef double scalar_type;
typedef fftw_complex complex_type;
::fftw_plan m_plan;
fftw_plan() :m_plan(NULL) {}
~fftw_plan() {if (m_plan) fftw_destroy_plan(m_plan);}
inline
void fwd(complex_type * dst,complex_type * src,int nfft) {
if (m_plan==NULL) m_plan = fftw_plan_dft_1d(nfft,src,dst, FFTW_FORWARD, FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftw_execute_dft( m_plan, src,dst);
}
inline
void inv(complex_type * dst,complex_type * src,int nfft) {
if (m_plan==NULL) m_plan = fftw_plan_dft_1d(nfft,src,dst, FFTW_BACKWARD , FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftw_execute_dft( m_plan, src,dst);
}
inline
void fwd(complex_type * dst,scalar_type * src,int nfft) {
if (m_plan==NULL) m_plan = fftw_plan_dft_r2c_1d(nfft,src,dst,FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftw_execute_dft_r2c( m_plan,src,dst);
}
inline
void inv(scalar_type * dst,complex_type * src,int nfft) {
if (m_plan==NULL)
m_plan = fftw_plan_dft_c2r_1d(nfft,src,dst,FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftw_execute_dft_c2r( m_plan, src,dst);
}
inline
void fwd2( complex_type * dst,complex_type * src,int n0,int n1) {
if (m_plan==NULL) m_plan = fftw_plan_dft_2d(n0,n1,src,dst,FFTW_FORWARD,FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftw_execute_dft( m_plan, src,dst);
}
inline
void inv2( complex_type * dst,complex_type * src,int n0,int n1) {
if (m_plan==NULL) m_plan = fftw_plan_dft_2d(n0,n1,src,dst,FFTW_BACKWARD,FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftw_execute_dft( m_plan, src,dst);
}
};
template <>
struct fftw_plan<long double>
{
typedef long double scalar_type;
typedef fftwl_complex complex_type;
fftwl_plan m_plan;
fftw_plan() :m_plan(NULL) {}
~fftw_plan() {if (m_plan) fftwl_destroy_plan(m_plan);}
inline
void fwd(complex_type * dst,complex_type * src,int nfft) {
if (m_plan==NULL) m_plan = fftwl_plan_dft_1d(nfft,src,dst, FFTW_FORWARD, FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftwl_execute_dft( m_plan, src,dst);
}
inline
void inv(complex_type * dst,complex_type * src,int nfft) {
if (m_plan==NULL) m_plan = fftwl_plan_dft_1d(nfft,src,dst, FFTW_BACKWARD , FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftwl_execute_dft( m_plan, src,dst);
}
inline
void fwd(complex_type * dst,scalar_type * src,int nfft) {
if (m_plan==NULL) m_plan = fftwl_plan_dft_r2c_1d(nfft,src,dst,FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftwl_execute_dft_r2c( m_plan,src,dst);
}
inline
void inv(scalar_type * dst,complex_type * src,int nfft) {
if (m_plan==NULL)
m_plan = fftwl_plan_dft_c2r_1d(nfft,src,dst,FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftwl_execute_dft_c2r( m_plan, src,dst);
}
inline
void fwd2( complex_type * dst,complex_type * src,int n0,int n1) {
if (m_plan==NULL) m_plan = fftwl_plan_dft_2d(n0,n1,src,dst,FFTW_FORWARD,FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftwl_execute_dft( m_plan, src,dst);
}
inline
void inv2( complex_type * dst,complex_type * src,int n0,int n1) {
if (m_plan==NULL) m_plan = fftwl_plan_dft_2d(n0,n1,src,dst,FFTW_BACKWARD,FFTW_ESTIMATE|FFTW_PRESERVE_INPUT);
fftwl_execute_dft( m_plan, src,dst);
}
};
template <typename _Scalar>
struct fftw_impl
{
typedef _Scalar Scalar;
typedef std::complex<Scalar> Complex;
inline
void clear()
{
m_plans.clear();
}
// complex-to-complex forward FFT
inline
void fwd( Complex * dst,const Complex *src,int nfft)
{
get_plan(nfft,false,dst,src).fwd(fftw_cast(dst), fftw_cast(src),nfft );
}
// real-to-complex forward FFT
inline
void fwd( Complex * dst,const Scalar * src,int nfft)
{
get_plan(nfft,false,dst,src).fwd(fftw_cast(dst), fftw_cast(src) ,nfft);
}
// 2-d complex-to-complex
inline
void fwd2(Complex * dst, const Complex * src, int n0,int n1)
{
get_plan(n0,n1,false,dst,src).fwd2(fftw_cast(dst), fftw_cast(src) ,n0,n1);
}
// inverse complex-to-complex
inline
void inv(Complex * dst,const Complex *src,int nfft)
{
get_plan(nfft,true,dst,src).inv(fftw_cast(dst), fftw_cast(src),nfft );
}
// half-complex to scalar
inline
void inv( Scalar * dst,const Complex * src,int nfft)
{
get_plan(nfft,true,dst,src).inv(fftw_cast(dst), fftw_cast(src),nfft );
}
// 2-d complex-to-complex
inline
void inv2(Complex * dst, const Complex * src, int n0,int n1)
{
get_plan(n0,n1,true,dst,src).inv2(fftw_cast(dst), fftw_cast(src) ,n0,n1);
}
protected:
typedef fftw_plan<Scalar> PlanData;
typedef std::map<int64_t,PlanData> PlanMap;
PlanMap m_plans;
inline
PlanData & get_plan(int nfft,bool inverse,void * dst,const void * src)
{
bool inplace = (dst==src);
bool aligned = ( (reinterpret_cast<size_t>(src)&15) | (reinterpret_cast<size_t>(dst)&15) ) == 0;
int64_t key = ( (nfft<<3 ) | (inverse<<2) | (inplace<<1) | aligned ) << 1;
return m_plans[key];
}
inline
PlanData & get_plan(int n0,int n1,bool inverse,void * dst,const void * src)
{
bool inplace = (dst==src);
bool aligned = ( (reinterpret_cast<size_t>(src)&15) | (reinterpret_cast<size_t>(dst)&15) ) == 0;
int64_t key = ( ( (((int64_t)n0) << 30)|(n1<<3 ) | (inverse<<2) | (inplace<<1) | aligned ) << 1 ) + 1;
return m_plans[key];
}
};
} // end namespace internal
} // end namespace Eigen
/* vim: set filetype=cpp et sw=2 ts=2 ai: */

View File

@@ -0,0 +1,420 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Mark Borgerding mark a borgerding net
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
namespace Eigen {
namespace internal {
// This FFT implementation was derived from kissfft http:sourceforge.net/projects/kissfft
// Copyright 2003-2009 Mark Borgerding
template <typename _Scalar>
struct kiss_cpx_fft
{
typedef _Scalar Scalar;
typedef std::complex<Scalar> Complex;
std::vector<Complex> m_twiddles;
std::vector<int> m_stageRadix;
std::vector<int> m_stageRemainder;
std::vector<Complex> m_scratchBuf;
bool m_inverse;
inline
void make_twiddles(int nfft,bool inverse)
{
using std::acos;
m_inverse = inverse;
m_twiddles.resize(nfft);
Scalar phinc = (inverse?2:-2)* acos( (Scalar) -1) / nfft;
for (int i=0;i<nfft;++i)
m_twiddles[i] = exp( Complex(0,i*phinc) );
}
void factorize(int nfft)
{
//start factoring out 4's, then 2's, then 3,5,7,9,...
int n= nfft;
int p=4;
do {
while (n % p) {
switch (p) {
case 4: p = 2; break;
case 2: p = 3; break;
default: p += 2; break;
}
if (p*p>n)
p=n;// impossible to have a factor > sqrt(n)
}
n /= p;
m_stageRadix.push_back(p);
m_stageRemainder.push_back(n);
if ( p > 5 )
m_scratchBuf.resize(p); // scratchbuf will be needed in bfly_generic
}while(n>1);
}
template <typename _Src>
inline
void work( int stage,Complex * xout, const _Src * xin, size_t fstride,size_t in_stride)
{
int p = m_stageRadix[stage];
int m = m_stageRemainder[stage];
Complex * Fout_beg = xout;
Complex * Fout_end = xout + p*m;
if (m>1) {
do{
// recursive call:
// DFT of size m*p performed by doing
// p instances of smaller DFTs of size m,
// each one takes a decimated version of the input
work(stage+1, xout , xin, fstride*p,in_stride);
xin += fstride*in_stride;
}while( (xout += m) != Fout_end );
}else{
do{
*xout = *xin;
xin += fstride*in_stride;
}while(++xout != Fout_end );
}
xout=Fout_beg;
// recombine the p smaller DFTs
switch (p) {
case 2: bfly2(xout,fstride,m); break;
case 3: bfly3(xout,fstride,m); break;
case 4: bfly4(xout,fstride,m); break;
case 5: bfly5(xout,fstride,m); break;
default: bfly_generic(xout,fstride,m,p); break;
}
}
inline
void bfly2( Complex * Fout, const size_t fstride, int m)
{
for (int k=0;k<m;++k) {
Complex t = Fout[m+k] * m_twiddles[k*fstride];
Fout[m+k] = Fout[k] - t;
Fout[k] += t;
}
}
inline
void bfly4( Complex * Fout, const size_t fstride, const size_t m)
{
Complex scratch[6];
int negative_if_inverse = m_inverse * -2 +1;
for (size_t k=0;k<m;++k) {
scratch[0] = Fout[k+m] * m_twiddles[k*fstride];
scratch[1] = Fout[k+2*m] * m_twiddles[k*fstride*2];
scratch[2] = Fout[k+3*m] * m_twiddles[k*fstride*3];
scratch[5] = Fout[k] - scratch[1];
Fout[k] += scratch[1];
scratch[3] = scratch[0] + scratch[2];
scratch[4] = scratch[0] - scratch[2];
scratch[4] = Complex( scratch[4].imag()*negative_if_inverse , -scratch[4].real()* negative_if_inverse );
Fout[k+2*m] = Fout[k] - scratch[3];
Fout[k] += scratch[3];
Fout[k+m] = scratch[5] + scratch[4];
Fout[k+3*m] = scratch[5] - scratch[4];
}
}
inline
void bfly3( Complex * Fout, const size_t fstride, const size_t m)
{
size_t k=m;
const size_t m2 = 2*m;
Complex *tw1,*tw2;
Complex scratch[5];
Complex epi3;
epi3 = m_twiddles[fstride*m];
tw1=tw2=&m_twiddles[0];
do{
scratch[1]=Fout[m] * *tw1;
scratch[2]=Fout[m2] * *tw2;
scratch[3]=scratch[1]+scratch[2];
scratch[0]=scratch[1]-scratch[2];
tw1 += fstride;
tw2 += fstride*2;
Fout[m] = Complex( Fout->real() - Scalar(.5)*scratch[3].real() , Fout->imag() - Scalar(.5)*scratch[3].imag() );
scratch[0] *= epi3.imag();
*Fout += scratch[3];
Fout[m2] = Complex( Fout[m].real() + scratch[0].imag() , Fout[m].imag() - scratch[0].real() );
Fout[m] += Complex( -scratch[0].imag(),scratch[0].real() );
++Fout;
}while(--k);
}
inline
void bfly5( Complex * Fout, const size_t fstride, const size_t m)
{
Complex *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
size_t u;
Complex scratch[13];
Complex * twiddles = &m_twiddles[0];
Complex *tw;
Complex ya,yb;
ya = twiddles[fstride*m];
yb = twiddles[fstride*2*m];
Fout0=Fout;
Fout1=Fout0+m;
Fout2=Fout0+2*m;
Fout3=Fout0+3*m;
Fout4=Fout0+4*m;
tw=twiddles;
for ( u=0; u<m; ++u ) {
scratch[0] = *Fout0;
scratch[1] = *Fout1 * tw[u*fstride];
scratch[2] = *Fout2 * tw[2*u*fstride];
scratch[3] = *Fout3 * tw[3*u*fstride];
scratch[4] = *Fout4 * tw[4*u*fstride];
scratch[7] = scratch[1] + scratch[4];
scratch[10] = scratch[1] - scratch[4];
scratch[8] = scratch[2] + scratch[3];
scratch[9] = scratch[2] - scratch[3];
*Fout0 += scratch[7];
*Fout0 += scratch[8];
scratch[5] = scratch[0] + Complex(
(scratch[7].real()*ya.real() ) + (scratch[8].real() *yb.real() ),
(scratch[7].imag()*ya.real()) + (scratch[8].imag()*yb.real())
);
scratch[6] = Complex(
(scratch[10].imag()*ya.imag()) + (scratch[9].imag()*yb.imag()),
-(scratch[10].real()*ya.imag()) - (scratch[9].real()*yb.imag())
);
*Fout1 = scratch[5] - scratch[6];
*Fout4 = scratch[5] + scratch[6];
scratch[11] = scratch[0] +
Complex(
(scratch[7].real()*yb.real()) + (scratch[8].real()*ya.real()),
(scratch[7].imag()*yb.real()) + (scratch[8].imag()*ya.real())
);
scratch[12] = Complex(
-(scratch[10].imag()*yb.imag()) + (scratch[9].imag()*ya.imag()),
(scratch[10].real()*yb.imag()) - (scratch[9].real()*ya.imag())
);
*Fout2=scratch[11]+scratch[12];
*Fout3=scratch[11]-scratch[12];
++Fout0;++Fout1;++Fout2;++Fout3;++Fout4;
}
}
/* perform the butterfly for one stage of a mixed radix FFT */
inline
void bfly_generic(
Complex * Fout,
const size_t fstride,
int m,
int p
)
{
int u,k,q1,q;
Complex * twiddles = &m_twiddles[0];
Complex t;
int Norig = static_cast<int>(m_twiddles.size());
Complex * scratchbuf = &m_scratchBuf[0];
for ( u=0; u<m; ++u ) {
k=u;
for ( q1=0 ; q1<p ; ++q1 ) {
scratchbuf[q1] = Fout[ k ];
k += m;
}
k=u;
for ( q1=0 ; q1<p ; ++q1 ) {
int twidx=0;
Fout[ k ] = scratchbuf[0];
for (q=1;q<p;++q ) {
twidx += static_cast<int>(fstride) * k;
if (twidx>=Norig) twidx-=Norig;
t=scratchbuf[q] * twiddles[twidx];
Fout[ k ] += t;
}
k += m;
}
}
}
};
template <typename _Scalar>
struct kissfft_impl
{
typedef _Scalar Scalar;
typedef std::complex<Scalar> Complex;
void clear()
{
m_plans.clear();
m_realTwiddles.clear();
}
inline
void fwd( Complex * dst,const Complex *src,int nfft)
{
get_plan(nfft,false).work(0, dst, src, 1,1);
}
inline
void fwd2( Complex * dst,const Complex *src,int n0,int n1)
{
EIGEN_UNUSED_VARIABLE(dst);
EIGEN_UNUSED_VARIABLE(src);
EIGEN_UNUSED_VARIABLE(n0);
EIGEN_UNUSED_VARIABLE(n1);
}
inline
void inv2( Complex * dst,const Complex *src,int n0,int n1)
{
EIGEN_UNUSED_VARIABLE(dst);
EIGEN_UNUSED_VARIABLE(src);
EIGEN_UNUSED_VARIABLE(n0);
EIGEN_UNUSED_VARIABLE(n1);
}
// real-to-complex forward FFT
// perform two FFTs of src even and src odd
// then twiddle to recombine them into the half-spectrum format
// then fill in the conjugate symmetric half
inline
void fwd( Complex * dst,const Scalar * src,int nfft)
{
if ( nfft&3 ) {
// use generic mode for odd
m_tmpBuf1.resize(nfft);
get_plan(nfft,false).work(0, &m_tmpBuf1[0], src, 1,1);
std::copy(m_tmpBuf1.begin(),m_tmpBuf1.begin()+(nfft>>1)+1,dst );
}else{
int ncfft = nfft>>1;
int ncfft2 = nfft>>2;
Complex * rtw = real_twiddles(ncfft2);
// use optimized mode for even real
fwd( dst, reinterpret_cast<const Complex*> (src), ncfft);
Complex dc = dst[0].real() + dst[0].imag();
Complex nyquist = dst[0].real() - dst[0].imag();
int k;
for ( k=1;k <= ncfft2 ; ++k ) {
Complex fpk = dst[k];
Complex fpnk = conj(dst[ncfft-k]);
Complex f1k = fpk + fpnk;
Complex f2k = fpk - fpnk;
Complex tw= f2k * rtw[k-1];
dst[k] = (f1k + tw) * Scalar(.5);
dst[ncfft-k] = conj(f1k -tw)*Scalar(.5);
}
dst[0] = dc;
dst[ncfft] = nyquist;
}
}
// inverse complex-to-complex
inline
void inv(Complex * dst,const Complex *src,int nfft)
{
get_plan(nfft,true).work(0, dst, src, 1,1);
}
// half-complex to scalar
inline
void inv( Scalar * dst,const Complex * src,int nfft)
{
if (nfft&3) {
m_tmpBuf1.resize(nfft);
m_tmpBuf2.resize(nfft);
std::copy(src,src+(nfft>>1)+1,m_tmpBuf1.begin() );
for (int k=1;k<(nfft>>1)+1;++k)
m_tmpBuf1[nfft-k] = conj(m_tmpBuf1[k]);
inv(&m_tmpBuf2[0],&m_tmpBuf1[0],nfft);
for (int k=0;k<nfft;++k)
dst[k] = m_tmpBuf2[k].real();
}else{
// optimized version for multiple of 4
int ncfft = nfft>>1;
int ncfft2 = nfft>>2;
Complex * rtw = real_twiddles(ncfft2);
m_tmpBuf1.resize(ncfft);
m_tmpBuf1[0] = Complex( src[0].real() + src[ncfft].real(), src[0].real() - src[ncfft].real() );
for (int k = 1; k <= ncfft / 2; ++k) {
Complex fk = src[k];
Complex fnkc = conj(src[ncfft-k]);
Complex fek = fk + fnkc;
Complex tmp = fk - fnkc;
Complex fok = tmp * conj(rtw[k-1]);
m_tmpBuf1[k] = fek + fok;
m_tmpBuf1[ncfft-k] = conj(fek - fok);
}
get_plan(ncfft,true).work(0, reinterpret_cast<Complex*>(dst), &m_tmpBuf1[0], 1,1);
}
}
protected:
typedef kiss_cpx_fft<Scalar> PlanData;
typedef std::map<int,PlanData> PlanMap;
PlanMap m_plans;
std::map<int, std::vector<Complex> > m_realTwiddles;
std::vector<Complex> m_tmpBuf1;
std::vector<Complex> m_tmpBuf2;
inline
int PlanKey(int nfft, bool isinverse) const { return (nfft<<1) | int(isinverse); }
inline
PlanData & get_plan(int nfft, bool inverse)
{
// TODO look for PlanKey(nfft, ! inverse) and conjugate the twiddles
PlanData & pd = m_plans[ PlanKey(nfft,inverse) ];
if ( pd.m_twiddles.size() == 0 ) {
pd.make_twiddles(nfft,inverse);
pd.factorize(nfft);
}
return pd;
}
inline
Complex * real_twiddles(int ncfft2)
{
using std::acos;
std::vector<Complex> & twidref = m_realTwiddles[ncfft2];// creates new if not there
if ( (int)twidref.size() != ncfft2 ) {
twidref.resize(ncfft2);
int ncfft= ncfft2<<1;
Scalar pi = acos( Scalar(-1) );
for (int k=1;k<=ncfft2;++k)
twidref[k-1] = exp( Complex(0,-pi * (Scalar(k) / ncfft + Scalar(.5)) ) );
}
return &twidref[0];
}
};
} // end namespace internal
} // end namespace Eigen
/* vim: set filetype=cpp et sw=2 ts=2 ai: */

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_IterativeSolvers_SRCS "*.h")
INSTALL(FILES
${Eigen_IterativeSolvers_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/IterativeSolvers COMPONENT Devel
)

View File

@@ -0,0 +1,189 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
/* NOTE The functions of this file have been adapted from the GMM++ library */
//========================================================================
//
// Copyright (C) 2002-2007 Yves Renard
//
// This file is a part of GETFEM++
//
// Getfem++ is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; version 2.1 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
// USA.
//
//========================================================================
#include "../../../../Eigen/src/Core/util/NonMPL2.h"
#ifndef EIGEN_CONSTRAINEDCG_H
#define EIGEN_CONSTRAINEDCG_H
#include <Eigen/Core>
namespace Eigen {
namespace internal {
/** \ingroup IterativeSolvers_Module
* Compute the pseudo inverse of the non-square matrix C such that
* \f$ CINV = (C * C^T)^{-1} * C \f$ based on a conjugate gradient method.
*
* This function is internally used by constrained_cg.
*/
template <typename CMatrix, typename CINVMatrix>
void pseudo_inverse(const CMatrix &C, CINVMatrix &CINV)
{
// optimisable : copie de la ligne, precalcul de C * trans(C).
typedef typename CMatrix::Scalar Scalar;
typedef typename CMatrix::Index Index;
// FIXME use sparse vectors ?
typedef Matrix<Scalar,Dynamic,1> TmpVec;
Index rows = C.rows(), cols = C.cols();
TmpVec d(rows), e(rows), l(cols), p(rows), q(rows), r(rows);
Scalar rho, rho_1, alpha;
d.setZero();
typedef Triplet<double> T;
std::vector<T> tripletList;
for (Index i = 0; i < rows; ++i)
{
d[i] = 1.0;
rho = 1.0;
e.setZero();
r = d;
p = d;
while (rho >= 1e-38)
{ /* conjugate gradient to compute e */
/* which is the i-th row of inv(C * trans(C)) */
l = C.transpose() * p;
q = C * l;
alpha = rho / p.dot(q);
e += alpha * p;
r += -alpha * q;
rho_1 = rho;
rho = r.dot(r);
p = (rho/rho_1) * p + r;
}
l = C.transpose() * e; // l is the i-th row of CINV
// FIXME add a generic "prune/filter" expression for both dense and sparse object to sparse
for (Index j=0; j<l.size(); ++j)
if (l[j]<1e-15)
tripletList.push_back(T(i,j,l(j)));
d[i] = 0.0;
}
CINV.setFromTriplets(tripletList.begin(), tripletList.end());
}
/** \ingroup IterativeSolvers_Module
* Constrained conjugate gradient
*
* Computes the minimum of \f$ 1/2((Ax).x) - bx \f$ under the contraint \f$ Cx \le f \f$
*/
template<typename TMatrix, typename CMatrix,
typename VectorX, typename VectorB, typename VectorF>
void constrained_cg(const TMatrix& A, const CMatrix& C, VectorX& x,
const VectorB& b, const VectorF& f, IterationController &iter)
{
using std::sqrt;
typedef typename TMatrix::Scalar Scalar;
typedef typename TMatrix::Index Index;
typedef Matrix<Scalar,Dynamic,1> TmpVec;
Scalar rho = 1.0, rho_1, lambda, gamma;
Index xSize = x.size();
TmpVec p(xSize), q(xSize), q2(xSize),
r(xSize), old_z(xSize), z(xSize),
memox(xSize);
std::vector<bool> satured(C.rows());
p.setZero();
iter.setRhsNorm(sqrt(b.dot(b))); // gael vect_sp(PS, b, b)
if (iter.rhsNorm() == 0.0) iter.setRhsNorm(1.0);
SparseMatrix<Scalar,RowMajor> CINV(C.rows(), C.cols());
pseudo_inverse(C, CINV);
while(true)
{
// computation of residual
old_z = z;
memox = x;
r = b;
r += A * -x;
z = r;
bool transition = false;
for (Index i = 0; i < C.rows(); ++i)
{
Scalar al = C.row(i).dot(x) - f.coeff(i);
if (al >= -1.0E-15)
{
if (!satured[i])
{
satured[i] = true;
transition = true;
}
Scalar bb = CINV.row(i).dot(z);
if (bb > 0.0)
// FIXME: we should allow that: z += -bb * C.row(i);
for (typename CMatrix::InnerIterator it(C,i); it; ++it)
z.coeffRef(it.index()) -= bb*it.value();
}
else
satured[i] = false;
}
// descent direction
rho_1 = rho;
rho = r.dot(z);
if (iter.finished(rho)) break;
if (iter.noiseLevel() > 0 && transition) std::cerr << "CCG: transition\n";
if (transition || iter.first()) gamma = 0.0;
else gamma = (std::max)(0.0, (rho - old_z.dot(z)) / rho_1);
p = z + gamma*p;
++iter;
// one dimensionnal optimization
q = A * p;
lambda = rho / q.dot(p);
for (Index i = 0; i < C.rows(); ++i)
{
if (!satured[i])
{
Scalar bb = C.row(i).dot(p) - f[i];
if (bb > 0.0)
lambda = (std::min)(lambda, (f.coeff(i)-C.row(i).dot(x)) / bb);
}
}
x += lambda * p;
memox -= x;
}
}
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_CONSTRAINEDCG_H

View File

@@ -0,0 +1,542 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2012 Désiré Nuentsa-Wakam <desire.nuentsa_wakam@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_DGMRES_H
#define EIGEN_DGMRES_H
#include <Eigen/Eigenvalues>
namespace Eigen {
template< typename _MatrixType,
typename _Preconditioner = DiagonalPreconditioner<typename _MatrixType::Scalar> >
class DGMRES;
namespace internal {
template< typename _MatrixType, typename _Preconditioner>
struct traits<DGMRES<_MatrixType,_Preconditioner> >
{
typedef _MatrixType MatrixType;
typedef _Preconditioner Preconditioner;
};
/** \brief Computes a permutation vector to have a sorted sequence
* \param vec The vector to reorder.
* \param perm gives the sorted sequence on output. Must be initialized with 0..n-1
* \param ncut Put the ncut smallest elements at the end of the vector
* WARNING This is an expensive sort, so should be used only
* for small size vectors
* TODO Use modified QuickSplit or std::nth_element to get the smallest values
*/
template <typename VectorType, typename IndexType>
void sortWithPermutation (VectorType& vec, IndexType& perm, typename IndexType::Scalar& ncut)
{
eigen_assert(vec.size() == perm.size());
typedef typename IndexType::Scalar Index;
typedef typename VectorType::Scalar Scalar;
bool flag;
for (Index k = 0; k < ncut; k++)
{
flag = false;
for (Index j = 0; j < vec.size()-1; j++)
{
if ( vec(perm(j)) < vec(perm(j+1)) )
{
std::swap(perm(j),perm(j+1));
flag = true;
}
if (!flag) break; // The vector is in sorted order
}
}
}
}
/**
* \ingroup IterativeLInearSolvers_Module
* \brief A Restarted GMRES with deflation.
* This class implements a modification of the GMRES solver for
* sparse linear systems. The basis is built with modified
* Gram-Schmidt. At each restart, a few approximated eigenvectors
* corresponding to the smallest eigenvalues are used to build a
* preconditioner for the next cycle. This preconditioner
* for deflation can be combined with any other preconditioner,
* the IncompleteLUT for instance. The preconditioner is applied
* at right of the matrix and the combination is multiplicative.
*
* \tparam _MatrixType the type of the sparse matrix A, can be a dense or a sparse matrix.
* \tparam _Preconditioner the type of the preconditioner. Default is DiagonalPreconditioner
* Typical usage :
* \code
* SparseMatrix<double> A;
* VectorXd x, b;
* //Fill A and b ...
* DGMRES<SparseMatrix<double> > solver;
* solver.set_restart(30); // Set restarting value
* solver.setEigenv(1); // Set the number of eigenvalues to deflate
* solver.compute(A);
* x = solver.solve(b);
* \endcode
*
* References :
* [1] D. NUENTSA WAKAM and F. PACULL, Memory Efficient Hybrid
* Algebraic Solvers for Linear Systems Arising from Compressible
* Flows, Computers and Fluids, In Press,
* http://dx.doi.org/10.1016/j.compfluid.2012.03.023
* [2] K. Burrage and J. Erhel, On the performance of various
* adaptive preconditioned GMRES strategies, 5(1998), 101-121.
* [3] J. Erhel, K. Burrage and B. Pohl, Restarted GMRES
* preconditioned by deflation,J. Computational and Applied
* Mathematics, 69(1996), 303-318.
*
*/
template< typename _MatrixType, typename _Preconditioner>
class DGMRES : public IterativeSolverBase<DGMRES<_MatrixType,_Preconditioner> >
{
typedef IterativeSolverBase<DGMRES> Base;
using Base::mp_matrix;
using Base::m_error;
using Base::m_iterations;
using Base::m_info;
using Base::m_isInitialized;
using Base::m_tolerance;
public:
typedef _MatrixType MatrixType;
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::Index Index;
typedef typename MatrixType::RealScalar RealScalar;
typedef _Preconditioner Preconditioner;
typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix;
typedef Matrix<RealScalar,Dynamic,Dynamic> DenseRealMatrix;
typedef Matrix<Scalar,Dynamic,1> DenseVector;
typedef Matrix<RealScalar,Dynamic,1> DenseRealVector;
typedef Matrix<std::complex<RealScalar>, Dynamic, 1> ComplexVector;
/** Default constructor. */
DGMRES() : Base(),m_restart(30),m_neig(0),m_r(0),m_maxNeig(5),m_isDeflAllocated(false),m_isDeflInitialized(false) {}
/** Initialize the solver with matrix \a A for further \c Ax=b solving.
*
* This constructor is a shortcut for the default constructor followed
* by a call to compute().
*
* \warning this class stores a reference to the matrix A as well as some
* precomputed values that depend on it. Therefore, if \a A is changed
* this class becomes invalid. Call compute() to update it with the new
* matrix A, or modify a copy of A.
*/
template<typename MatrixDerived>
explicit DGMRES(const EigenBase<MatrixDerived>& A) : Base(A.derived()), m_restart(30),m_neig(0),m_r(0),m_maxNeig(5),m_isDeflAllocated(false),m_isDeflInitialized(false) {}
~DGMRES() {}
/** \returns the solution x of \f$ A x = b \f$ using the current decomposition of A
* \a x0 as an initial solution.
*
* \sa compute()
*/
template<typename Rhs,typename Guess>
inline const internal::solve_retval_with_guess<DGMRES, Rhs, Guess>
solveWithGuess(const MatrixBase<Rhs>& b, const Guess& x0) const
{
eigen_assert(m_isInitialized && "DGMRES is not initialized.");
eigen_assert(Base::rows()==b.rows()
&& "DGMRES::solve(): invalid number of rows of the right hand side matrix b");
return internal::solve_retval_with_guess
<DGMRES, Rhs, Guess>(*this, b.derived(), x0);
}
/** \internal */
template<typename Rhs,typename Dest>
void _solveWithGuess(const Rhs& b, Dest& x) const
{
bool failed = false;
for(int j=0; j<b.cols(); ++j)
{
m_iterations = Base::maxIterations();
m_error = Base::m_tolerance;
typename Dest::ColXpr xj(x,j);
dgmres(*mp_matrix, b.col(j), xj, Base::m_preconditioner);
}
m_info = failed ? NumericalIssue
: m_error <= Base::m_tolerance ? Success
: NoConvergence;
m_isInitialized = true;
}
/** \internal */
template<typename Rhs,typename Dest>
void _solve(const Rhs& b, Dest& x) const
{
x = b;
_solveWithGuess(b,x);
}
/**
* Get the restart value
*/
int restart() { return m_restart; }
/**
* Set the restart value (default is 30)
*/
void set_restart(const int restart) { m_restart=restart; }
/**
* Set the number of eigenvalues to deflate at each restart
*/
void setEigenv(const int neig)
{
m_neig = neig;
if (neig+1 > m_maxNeig) m_maxNeig = neig+1; // To allow for complex conjugates
}
/**
* Get the size of the deflation subspace size
*/
int deflSize() {return m_r; }
/**
* Set the maximum size of the deflation subspace
*/
void setMaxEigenv(const int maxNeig) { m_maxNeig = maxNeig; }
protected:
// DGMRES algorithm
template<typename Rhs, typename Dest>
void dgmres(const MatrixType& mat,const Rhs& rhs, Dest& x, const Preconditioner& precond) const;
// Perform one cycle of GMRES
template<typename Dest>
int dgmresCycle(const MatrixType& mat, const Preconditioner& precond, Dest& x, DenseVector& r0, RealScalar& beta, const RealScalar& normRhs, int& nbIts) const;
// Compute data to use for deflation
int dgmresComputeDeflationData(const MatrixType& mat, const Preconditioner& precond, const Index& it, Index& neig) const;
// Apply deflation to a vector
template<typename RhsType, typename DestType>
int dgmresApplyDeflation(const RhsType& In, DestType& Out) const;
ComplexVector schurValues(const ComplexSchur<DenseMatrix>& schurofH) const;
ComplexVector schurValues(const RealSchur<DenseMatrix>& schurofH) const;
// Init data for deflation
void dgmresInitDeflation(Index& rows) const;
mutable DenseMatrix m_V; // Krylov basis vectors
mutable DenseMatrix m_H; // Hessenberg matrix
mutable DenseMatrix m_Hes; // Initial hessenberg matrix wihout Givens rotations applied
mutable Index m_restart; // Maximum size of the Krylov subspace
mutable DenseMatrix m_U; // Vectors that form the basis of the invariant subspace
mutable DenseMatrix m_MU; // matrix operator applied to m_U (for next cycles)
mutable DenseMatrix m_T; /* T=U^T*M^{-1}*A*U */
mutable PartialPivLU<DenseMatrix> m_luT; // LU factorization of m_T
mutable int m_neig; //Number of eigenvalues to extract at each restart
mutable int m_r; // Current number of deflated eigenvalues, size of m_U
mutable int m_maxNeig; // Maximum number of eigenvalues to deflate
mutable RealScalar m_lambdaN; //Modulus of the largest eigenvalue of A
mutable bool m_isDeflAllocated;
mutable bool m_isDeflInitialized;
//Adaptive strategy
mutable RealScalar m_smv; // Smaller multiple of the remaining number of steps allowed
mutable bool m_force; // Force the use of deflation at each restart
};
/**
* \brief Perform several cycles of restarted GMRES with modified Gram Schmidt,
*
* A right preconditioner is used combined with deflation.
*
*/
template< typename _MatrixType, typename _Preconditioner>
template<typename Rhs, typename Dest>
void DGMRES<_MatrixType, _Preconditioner>::dgmres(const MatrixType& mat,const Rhs& rhs, Dest& x,
const Preconditioner& precond) const
{
//Initialization
int n = mat.rows();
DenseVector r0(n);
int nbIts = 0;
m_H.resize(m_restart+1, m_restart);
m_Hes.resize(m_restart, m_restart);
m_V.resize(n,m_restart+1);
//Initial residual vector and intial norm
x = precond.solve(x);
r0 = rhs - mat * x;
RealScalar beta = r0.norm();
RealScalar normRhs = rhs.norm();
m_error = beta/normRhs;
if(m_error < m_tolerance)
m_info = Success;
else
m_info = NoConvergence;
// Iterative process
while (nbIts < m_iterations && m_info == NoConvergence)
{
dgmresCycle(mat, precond, x, r0, beta, normRhs, nbIts);
// Compute the new residual vector for the restart
if (nbIts < m_iterations && m_info == NoConvergence)
r0 = rhs - mat * x;
}
}
/**
* \brief Perform one restart cycle of DGMRES
* \param mat The coefficient matrix
* \param precond The preconditioner
* \param x the new approximated solution
* \param r0 The initial residual vector
* \param beta The norm of the residual computed so far
* \param normRhs The norm of the right hand side vector
* \param nbIts The number of iterations
*/
template< typename _MatrixType, typename _Preconditioner>
template<typename Dest>
int DGMRES<_MatrixType, _Preconditioner>::dgmresCycle(const MatrixType& mat, const Preconditioner& precond, Dest& x, DenseVector& r0, RealScalar& beta, const RealScalar& normRhs, int& nbIts) const
{
//Initialization
DenseVector g(m_restart+1); // Right hand side of the least square problem
g.setZero();
g(0) = Scalar(beta);
m_V.col(0) = r0/beta;
m_info = NoConvergence;
std::vector<JacobiRotation<Scalar> >gr(m_restart); // Givens rotations
int it = 0; // Number of inner iterations
int n = mat.rows();
DenseVector tv1(n), tv2(n); //Temporary vectors
while (m_info == NoConvergence && it < m_restart && nbIts < m_iterations)
{
// Apply preconditioner(s) at right
if (m_isDeflInitialized )
{
dgmresApplyDeflation(m_V.col(it), tv1); // Deflation
tv2 = precond.solve(tv1);
}
else
{
tv2 = precond.solve(m_V.col(it)); // User's selected preconditioner
}
tv1 = mat * tv2;
// Orthogonalize it with the previous basis in the basis using modified Gram-Schmidt
Scalar coef;
for (int i = 0; i <= it; ++i)
{
coef = tv1.dot(m_V.col(i));
tv1 = tv1 - coef * m_V.col(i);
m_H(i,it) = coef;
m_Hes(i,it) = coef;
}
// Normalize the vector
coef = tv1.norm();
m_V.col(it+1) = tv1/coef;
m_H(it+1, it) = coef;
// m_Hes(it+1,it) = coef;
// FIXME Check for happy breakdown
// Update Hessenberg matrix with Givens rotations
for (int i = 1; i <= it; ++i)
{
m_H.col(it).applyOnTheLeft(i-1,i,gr[i-1].adjoint());
}
// Compute the new plane rotation
gr[it].makeGivens(m_H(it, it), m_H(it+1,it));
// Apply the new rotation
m_H.col(it).applyOnTheLeft(it,it+1,gr[it].adjoint());
g.applyOnTheLeft(it,it+1, gr[it].adjoint());
beta = std::abs(g(it+1));
m_error = beta/normRhs;
std::cerr << nbIts << " Relative Residual Norm " << m_error << std::endl;
it++; nbIts++;
if (m_error < m_tolerance)
{
// The method has converged
m_info = Success;
break;
}
}
// Compute the new coefficients by solving the least square problem
// it++;
//FIXME Check first if the matrix is singular ... zero diagonal
DenseVector nrs(m_restart);
nrs = m_H.topLeftCorner(it,it).template triangularView<Upper>().solve(g.head(it));
// Form the new solution
if (m_isDeflInitialized)
{
tv1 = m_V.leftCols(it) * nrs;
dgmresApplyDeflation(tv1, tv2);
x = x + precond.solve(tv2);
}
else
x = x + precond.solve(m_V.leftCols(it) * nrs);
// Go for a new cycle and compute data for deflation
if(nbIts < m_iterations && m_info == NoConvergence && m_neig > 0 && (m_r+m_neig) < m_maxNeig)
dgmresComputeDeflationData(mat, precond, it, m_neig);
return 0;
}
template< typename _MatrixType, typename _Preconditioner>
void DGMRES<_MatrixType, _Preconditioner>::dgmresInitDeflation(Index& rows) const
{
m_U.resize(rows, m_maxNeig);
m_MU.resize(rows, m_maxNeig);
m_T.resize(m_maxNeig, m_maxNeig);
m_lambdaN = 0.0;
m_isDeflAllocated = true;
}
template< typename _MatrixType, typename _Preconditioner>
inline typename DGMRES<_MatrixType, _Preconditioner>::ComplexVector DGMRES<_MatrixType, _Preconditioner>::schurValues(const ComplexSchur<DenseMatrix>& schurofH) const
{
return schurofH.matrixT().diagonal();
}
template< typename _MatrixType, typename _Preconditioner>
inline typename DGMRES<_MatrixType, _Preconditioner>::ComplexVector DGMRES<_MatrixType, _Preconditioner>::schurValues(const RealSchur<DenseMatrix>& schurofH) const
{
typedef typename MatrixType::Index Index;
const DenseMatrix& T = schurofH.matrixT();
Index it = T.rows();
ComplexVector eig(it);
Index j = 0;
while (j < it-1)
{
if (T(j+1,j) ==Scalar(0))
{
eig(j) = std::complex<RealScalar>(T(j,j),RealScalar(0));
j++;
}
else
{
eig(j) = std::complex<RealScalar>(T(j,j),T(j+1,j));
eig(j+1) = std::complex<RealScalar>(T(j,j+1),T(j+1,j+1));
j++;
}
}
if (j < it-1) eig(j) = std::complex<RealScalar>(T(j,j),RealScalar(0));
return eig;
}
template< typename _MatrixType, typename _Preconditioner>
int DGMRES<_MatrixType, _Preconditioner>::dgmresComputeDeflationData(const MatrixType& mat, const Preconditioner& precond, const Index& it, Index& neig) const
{
// First, find the Schur form of the Hessenberg matrix H
typename internal::conditional<NumTraits<Scalar>::IsComplex, ComplexSchur<DenseMatrix>, RealSchur<DenseMatrix> >::type schurofH;
bool computeU = true;
DenseMatrix matrixQ(it,it);
matrixQ.setIdentity();
schurofH.computeFromHessenberg(m_Hes.topLeftCorner(it,it), matrixQ, computeU);
ComplexVector eig(it);
Matrix<Index,Dynamic,1>perm(it);
eig = this->schurValues(schurofH);
// Reorder the absolute values of Schur values
DenseRealVector modulEig(it);
for (int j=0; j<it; ++j) modulEig(j) = std::abs(eig(j));
perm.setLinSpaced(it,0,it-1);
internal::sortWithPermutation(modulEig, perm, neig);
if (!m_lambdaN)
{
m_lambdaN = (std::max)(modulEig.maxCoeff(), m_lambdaN);
}
//Count the real number of extracted eigenvalues (with complex conjugates)
int nbrEig = 0;
while (nbrEig < neig)
{
if(eig(perm(it-nbrEig-1)).imag() == RealScalar(0)) nbrEig++;
else nbrEig += 2;
}
// Extract the Schur vectors corresponding to the smallest Ritz values
DenseMatrix Sr(it, nbrEig);
Sr.setZero();
for (int j = 0; j < nbrEig; j++)
{
Sr.col(j) = schurofH.matrixU().col(perm(it-j-1));
}
// Form the Schur vectors of the initial matrix using the Krylov basis
DenseMatrix X;
X = m_V.leftCols(it) * Sr;
if (m_r)
{
// Orthogonalize X against m_U using modified Gram-Schmidt
for (int j = 0; j < nbrEig; j++)
for (int k =0; k < m_r; k++)
X.col(j) = X.col(j) - (m_U.col(k).dot(X.col(j)))*m_U.col(k);
}
// Compute m_MX = A * M^-1 * X
Index m = m_V.rows();
if (!m_isDeflAllocated)
dgmresInitDeflation(m);
DenseMatrix MX(m, nbrEig);
DenseVector tv1(m);
for (int j = 0; j < nbrEig; j++)
{
tv1 = mat * X.col(j);
MX.col(j) = precond.solve(tv1);
}
//Update m_T = [U'MU U'MX; X'MU X'MX]
m_T.block(m_r, m_r, nbrEig, nbrEig) = X.transpose() * MX;
if(m_r)
{
m_T.block(0, m_r, m_r, nbrEig) = m_U.leftCols(m_r).transpose() * MX;
m_T.block(m_r, 0, nbrEig, m_r) = X.transpose() * m_MU.leftCols(m_r);
}
// Save X into m_U and m_MX in m_MU
for (int j = 0; j < nbrEig; j++) m_U.col(m_r+j) = X.col(j);
for (int j = 0; j < nbrEig; j++) m_MU.col(m_r+j) = MX.col(j);
// Increase the size of the invariant subspace
m_r += nbrEig;
// Factorize m_T into m_luT
m_luT.compute(m_T.topLeftCorner(m_r, m_r));
//FIXME CHeck if the factorization was correctly done (nonsingular matrix)
m_isDeflInitialized = true;
return 0;
}
template<typename _MatrixType, typename _Preconditioner>
template<typename RhsType, typename DestType>
int DGMRES<_MatrixType, _Preconditioner>::dgmresApplyDeflation(const RhsType &x, DestType &y) const
{
DenseVector x1 = m_U.leftCols(m_r).transpose() * x;
y = x + m_U.leftCols(m_r) * ( m_lambdaN * m_luT.solve(x1) - x1);
return 0;
}
namespace internal {
template<typename _MatrixType, typename _Preconditioner, typename Rhs>
struct solve_retval<DGMRES<_MatrixType, _Preconditioner>, Rhs>
: solve_retval_base<DGMRES<_MatrixType, _Preconditioner>, Rhs>
{
typedef DGMRES<_MatrixType, _Preconditioner> Dec;
EIGEN_MAKE_SOLVE_HELPERS(Dec,Rhs)
template<typename Dest> void evalTo(Dest& dst) const
{
dec()._solve(rhs(),dst);
}
};
} // end namespace internal
} // end namespace Eigen
#endif

View File

@@ -0,0 +1,371 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2011 Gael Guennebaud <gael.guennebaud@inria.fr>
// Copyright (C) 2012, 2014 Kolja Brix <brix@igpm.rwth-aaachen.de>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_GMRES_H
#define EIGEN_GMRES_H
namespace Eigen {
namespace internal {
/**
* Generalized Minimal Residual Algorithm based on the
* Arnoldi algorithm implemented with Householder reflections.
*
* Parameters:
* \param mat matrix of linear system of equations
* \param Rhs right hand side vector of linear system of equations
* \param x on input: initial guess, on output: solution
* \param precond preconditioner used
* \param iters on input: maximum number of iterations to perform
* on output: number of iterations performed
* \param restart number of iterations for a restart
* \param tol_error on input: residual tolerance
* on output: residuum achieved
*
* \sa IterativeMethods::bicgstab()
*
*
* For references, please see:
*
* Saad, Y. and Schultz, M. H.
* GMRES: A Generalized Minimal Residual Algorithm for Solving Nonsymmetric Linear Systems.
* SIAM J.Sci.Stat.Comp. 7, 1986, pp. 856 - 869.
*
* Saad, Y.
* Iterative Methods for Sparse Linear Systems.
* Society for Industrial and Applied Mathematics, Philadelphia, 2003.
*
* Walker, H. F.
* Implementations of the GMRES method.
* Comput.Phys.Comm. 53, 1989, pp. 311 - 320.
*
* Walker, H. F.
* Implementation of the GMRES Method using Householder Transformations.
* SIAM J.Sci.Stat.Comp. 9, 1988, pp. 152 - 163.
*
*/
template<typename MatrixType, typename Rhs, typename Dest, typename Preconditioner>
bool gmres(const MatrixType & mat, const Rhs & rhs, Dest & x, const Preconditioner & precond,
int &iters, const int &restart, typename Dest::RealScalar & tol_error) {
using std::sqrt;
using std::abs;
typedef typename Dest::RealScalar RealScalar;
typedef typename Dest::Scalar Scalar;
typedef Matrix < Scalar, Dynamic, 1 > VectorType;
typedef Matrix < Scalar, Dynamic, Dynamic > FMatrixType;
RealScalar tol = tol_error;
const int maxIters = iters;
iters = 0;
const int m = mat.rows();
VectorType p0 = rhs - mat*x;
VectorType r0 = precond.solve(p0);
// is initial guess already good enough?
if(abs(r0.norm()) < tol) {
return true;
}
VectorType w = VectorType::Zero(restart + 1);
FMatrixType H = FMatrixType::Zero(m, restart + 1); // Hessenberg matrix
VectorType tau = VectorType::Zero(restart + 1);
std::vector < JacobiRotation < Scalar > > G(restart);
// generate first Householder vector
VectorType e(m-1);
RealScalar beta;
r0.makeHouseholder(e, tau.coeffRef(0), beta);
w(0)=(Scalar) beta;
H.bottomLeftCorner(m - 1, 1) = e;
for (int k = 1; k <= restart; ++k) {
++iters;
VectorType v = VectorType::Unit(m, k - 1), workspace(m);
// apply Householder reflections H_{1} ... H_{k-1} to v
for (int i = k - 1; i >= 0; --i) {
v.tail(m - i).applyHouseholderOnTheLeft(H.col(i).tail(m - i - 1), tau.coeffRef(i), workspace.data());
}
// apply matrix M to v: v = mat * v;
VectorType t=mat*v;
v=precond.solve(t);
// apply Householder reflections H_{k-1} ... H_{1} to v
for (int i = 0; i < k; ++i) {
v.tail(m - i).applyHouseholderOnTheLeft(H.col(i).tail(m - i - 1), tau.coeffRef(i), workspace.data());
}
if (v.tail(m - k).norm() != 0.0) {
if (k <= restart) {
// generate new Householder vector
VectorType e(m - k - 1);
RealScalar beta;
v.tail(m - k).makeHouseholder(e, tau.coeffRef(k), beta);
H.col(k).tail(m - k - 1) = e;
// apply Householder reflection H_{k} to v
v.tail(m - k).applyHouseholderOnTheLeft(H.col(k).tail(m - k - 1), tau.coeffRef(k), workspace.data());
}
}
if (k > 1) {
for (int i = 0; i < k - 1; ++i) {
// apply old Givens rotations to v
v.applyOnTheLeft(i, i + 1, G[i].adjoint());
}
}
if (k<m && v(k) != (Scalar) 0) {
// determine next Givens rotation
G[k - 1].makeGivens(v(k - 1), v(k));
// apply Givens rotation to v and w
v.applyOnTheLeft(k - 1, k, G[k - 1].adjoint());
w.applyOnTheLeft(k - 1, k, G[k - 1].adjoint());
}
// insert coefficients into upper matrix triangle
H.col(k - 1).head(k) = v.head(k);
bool stop=(k==m || abs(w(k)) < tol || iters == maxIters);
if (stop || k == restart) {
// solve upper triangular system
VectorType y = w.head(k);
H.topLeftCorner(k, k).template triangularView < Eigen::Upper > ().solveInPlace(y);
// use Horner-like scheme to calculate solution vector
VectorType x_new = y(k - 1) * VectorType::Unit(m, k - 1);
// apply Householder reflection H_{k} to x_new
x_new.tail(m - k + 1).applyHouseholderOnTheLeft(H.col(k - 1).tail(m - k), tau.coeffRef(k - 1), workspace.data());
for (int i = k - 2; i >= 0; --i) {
x_new += y(i) * VectorType::Unit(m, i);
// apply Householder reflection H_{i} to x_new
x_new.tail(m - i).applyHouseholderOnTheLeft(H.col(i).tail(m - i - 1), tau.coeffRef(i), workspace.data());
}
x += x_new;
if (stop) {
return true;
} else {
k=0;
// reset data for a restart r0 = rhs - mat * x;
VectorType p0=mat*x;
VectorType p1=precond.solve(p0);
r0 = rhs - p1;
// r0_sqnorm = r0.squaredNorm();
w = VectorType::Zero(restart + 1);
H = FMatrixType::Zero(m, restart + 1);
tau = VectorType::Zero(restart + 1);
// generate first Householder vector
RealScalar beta;
r0.makeHouseholder(e, tau.coeffRef(0), beta);
w(0)=(Scalar) beta;
H.bottomLeftCorner(m - 1, 1) = e;
}
}
}
return false;
}
}
template< typename _MatrixType,
typename _Preconditioner = DiagonalPreconditioner<typename _MatrixType::Scalar> >
class GMRES;
namespace internal {
template< typename _MatrixType, typename _Preconditioner>
struct traits<GMRES<_MatrixType,_Preconditioner> >
{
typedef _MatrixType MatrixType;
typedef _Preconditioner Preconditioner;
};
}
/** \ingroup IterativeLinearSolvers_Module
* \brief A GMRES solver for sparse square problems
*
* This class allows to solve for A.x = b sparse linear problems using a generalized minimal
* residual method. The vectors x and b can be either dense or sparse.
*
* \tparam _MatrixType the type of the sparse matrix A, can be a dense or a sparse matrix.
* \tparam _Preconditioner the type of the preconditioner. Default is DiagonalPreconditioner
*
* The maximal number of iterations and tolerance value can be controlled via the setMaxIterations()
* and setTolerance() methods. The defaults are the size of the problem for the maximal number of iterations
* and NumTraits<Scalar>::epsilon() for the tolerance.
*
* This class can be used as the direct solver classes. Here is a typical usage example:
* \code
* int n = 10000;
* VectorXd x(n), b(n);
* SparseMatrix<double> A(n,n);
* // fill A and b
* GMRES<SparseMatrix<double> > solver(A);
* x = solver.solve(b);
* std::cout << "#iterations: " << solver.iterations() << std::endl;
* std::cout << "estimated error: " << solver.error() << std::endl;
* // update b, and solve again
* x = solver.solve(b);
* \endcode
*
* By default the iterations start with x=0 as an initial guess of the solution.
* One can control the start using the solveWithGuess() method.
*
* \sa class SimplicialCholesky, DiagonalPreconditioner, IdentityPreconditioner
*/
template< typename _MatrixType, typename _Preconditioner>
class GMRES : public IterativeSolverBase<GMRES<_MatrixType,_Preconditioner> >
{
typedef IterativeSolverBase<GMRES> Base;
using Base::mp_matrix;
using Base::m_error;
using Base::m_iterations;
using Base::m_info;
using Base::m_isInitialized;
private:
int m_restart;
public:
typedef _MatrixType MatrixType;
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::Index Index;
typedef typename MatrixType::RealScalar RealScalar;
typedef _Preconditioner Preconditioner;
public:
/** Default constructor. */
GMRES() : Base(), m_restart(30) {}
/** Initialize the solver with matrix \a A for further \c Ax=b solving.
*
* This constructor is a shortcut for the default constructor followed
* by a call to compute().
*
* \warning this class stores a reference to the matrix A as well as some
* precomputed values that depend on it. Therefore, if \a A is changed
* this class becomes invalid. Call compute() to update it with the new
* matrix A, or modify a copy of A.
*/
template<typename MatrixDerived>
explicit GMRES(const EigenBase<MatrixDerived>& A) : Base(A.derived()), m_restart(30) {}
~GMRES() {}
/** Get the number of iterations after that a restart is performed.
*/
int get_restart() { return m_restart; }
/** Set the number of iterations after that a restart is performed.
* \param restart number of iterations for a restarti, default is 30.
*/
void set_restart(const int restart) { m_restart=restart; }
/** \returns the solution x of \f$ A x = b \f$ using the current decomposition of A
* \a x0 as an initial solution.
*
* \sa compute()
*/
template<typename Rhs,typename Guess>
inline const internal::solve_retval_with_guess<GMRES, Rhs, Guess>
solveWithGuess(const MatrixBase<Rhs>& b, const Guess& x0) const
{
eigen_assert(m_isInitialized && "GMRES is not initialized.");
eigen_assert(Base::rows()==b.rows()
&& "GMRES::solve(): invalid number of rows of the right hand side matrix b");
return internal::solve_retval_with_guess
<GMRES, Rhs, Guess>(*this, b.derived(), x0);
}
/** \internal */
template<typename Rhs,typename Dest>
void _solveWithGuess(const Rhs& b, Dest& x) const
{
bool failed = false;
for(int j=0; j<b.cols(); ++j)
{
m_iterations = Base::maxIterations();
m_error = Base::m_tolerance;
typename Dest::ColXpr xj(x,j);
if(!internal::gmres(*mp_matrix, b.col(j), xj, Base::m_preconditioner, m_iterations, m_restart, m_error))
failed = true;
}
m_info = failed ? NumericalIssue
: m_error <= Base::m_tolerance ? Success
: NoConvergence;
m_isInitialized = true;
}
/** \internal */
template<typename Rhs,typename Dest>
void _solve(const Rhs& b, Dest& x) const
{
x = b;
if(x.squaredNorm() == 0) return; // Check Zero right hand side
_solveWithGuess(b,x);
}
protected:
};
namespace internal {
template<typename _MatrixType, typename _Preconditioner, typename Rhs>
struct solve_retval<GMRES<_MatrixType, _Preconditioner>, Rhs>
: solve_retval_base<GMRES<_MatrixType, _Preconditioner>, Rhs>
{
typedef GMRES<_MatrixType, _Preconditioner> Dec;
EIGEN_MAKE_SOLVE_HELPERS(Dec,Rhs)
template<typename Dest> void evalTo(Dest& dst) const
{
dec()._solve(rhs(),dst);
}
};
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_GMRES_H

View File

@@ -0,0 +1,278 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2012 Désiré Nuentsa-Wakam <desire.nuentsa_wakam@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_INCOMPLETE_CHOlESKY_H
#define EIGEN_INCOMPLETE_CHOlESKY_H
#include "Eigen/src/IterativeLinearSolvers/IncompleteLUT.h"
#include <Eigen/OrderingMethods>
#include <list>
namespace Eigen {
/**
* \brief Modified Incomplete Cholesky with dual threshold
*
* References : C-J. Lin and J. J. Moré, Incomplete Cholesky Factorizations with
* Limited memory, SIAM J. Sci. Comput. 21(1), pp. 24-45, 1999
*
* \tparam _MatrixType The type of the sparse matrix. It should be a symmetric
* matrix. It is advised to give a row-oriented sparse matrix
* \tparam _UpLo The triangular part of the matrix to reference.
* \tparam _OrderingType
*/
template <typename Scalar, int _UpLo = Lower, typename _OrderingType = NaturalOrdering<int> >
class IncompleteCholesky : internal::noncopyable
{
public:
typedef SparseMatrix<Scalar,ColMajor> MatrixType;
typedef _OrderingType OrderingType;
typedef typename MatrixType::RealScalar RealScalar;
typedef typename MatrixType::Index Index;
typedef PermutationMatrix<Dynamic, Dynamic, Index> PermutationType;
typedef Matrix<Scalar,Dynamic,1> ScalarType;
typedef Matrix<Index,Dynamic, 1> IndexType;
typedef std::vector<std::list<Index> > VectorList;
enum { UpLo = _UpLo };
public:
IncompleteCholesky() : m_shift(1),m_factorizationIsOk(false) {}
IncompleteCholesky(const MatrixType& matrix) : m_shift(1),m_factorizationIsOk(false)
{
compute(matrix);
}
Index rows() const { return m_L.rows(); }
Index cols() const { return m_L.cols(); }
/** \brief Reports whether previous computation was successful.
*
* \returns \c Success if computation was succesful,
* \c NumericalIssue if the matrix appears to be negative.
*/
ComputationInfo info() const
{
eigen_assert(m_isInitialized && "IncompleteLLT is not initialized.");
return m_info;
}
/**
* \brief Set the initial shift parameter
*/
void setShift( Scalar shift) { m_shift = shift; }
/**
* \brief Computes the fill reducing permutation vector.
*/
template<typename MatrixType>
void analyzePattern(const MatrixType& mat)
{
OrderingType ord;
ord(mat.template selfadjointView<UpLo>(), m_perm);
m_analysisIsOk = true;
}
template<typename MatrixType>
void factorize(const MatrixType& amat);
template<typename MatrixType>
void compute (const MatrixType& matrix)
{
analyzePattern(matrix);
factorize(matrix);
}
template<typename Rhs, typename Dest>
void _solve(const Rhs& b, Dest& x) const
{
eigen_assert(m_factorizationIsOk && "factorize() should be called first");
if (m_perm.rows() == b.rows())
x = m_perm.inverse() * b;
else
x = b;
x = m_scal.asDiagonal() * x;
x = m_L.template triangularView<UnitLower>().solve(x);
x = m_L.adjoint().template triangularView<Upper>().solve(x);
if (m_perm.rows() == b.rows())
x = m_perm * x;
x = m_scal.asDiagonal() * x;
}
template<typename Rhs> inline const internal::solve_retval<IncompleteCholesky, Rhs>
solve(const MatrixBase<Rhs>& b) const
{
eigen_assert(m_factorizationIsOk && "IncompleteLLT did not succeed");
eigen_assert(m_isInitialized && "IncompleteLLT is not initialized.");
eigen_assert(cols()==b.rows()
&& "IncompleteLLT::solve(): invalid number of rows of the right hand side matrix b");
return internal::solve_retval<IncompleteCholesky, Rhs>(*this, b.derived());
}
protected:
SparseMatrix<Scalar,ColMajor> m_L; // The lower part stored in CSC
ScalarType m_scal; // The vector for scaling the matrix
Scalar m_shift; //The initial shift parameter
bool m_analysisIsOk;
bool m_factorizationIsOk;
bool m_isInitialized;
ComputationInfo m_info;
PermutationType m_perm;
private:
template <typename IdxType, typename SclType>
inline void updateList(const IdxType& colPtr, IdxType& rowIdx, SclType& vals, const Index& col, const Index& jk, IndexType& firstElt, VectorList& listCol);
};
template<typename Scalar, int _UpLo, typename OrderingType>
template<typename _MatrixType>
void IncompleteCholesky<Scalar,_UpLo, OrderingType>::factorize(const _MatrixType& mat)
{
using std::sqrt;
using std::min;
eigen_assert(m_analysisIsOk && "analyzePattern() should be called first");
// Dropping strategies : Keep only the p largest elements per column, where p is the number of elements in the column of the original matrix. Other strategies will be added
// Apply the fill-reducing permutation computed in analyzePattern()
if (m_perm.rows() == mat.rows() ) // To detect the null permutation
m_L.template selfadjointView<Lower>() = mat.template selfadjointView<_UpLo>().twistedBy(m_perm);
else
m_L.template selfadjointView<Lower>() = mat.template selfadjointView<_UpLo>();
Index n = m_L.cols();
Index nnz = m_L.nonZeros();
Map<ScalarType> vals(m_L.valuePtr(), nnz); //values
Map<IndexType> rowIdx(m_L.innerIndexPtr(), nnz); //Row indices
Map<IndexType> colPtr( m_L.outerIndexPtr(), n+1); // Pointer to the beginning of each row
IndexType firstElt(n-1); // for each j, points to the next entry in vals that will be used in the factorization
VectorList listCol(n); // listCol(j) is a linked list of columns to update column j
ScalarType curCol(n); // Store a nonzero values in each column
IndexType irow(n); // Row indices of nonzero elements in each column
// Computes the scaling factors
m_scal.resize(n);
for (int j = 0; j < n; j++)
{
m_scal(j) = m_L.col(j).norm();
m_scal(j) = sqrt(m_scal(j));
}
// Scale and compute the shift for the matrix
Scalar mindiag = vals[0];
for (int j = 0; j < n; j++){
for (int k = colPtr[j]; k < colPtr[j+1]; k++)
vals[k] /= (m_scal(j) * m_scal(rowIdx[k]));
mindiag = (min)(vals[colPtr[j]], mindiag);
}
if(mindiag < Scalar(0.)) m_shift = m_shift - mindiag;
// Apply the shift to the diagonal elements of the matrix
for (int j = 0; j < n; j++)
vals[colPtr[j]] += m_shift;
// jki version of the Cholesky factorization
for (int j=0; j < n; ++j)
{
//Left-looking factorize the column j
// First, load the jth column into curCol
Scalar diag = vals[colPtr[j]]; // It is assumed that only the lower part is stored
curCol.setZero();
irow.setLinSpaced(n,0,n-1);
for (int i = colPtr[j] + 1; i < colPtr[j+1]; i++)
{
curCol(rowIdx[i]) = vals[i];
irow(rowIdx[i]) = rowIdx[i];
}
std::list<int>::iterator k;
// Browse all previous columns that will update column j
for(k = listCol[j].begin(); k != listCol[j].end(); k++)
{
int jk = firstElt(*k); // First element to use in the column
jk += 1;
for (int i = jk; i < colPtr[*k+1]; i++)
{
curCol(rowIdx[i]) -= vals[i] * vals[jk] ;
}
updateList(colPtr,rowIdx,vals, *k, jk, firstElt, listCol);
}
// Scale the current column
if(RealScalar(diag) <= 0)
{
std::cerr << "\nNegative diagonal during Incomplete factorization... "<< j << "\n";
m_info = NumericalIssue;
return;
}
RealScalar rdiag = sqrt(RealScalar(diag));
vals[colPtr[j]] = rdiag;
for (int i = j+1; i < n; i++)
{
//Scale
curCol(i) /= rdiag;
//Update the remaining diagonals with curCol
vals[colPtr[i]] -= curCol(i) * curCol(i);
}
// Select the largest p elements
// p is the original number of elements in the column (without the diagonal)
int p = colPtr[j+1] - colPtr[j] - 1 ;
internal::QuickSplit(curCol, irow, p);
// Insert the largest p elements in the matrix
int cpt = 0;
for (int i = colPtr[j]+1; i < colPtr[j+1]; i++)
{
vals[i] = curCol(cpt);
rowIdx[i] = irow(cpt);
cpt ++;
}
// Get the first smallest row index and put it after the diagonal element
Index jk = colPtr(j)+1;
updateList(colPtr,rowIdx,vals,j,jk,firstElt,listCol);
}
m_factorizationIsOk = true;
m_isInitialized = true;
m_info = Success;
}
template<typename Scalar, int _UpLo, typename OrderingType>
template <typename IdxType, typename SclType>
inline void IncompleteCholesky<Scalar,_UpLo, OrderingType>::updateList(const IdxType& colPtr, IdxType& rowIdx, SclType& vals, const Index& col, const Index& jk, IndexType& firstElt, VectorList& listCol)
{
if (jk < colPtr(col+1) )
{
Index p = colPtr(col+1) - jk;
Index minpos;
rowIdx.segment(jk,p).minCoeff(&minpos);
minpos += jk;
if (rowIdx(minpos) != rowIdx(jk))
{
//Swap
std::swap(rowIdx(jk),rowIdx(minpos));
std::swap(vals(jk),vals(minpos));
}
firstElt(col) = jk;
listCol[rowIdx(jk)].push_back(col);
}
}
namespace internal {
template<typename _Scalar, int _UpLo, typename OrderingType, typename Rhs>
struct solve_retval<IncompleteCholesky<_Scalar, _UpLo, OrderingType>, Rhs>
: solve_retval_base<IncompleteCholesky<_Scalar, _UpLo, OrderingType>, Rhs>
{
typedef IncompleteCholesky<_Scalar, _UpLo, OrderingType> Dec;
EIGEN_MAKE_SOLVE_HELPERS(Dec,Rhs)
template<typename Dest> void evalTo(Dest& dst) const
{
dec()._solve(rhs(),dst);
}
};
} // end namespace internal
} // end namespace Eigen
#endif

View File

@@ -0,0 +1,113 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2011 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_INCOMPLETE_LU_H
#define EIGEN_INCOMPLETE_LU_H
namespace Eigen {
template <typename _Scalar>
class IncompleteLU
{
typedef _Scalar Scalar;
typedef Matrix<Scalar,Dynamic,1> Vector;
typedef typename Vector::Index Index;
typedef SparseMatrix<Scalar,RowMajor> FactorType;
public:
typedef Matrix<Scalar,Dynamic,Dynamic> MatrixType;
IncompleteLU() : m_isInitialized(false) {}
template<typename MatrixType>
IncompleteLU(const MatrixType& mat) : m_isInitialized(false)
{
compute(mat);
}
Index rows() const { return m_lu.rows(); }
Index cols() const { return m_lu.cols(); }
template<typename MatrixType>
IncompleteLU& compute(const MatrixType& mat)
{
m_lu = mat;
int size = mat.cols();
Vector diag(size);
for(int i=0; i<size; ++i)
{
typename FactorType::InnerIterator k_it(m_lu,i);
for(; k_it && k_it.index()<i; ++k_it)
{
int k = k_it.index();
k_it.valueRef() /= diag(k);
typename FactorType::InnerIterator j_it(k_it);
typename FactorType::InnerIterator kj_it(m_lu, k);
while(kj_it && kj_it.index()<=k) ++kj_it;
for(++j_it; j_it; )
{
if(kj_it.index()==j_it.index())
{
j_it.valueRef() -= k_it.value() * kj_it.value();
++j_it;
++kj_it;
}
else if(kj_it.index()<j_it.index()) ++kj_it;
else ++j_it;
}
}
if(k_it && k_it.index()==i) diag(i) = k_it.value();
else diag(i) = 1;
}
m_isInitialized = true;
return *this;
}
template<typename Rhs, typename Dest>
void _solve(const Rhs& b, Dest& x) const
{
x = m_lu.template triangularView<UnitLower>().solve(b);
x = m_lu.template triangularView<Upper>().solve(x);
}
template<typename Rhs> inline const internal::solve_retval<IncompleteLU, Rhs>
solve(const MatrixBase<Rhs>& b) const
{
eigen_assert(m_isInitialized && "IncompleteLU is not initialized.");
eigen_assert(cols()==b.rows()
&& "IncompleteLU::solve(): invalid number of rows of the right hand side matrix b");
return internal::solve_retval<IncompleteLU, Rhs>(*this, b.derived());
}
protected:
FactorType m_lu;
bool m_isInitialized;
};
namespace internal {
template<typename _MatrixType, typename Rhs>
struct solve_retval<IncompleteLU<_MatrixType>, Rhs>
: solve_retval_base<IncompleteLU<_MatrixType>, Rhs>
{
typedef IncompleteLU<_MatrixType> Dec;
EIGEN_MAKE_SOLVE_HELPERS(Dec,Rhs)
template<typename Dest> void evalTo(Dest& dst) const
{
dec()._solve(rhs(),dst);
}
};
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_INCOMPLETE_LU_H

View File

@@ -0,0 +1,154 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr>
/* NOTE The class IterationController has been adapted from the iteration
* class of the GMM++ and ITL libraries.
*/
//=======================================================================
// Copyright (C) 1997-2001
// Authors: Andrew Lumsdaine <lums@osl.iu.edu>
// Lie-Quan Lee <llee@osl.iu.edu>
//
// This file is part of the Iterative Template Library
//
// You should have received a copy of the License Agreement for the
// Iterative Template Library along with the software; see the
// file LICENSE.
//
// Permission to modify the code and to distribute modified code is
// granted, provided the text of this NOTICE is retained, a notice that
// the code was modified is included with the above COPYRIGHT NOTICE and
// with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
// file is distributed with the modified code.
//
// LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
// By way of example, but not limitation, Licensor MAKES NO
// REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
// PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
// OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
// OR OTHER RIGHTS.
//=======================================================================
//========================================================================
//
// Copyright (C) 2002-2007 Yves Renard
//
// This file is a part of GETFEM++
//
// Getfem++ is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; version 2.1 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
// USA.
//
//========================================================================
#include "../../../../Eigen/src/Core/util/NonMPL2.h"
#ifndef EIGEN_ITERATION_CONTROLLER_H
#define EIGEN_ITERATION_CONTROLLER_H
namespace Eigen {
/** \ingroup IterativeSolvers_Module
* \class IterationController
*
* \brief Controls the iterations of the iterative solvers
*
* This class has been adapted from the iteration class of GMM++ and ITL libraries.
*
*/
class IterationController
{
protected :
double m_rhsn; ///< Right hand side norm
size_t m_maxiter; ///< Max. number of iterations
int m_noise; ///< if noise > 0 iterations are printed
double m_resmax; ///< maximum residual
double m_resminreach, m_resadd;
size_t m_nit; ///< iteration number
double m_res; ///< last computed residual
bool m_written;
void (*m_callback)(const IterationController&);
public :
void init()
{
m_nit = 0; m_res = 0.0; m_written = false;
m_resminreach = 1E50; m_resadd = 0.0;
m_callback = 0;
}
IterationController(double r = 1.0E-8, int noi = 0, size_t mit = size_t(-1))
: m_rhsn(1.0), m_maxiter(mit), m_noise(noi), m_resmax(r) { init(); }
void operator ++(int) { m_nit++; m_written = false; m_resadd += m_res; }
void operator ++() { (*this)++; }
bool first() { return m_nit == 0; }
/* get/set the "noisyness" (verbosity) of the solvers */
int noiseLevel() const { return m_noise; }
void setNoiseLevel(int n) { m_noise = n; }
void reduceNoiseLevel() { if (m_noise > 0) m_noise--; }
double maxResidual() const { return m_resmax; }
void setMaxResidual(double r) { m_resmax = r; }
double residual() const { return m_res; }
/* change the user-definable callback, called after each iteration */
void setCallback(void (*t)(const IterationController&))
{
m_callback = t;
}
size_t iteration() const { return m_nit; }
void setIteration(size_t i) { m_nit = i; }
size_t maxIterarions() const { return m_maxiter; }
void setMaxIterations(size_t i) { m_maxiter = i; }
double rhsNorm() const { return m_rhsn; }
void setRhsNorm(double r) { m_rhsn = r; }
bool converged() const { return m_res <= m_rhsn * m_resmax; }
bool converged(double nr)
{
using std::abs;
m_res = abs(nr);
m_resminreach = (std::min)(m_resminreach, m_res);
return converged();
}
template<typename VectorType> bool converged(const VectorType &v)
{ return converged(v.squaredNorm()); }
bool finished(double nr)
{
if (m_callback) m_callback(*this);
if (m_noise > 0 && !m_written)
{
converged(nr);
m_written = true;
}
return (m_nit >= m_maxiter || converged(nr));
}
template <typename VectorType>
bool finished(const MatrixBase<VectorType> &v)
{ return finished(double(v.squaredNorm())); }
};
} // end namespace Eigen
#endif // EIGEN_ITERATION_CONTROLLER_H

View File

@@ -0,0 +1,311 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2012 Giacomo Po <gpo@ucla.edu>
// Copyright (C) 2011 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_MINRES_H_
#define EIGEN_MINRES_H_
namespace Eigen {
namespace internal {
/** \internal Low-level MINRES algorithm
* \param mat The matrix A
* \param rhs The right hand side vector b
* \param x On input and initial solution, on output the computed solution.
* \param precond A right preconditioner being able to efficiently solve for an
* approximation of Ax=b (regardless of b)
* \param iters On input the max number of iteration, on output the number of performed iterations.
* \param tol_error On input the tolerance error, on output an estimation of the relative error.
*/
template<typename MatrixType, typename Rhs, typename Dest, typename Preconditioner>
EIGEN_DONT_INLINE
void minres(const MatrixType& mat, const Rhs& rhs, Dest& x,
const Preconditioner& precond, int& iters,
typename Dest::RealScalar& tol_error)
{
using std::sqrt;
typedef typename Dest::RealScalar RealScalar;
typedef typename Dest::Scalar Scalar;
typedef Matrix<Scalar,Dynamic,1> VectorType;
// Check for zero rhs
const RealScalar rhsNorm2(rhs.squaredNorm());
if(rhsNorm2 == 0)
{
x.setZero();
iters = 0;
tol_error = 0;
return;
}
// initialize
const int maxIters(iters); // initialize maxIters to iters
const int N(mat.cols()); // the size of the matrix
const RealScalar threshold2(tol_error*tol_error*rhsNorm2); // convergence threshold (compared to residualNorm2)
// Initialize preconditioned Lanczos
VectorType v_old(N); // will be initialized inside loop
VectorType v( VectorType::Zero(N) ); //initialize v
VectorType v_new(rhs-mat*x); //initialize v_new
RealScalar residualNorm2(v_new.squaredNorm());
VectorType w(N); // will be initialized inside loop
VectorType w_new(precond.solve(v_new)); // initialize w_new
// RealScalar beta; // will be initialized inside loop
RealScalar beta_new2(v_new.dot(w_new));
eigen_assert(beta_new2 >= 0.0 && "PRECONDITIONER IS NOT POSITIVE DEFINITE");
RealScalar beta_new(sqrt(beta_new2));
const RealScalar beta_one(beta_new);
v_new /= beta_new;
w_new /= beta_new;
// Initialize other variables
RealScalar c(1.0); // the cosine of the Givens rotation
RealScalar c_old(1.0);
RealScalar s(0.0); // the sine of the Givens rotation
RealScalar s_old(0.0); // the sine of the Givens rotation
VectorType p_oold(N); // will be initialized in loop
VectorType p_old(VectorType::Zero(N)); // initialize p_old=0
VectorType p(p_old); // initialize p=0
RealScalar eta(1.0);
iters = 0; // reset iters
while ( iters < maxIters )
{
// Preconditioned Lanczos
/* Note that there are 4 variants on the Lanczos algorithm. These are
* described in Paige, C. C. (1972). Computational variants of
* the Lanczos method for the eigenproblem. IMA Journal of Applied
* Mathematics, 10(3), 373381. The current implementation corresponds
* to the case A(2,7) in the paper. It also corresponds to
* algorithm 6.14 in Y. Saad, Iterative Methods for Sparse Linear
* Systems, 2003 p.173. For the preconditioned version see
* A. Greenbaum, Iterative Methods for Solving Linear Systems, SIAM (1987).
*/
const RealScalar beta(beta_new);
v_old = v; // update: at first time step, this makes v_old = 0 so value of beta doesn't matter
// const VectorType v_old(v); // NOT SURE IF CREATING v_old EVERY ITERATION IS EFFICIENT
v = v_new; // update
w = w_new; // update
// const VectorType w(w_new); // NOT SURE IF CREATING w EVERY ITERATION IS EFFICIENT
v_new.noalias() = mat*w - beta*v_old; // compute v_new
const RealScalar alpha = v_new.dot(w);
v_new -= alpha*v; // overwrite v_new
w_new = precond.solve(v_new); // overwrite w_new
beta_new2 = v_new.dot(w_new); // compute beta_new
eigen_assert(beta_new2 >= 0.0 && "PRECONDITIONER IS NOT POSITIVE DEFINITE");
beta_new = sqrt(beta_new2); // compute beta_new
v_new /= beta_new; // overwrite v_new for next iteration
w_new /= beta_new; // overwrite w_new for next iteration
// Givens rotation
const RealScalar r2 =s*alpha+c*c_old*beta; // s, s_old, c and c_old are still from previous iteration
const RealScalar r3 =s_old*beta; // s, s_old, c and c_old are still from previous iteration
const RealScalar r1_hat=c*alpha-c_old*s*beta;
const RealScalar r1 =sqrt( std::pow(r1_hat,2) + std::pow(beta_new,2) );
c_old = c; // store for next iteration
s_old = s; // store for next iteration
c=r1_hat/r1; // new cosine
s=beta_new/r1; // new sine
// Update solution
p_oold = p_old;
// const VectorType p_oold(p_old); // NOT SURE IF CREATING p_oold EVERY ITERATION IS EFFICIENT
p_old = p;
p.noalias()=(w-r2*p_old-r3*p_oold) /r1; // IS NOALIAS REQUIRED?
x += beta_one*c*eta*p;
/* Update the squared residual. Note that this is the estimated residual.
The real residual |Ax-b|^2 may be slightly larger */
residualNorm2 *= s*s;
if ( residualNorm2 < threshold2)
{
break;
}
eta=-s*eta; // update eta
iters++; // increment iteration number (for output purposes)
}
/* Compute error. Note that this is the estimated error. The real
error |Ax-b|/|b| may be slightly larger */
tol_error = std::sqrt(residualNorm2 / rhsNorm2);
}
}
template< typename _MatrixType, int _UpLo=Lower,
typename _Preconditioner = IdentityPreconditioner>
// typename _Preconditioner = IdentityPreconditioner<typename _MatrixType::Scalar> > // preconditioner must be positive definite
class MINRES;
namespace internal {
template< typename _MatrixType, int _UpLo, typename _Preconditioner>
struct traits<MINRES<_MatrixType,_UpLo,_Preconditioner> >
{
typedef _MatrixType MatrixType;
typedef _Preconditioner Preconditioner;
};
}
/** \ingroup IterativeLinearSolvers_Module
* \brief A minimal residual solver for sparse symmetric problems
*
* This class allows to solve for A.x = b sparse linear problems using the MINRES algorithm
* of Paige and Saunders (1975). The sparse matrix A must be symmetric (possibly indefinite).
* The vectors x and b can be either dense or sparse.
*
* \tparam _MatrixType the type of the sparse matrix A, can be a dense or a sparse matrix.
* \tparam _UpLo the triangular part that will be used for the computations. It can be Lower
* or Upper. Default is Lower.
* \tparam _Preconditioner the type of the preconditioner. Default is DiagonalPreconditioner
*
* The maximal number of iterations and tolerance value can be controlled via the setMaxIterations()
* and setTolerance() methods. The defaults are the size of the problem for the maximal number of iterations
* and NumTraits<Scalar>::epsilon() for the tolerance.
*
* This class can be used as the direct solver classes. Here is a typical usage example:
* \code
* int n = 10000;
* VectorXd x(n), b(n);
* SparseMatrix<double> A(n,n);
* // fill A and b
* MINRES<SparseMatrix<double> > mr;
* mr.compute(A);
* x = mr.solve(b);
* std::cout << "#iterations: " << mr.iterations() << std::endl;
* std::cout << "estimated error: " << mr.error() << std::endl;
* // update b, and solve again
* x = mr.solve(b);
* \endcode
*
* By default the iterations start with x=0 as an initial guess of the solution.
* One can control the start using the solveWithGuess() method.
*
* \sa class ConjugateGradient, BiCGSTAB, SimplicialCholesky, DiagonalPreconditioner, IdentityPreconditioner
*/
template< typename _MatrixType, int _UpLo, typename _Preconditioner>
class MINRES : public IterativeSolverBase<MINRES<_MatrixType,_UpLo,_Preconditioner> >
{
typedef IterativeSolverBase<MINRES> Base;
using Base::mp_matrix;
using Base::m_error;
using Base::m_iterations;
using Base::m_info;
using Base::m_isInitialized;
public:
typedef _MatrixType MatrixType;
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::Index Index;
typedef typename MatrixType::RealScalar RealScalar;
typedef _Preconditioner Preconditioner;
enum {UpLo = _UpLo};
public:
/** Default constructor. */
MINRES() : Base() {}
/** Initialize the solver with matrix \a A for further \c Ax=b solving.
*
* This constructor is a shortcut for the default constructor followed
* by a call to compute().
*
* \warning this class stores a reference to the matrix A as well as some
* precomputed values that depend on it. Therefore, if \a A is changed
* this class becomes invalid. Call compute() to update it with the new
* matrix A, or modify a copy of A.
*/
template<typename MatrixDerived>
explicit MINRES(const EigenBase<MatrixDerived>& A) : Base(A.derived()) {}
/** Destructor. */
~MINRES(){}
/** \returns the solution x of \f$ A x = b \f$ using the current decomposition of A
* \a x0 as an initial solution.
*
* \sa compute()
*/
template<typename Rhs,typename Guess>
inline const internal::solve_retval_with_guess<MINRES, Rhs, Guess>
solveWithGuess(const MatrixBase<Rhs>& b, const Guess& x0) const
{
eigen_assert(m_isInitialized && "MINRES is not initialized.");
eigen_assert(Base::rows()==b.rows()
&& "MINRES::solve(): invalid number of rows of the right hand side matrix b");
return internal::solve_retval_with_guess
<MINRES, Rhs, Guess>(*this, b.derived(), x0);
}
/** \internal */
template<typename Rhs,typename Dest>
void _solveWithGuess(const Rhs& b, Dest& x) const
{
typedef typename internal::conditional<UpLo==(Lower|Upper),
const MatrixType&,
SparseSelfAdjointView<const MatrixType, UpLo>
>::type MatrixWrapperType;
m_iterations = Base::maxIterations();
m_error = Base::m_tolerance;
for(int j=0; j<b.cols(); ++j)
{
m_iterations = Base::maxIterations();
m_error = Base::m_tolerance;
typename Dest::ColXpr xj(x,j);
internal::minres(MatrixWrapperType(*mp_matrix), b.col(j), xj,
Base::m_preconditioner, m_iterations, m_error);
}
m_isInitialized = true;
m_info = m_error <= Base::m_tolerance ? Success : NoConvergence;
}
/** \internal */
template<typename Rhs,typename Dest>
void _solve(const Rhs& b, Dest& x) const
{
x.setZero();
_solveWithGuess(b,x);
}
protected:
};
namespace internal {
template<typename _MatrixType, int _UpLo, typename _Preconditioner, typename Rhs>
struct solve_retval<MINRES<_MatrixType,_UpLo,_Preconditioner>, Rhs>
: solve_retval_base<MINRES<_MatrixType,_UpLo,_Preconditioner>, Rhs>
{
typedef MINRES<_MatrixType,_UpLo,_Preconditioner> Dec;
EIGEN_MAKE_SOLVE_HELPERS(Dec,Rhs)
template<typename Dest> void evalTo(Dest& dst) const
{
dec()._solve(rhs(),dst);
}
};
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_MINRES_H

View File

@@ -0,0 +1,185 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2012 Desire NUENTSA WAKAM <desire.nuentsa_wakam@inria.fr
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_ITERSCALING_H
#define EIGEN_ITERSCALING_H
/**
* \ingroup IterativeSolvers_Module
* \brief iterative scaling algorithm to equilibrate rows and column norms in matrices
*
* This class can be used as a preprocessing tool to accelerate the convergence of iterative methods
*
* This feature is useful to limit the pivoting amount during LU/ILU factorization
* The scaling strategy as presented here preserves the symmetry of the problem
* NOTE It is assumed that the matrix does not have empty row or column,
*
* Example with key steps
* \code
* VectorXd x(n), b(n);
* SparseMatrix<double> A;
* // fill A and b;
* IterScaling<SparseMatrix<double> > scal;
* // Compute the left and right scaling vectors. The matrix is equilibrated at output
* scal.computeRef(A);
* // Scale the right hand side
* b = scal.LeftScaling().cwiseProduct(b);
* // Now, solve the equilibrated linear system with any available solver
*
* // Scale back the computed solution
* x = scal.RightScaling().cwiseProduct(x);
* \endcode
*
* \tparam _MatrixType the type of the matrix. It should be a real square sparsematrix
*
* References : D. Ruiz and B. Ucar, A Symmetry Preserving Algorithm for Matrix Scaling, INRIA Research report RR-7552
*
* \sa \ref IncompleteLUT
*/
namespace Eigen {
using std::abs;
template<typename _MatrixType>
class IterScaling
{
public:
typedef _MatrixType MatrixType;
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::Index Index;
public:
IterScaling() { init(); }
IterScaling(const MatrixType& matrix)
{
init();
compute(matrix);
}
~IterScaling() { }
/**
* Compute the left and right diagonal matrices to scale the input matrix @p mat
*
* FIXME This algorithm will be modified such that the diagonal elements are permuted on the diagonal.
*
* \sa LeftScaling() RightScaling()
*/
void compute (const MatrixType& mat)
{
int m = mat.rows();
int n = mat.cols();
eigen_assert((m>0 && m == n) && "Please give a non - empty matrix");
m_left.resize(m);
m_right.resize(n);
m_left.setOnes();
m_right.setOnes();
m_matrix = mat;
VectorXd Dr, Dc, DrRes, DcRes; // Temporary Left and right scaling vectors
Dr.resize(m); Dc.resize(n);
DrRes.resize(m); DcRes.resize(n);
double EpsRow = 1.0, EpsCol = 1.0;
int its = 0;
do
{ // Iterate until the infinite norm of each row and column is approximately 1
// Get the maximum value in each row and column
Dr.setZero(); Dc.setZero();
for (int k=0; k<m_matrix.outerSize(); ++k)
{
for (typename MatrixType::InnerIterator it(m_matrix, k); it; ++it)
{
if ( Dr(it.row()) < abs(it.value()) )
Dr(it.row()) = abs(it.value());
if ( Dc(it.col()) < abs(it.value()) )
Dc(it.col()) = abs(it.value());
}
}
for (int i = 0; i < m; ++i)
{
Dr(i) = std::sqrt(Dr(i));
Dc(i) = std::sqrt(Dc(i));
}
// Save the scaling factors
for (int i = 0; i < m; ++i)
{
m_left(i) /= Dr(i);
m_right(i) /= Dc(i);
}
// Scale the rows and the columns of the matrix
DrRes.setZero(); DcRes.setZero();
for (int k=0; k<m_matrix.outerSize(); ++k)
{
for (typename MatrixType::InnerIterator it(m_matrix, k); it; ++it)
{
it.valueRef() = it.value()/( Dr(it.row()) * Dc(it.col()) );
// Accumulate the norms of the row and column vectors
if ( DrRes(it.row()) < abs(it.value()) )
DrRes(it.row()) = abs(it.value());
if ( DcRes(it.col()) < abs(it.value()) )
DcRes(it.col()) = abs(it.value());
}
}
DrRes.array() = (1-DrRes.array()).abs();
EpsRow = DrRes.maxCoeff();
DcRes.array() = (1-DcRes.array()).abs();
EpsCol = DcRes.maxCoeff();
its++;
}while ( (EpsRow >m_tol || EpsCol > m_tol) && (its < m_maxits) );
m_isInitialized = true;
}
/** Compute the left and right vectors to scale the vectors
* the input matrix is scaled with the computed vectors at output
*
* \sa compute()
*/
void computeRef (MatrixType& mat)
{
compute (mat);
mat = m_matrix;
}
/** Get the vector to scale the rows of the matrix
*/
VectorXd& LeftScaling()
{
return m_left;
}
/** Get the vector to scale the columns of the matrix
*/
VectorXd& RightScaling()
{
return m_right;
}
/** Set the tolerance for the convergence of the iterative scaling algorithm
*/
void setTolerance(double tol)
{
m_tol = tol;
}
protected:
void init()
{
m_tol = 1e-10;
m_maxits = 5;
m_isInitialized = false;
}
MatrixType m_matrix;
mutable ComputationInfo m_info;
bool m_isInitialized;
VectorXd m_left; // Left scaling vector
VectorXd m_right; // m_right scaling vector
double m_tol;
int m_maxits; // Maximum number of iterations allowed
};
}
#endif

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_KroneckerProduct_SRCS "*.h")
INSTALL(FILES
${Eigen_KroneckerProduct_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/KroneckerProduct COMPONENT Devel
)

View File

@@ -0,0 +1,244 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2011 Kolja Brix <brix@igpm.rwth-aachen.de>
// Copyright (C) 2011 Andreas Platen <andiplaten@gmx.de>
// Copyright (C) 2012 Chen-Pang He <jdh8@ms63.hinet.net>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef KRONECKER_TENSOR_PRODUCT_H
#define KRONECKER_TENSOR_PRODUCT_H
namespace Eigen {
template<typename Scalar, int Options, typename Index> class SparseMatrix;
/*!
* \brief Kronecker tensor product helper class for dense matrices
*
* This class is the return value of kroneckerProduct(MatrixBase,
* MatrixBase). Use the function rather than construct this class
* directly to avoid specifying template prarameters.
*
* \tparam Lhs Type of the left-hand side, a matrix expression.
* \tparam Rhs Type of the rignt-hand side, a matrix expression.
*/
template<typename Lhs, typename Rhs>
class KroneckerProduct : public ReturnByValue<KroneckerProduct<Lhs,Rhs> >
{
private:
typedef ReturnByValue<KroneckerProduct> Base;
typedef typename Base::Scalar Scalar;
typedef typename Base::Index Index;
public:
/*! \brief Constructor. */
KroneckerProduct(const Lhs& A, const Rhs& B)
: m_A(A), m_B(B)
{}
/*! \brief Evaluate the Kronecker tensor product. */
template<typename Dest> void evalTo(Dest& dst) const;
inline Index rows() const { return m_A.rows() * m_B.rows(); }
inline Index cols() const { return m_A.cols() * m_B.cols(); }
Scalar coeff(Index row, Index col) const
{
return m_A.coeff(row / m_B.rows(), col / m_B.cols()) *
m_B.coeff(row % m_B.rows(), col % m_B.cols());
}
Scalar coeff(Index i) const
{
EIGEN_STATIC_ASSERT_VECTOR_ONLY(KroneckerProduct);
return m_A.coeff(i / m_A.size()) * m_B.coeff(i % m_A.size());
}
private:
typename Lhs::Nested m_A;
typename Rhs::Nested m_B;
};
/*!
* \brief Kronecker tensor product helper class for sparse matrices
*
* If at least one of the operands is a sparse matrix expression,
* then this class is returned and evaluates into a sparse matrix.
*
* This class is the return value of kroneckerProduct(EigenBase,
* EigenBase). Use the function rather than construct this class
* directly to avoid specifying template prarameters.
*
* \tparam Lhs Type of the left-hand side, a matrix expression.
* \tparam Rhs Type of the rignt-hand side, a matrix expression.
*/
template<typename Lhs, typename Rhs>
class KroneckerProductSparse : public EigenBase<KroneckerProductSparse<Lhs,Rhs> >
{
private:
typedef typename internal::traits<KroneckerProductSparse>::Index Index;
public:
/*! \brief Constructor. */
KroneckerProductSparse(const Lhs& A, const Rhs& B)
: m_A(A), m_B(B)
{}
/*! \brief Evaluate the Kronecker tensor product. */
template<typename Dest> void evalTo(Dest& dst) const;
inline Index rows() const { return m_A.rows() * m_B.rows(); }
inline Index cols() const { return m_A.cols() * m_B.cols(); }
template<typename Scalar, int Options, typename Index>
operator SparseMatrix<Scalar, Options, Index>()
{
SparseMatrix<Scalar, Options, Index> result;
evalTo(result.derived());
return result;
}
private:
typename Lhs::Nested m_A;
typename Rhs::Nested m_B;
};
template<typename Lhs, typename Rhs>
template<typename Dest>
void KroneckerProduct<Lhs,Rhs>::evalTo(Dest& dst) const
{
const int BlockRows = Rhs::RowsAtCompileTime,
BlockCols = Rhs::ColsAtCompileTime;
const Index Br = m_B.rows(),
Bc = m_B.cols();
for (Index i=0; i < m_A.rows(); ++i)
for (Index j=0; j < m_A.cols(); ++j)
Block<Dest,BlockRows,BlockCols>(dst,i*Br,j*Bc,Br,Bc) = m_A.coeff(i,j) * m_B;
}
template<typename Lhs, typename Rhs>
template<typename Dest>
void KroneckerProductSparse<Lhs,Rhs>::evalTo(Dest& dst) const
{
const Index Br = m_B.rows(),
Bc = m_B.cols();
dst.resize(rows(),cols());
dst.resizeNonZeros(0);
dst.reserve(m_A.nonZeros() * m_B.nonZeros());
for (Index kA=0; kA < m_A.outerSize(); ++kA)
{
for (Index kB=0; kB < m_B.outerSize(); ++kB)
{
for (typename Lhs::InnerIterator itA(m_A,kA); itA; ++itA)
{
for (typename Rhs::InnerIterator itB(m_B,kB); itB; ++itB)
{
const Index i = itA.row() * Br + itB.row(),
j = itA.col() * Bc + itB.col();
dst.insert(i,j) = itA.value() * itB.value();
}
}
}
}
}
namespace internal {
template<typename _Lhs, typename _Rhs>
struct traits<KroneckerProduct<_Lhs,_Rhs> >
{
typedef typename remove_all<_Lhs>::type Lhs;
typedef typename remove_all<_Rhs>::type Rhs;
typedef typename scalar_product_traits<typename Lhs::Scalar, typename Rhs::Scalar>::ReturnType Scalar;
enum {
Rows = size_at_compile_time<traits<Lhs>::RowsAtCompileTime, traits<Rhs>::RowsAtCompileTime>::ret,
Cols = size_at_compile_time<traits<Lhs>::ColsAtCompileTime, traits<Rhs>::ColsAtCompileTime>::ret,
MaxRows = size_at_compile_time<traits<Lhs>::MaxRowsAtCompileTime, traits<Rhs>::MaxRowsAtCompileTime>::ret,
MaxCols = size_at_compile_time<traits<Lhs>::MaxColsAtCompileTime, traits<Rhs>::MaxColsAtCompileTime>::ret,
CoeffReadCost = Lhs::CoeffReadCost + Rhs::CoeffReadCost + NumTraits<Scalar>::MulCost
};
typedef Matrix<Scalar,Rows,Cols> ReturnType;
};
template<typename _Lhs, typename _Rhs>
struct traits<KroneckerProductSparse<_Lhs,_Rhs> >
{
typedef MatrixXpr XprKind;
typedef typename remove_all<_Lhs>::type Lhs;
typedef typename remove_all<_Rhs>::type Rhs;
typedef typename scalar_product_traits<typename Lhs::Scalar, typename Rhs::Scalar>::ReturnType Scalar;
typedef typename promote_storage_type<typename traits<Lhs>::StorageKind, typename traits<Rhs>::StorageKind>::ret StorageKind;
typedef typename promote_index_type<typename Lhs::Index, typename Rhs::Index>::type Index;
enum {
LhsFlags = Lhs::Flags,
RhsFlags = Rhs::Flags,
RowsAtCompileTime = size_at_compile_time<traits<Lhs>::RowsAtCompileTime, traits<Rhs>::RowsAtCompileTime>::ret,
ColsAtCompileTime = size_at_compile_time<traits<Lhs>::ColsAtCompileTime, traits<Rhs>::ColsAtCompileTime>::ret,
MaxRowsAtCompileTime = size_at_compile_time<traits<Lhs>::MaxRowsAtCompileTime, traits<Rhs>::MaxRowsAtCompileTime>::ret,
MaxColsAtCompileTime = size_at_compile_time<traits<Lhs>::MaxColsAtCompileTime, traits<Rhs>::MaxColsAtCompileTime>::ret,
EvalToRowMajor = (LhsFlags & RhsFlags & RowMajorBit),
RemovedBits = ~(EvalToRowMajor ? 0 : RowMajorBit),
Flags = ((LhsFlags | RhsFlags) & HereditaryBits & RemovedBits)
| EvalBeforeNestingBit | EvalBeforeAssigningBit,
CoeffReadCost = Dynamic
};
};
} // end namespace internal
/*!
* \ingroup KroneckerProduct_Module
*
* Computes Kronecker tensor product of two dense matrices
*
* \warning If you want to replace a matrix by its Kronecker product
* with some matrix, do \b NOT do this:
* \code
* A = kroneckerProduct(A,B); // bug!!! caused by aliasing effect
* \endcode
* instead, use eval() to work around this:
* \code
* A = kroneckerProduct(A,B).eval();
* \endcode
*
* \param a Dense matrix a
* \param b Dense matrix b
* \return Kronecker tensor product of a and b
*/
template<typename A, typename B>
KroneckerProduct<A,B> kroneckerProduct(const MatrixBase<A>& a, const MatrixBase<B>& b)
{
return KroneckerProduct<A, B>(a.derived(), b.derived());
}
/*!
* \ingroup KroneckerProduct_Module
*
* Computes Kronecker tensor product of two matrices, at least one of
* which is sparse
*
* \param a Dense/sparse matrix a
* \param b Dense/sparse matrix b
* \return Kronecker tensor product of a and b, stored in a sparse
* matrix
*/
template<typename A, typename B>
KroneckerProductSparse<A,B> kroneckerProduct(const EigenBase<A>& a, const EigenBase<B>& b)
{
return KroneckerProductSparse<A,B>(a.derived(), b.derived());
}
} // end namespace Eigen
#endif // KRONECKER_TENSOR_PRODUCT_H

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_LevenbergMarquardt_SRCS "*.h")
INSTALL(FILES
${Eigen_LevenbergMarquardt_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/LevenbergMarquardt COMPONENT Devel
)

View File

@@ -0,0 +1,52 @@
Minpack Copyright Notice (1999) University of Chicago. All rights reserved
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the
following conditions are met:
1. Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
3. The end-user documentation included with the
redistribution, if any, must include the following
acknowledgment:
"This product includes software developed by the
University of Chicago, as Operator of Argonne National
Laboratory.
Alternately, this acknowledgment may appear in the software
itself, if and wherever such third-party acknowledgments
normally appear.
4. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS"
WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE
UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND
THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE
OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY
OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR
USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF
THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)
DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION
UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL
BE CORRECTED.
5. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT
HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF
ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,
INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF
ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF
PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER
SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT
(INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,
EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
POSSIBILITY OF SUCH LOSS OR DAMAGES.

View File

@@ -0,0 +1,85 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// This code initially comes from MINPACK whose original authors are:
// Copyright Jorge More - Argonne National Laboratory
// Copyright Burt Garbow - Argonne National Laboratory
// Copyright Ken Hillstrom - Argonne National Laboratory
//
// This Source Code Form is subject to the terms of the Minpack license
// (a BSD-like license) described in the campaigned CopyrightMINPACK.txt file.
#ifndef EIGEN_LMCOVAR_H
#define EIGEN_LMCOVAR_H
namespace Eigen {
namespace internal {
template <typename Scalar>
void covar(
Matrix< Scalar, Dynamic, Dynamic > &r,
const VectorXi& ipvt,
Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon()) )
{
using std::abs;
typedef DenseIndex Index;
/* Local variables */
Index i, j, k, l, ii, jj;
bool sing;
Scalar temp;
/* Function Body */
const Index n = r.cols();
const Scalar tolr = tol * abs(r(0,0));
Matrix< Scalar, Dynamic, 1 > wa(n);
eigen_assert(ipvt.size()==n);
/* form the inverse of r in the full upper triangle of r. */
l = -1;
for (k = 0; k < n; ++k)
if (abs(r(k,k)) > tolr) {
r(k,k) = 1. / r(k,k);
for (j = 0; j <= k-1; ++j) {
temp = r(k,k) * r(j,k);
r(j,k) = 0.;
r.col(k).head(j+1) -= r.col(j).head(j+1) * temp;
}
l = k;
}
/* form the full upper triangle of the inverse of (r transpose)*r */
/* in the full upper triangle of r. */
for (k = 0; k <= l; ++k) {
for (j = 0; j <= k-1; ++j)
r.col(j).head(j+1) += r.col(k).head(j+1) * r(j,k);
r.col(k).head(k+1) *= r(k,k);
}
/* form the full lower triangle of the covariance matrix */
/* in the strict lower triangle of r and in wa. */
for (j = 0; j < n; ++j) {
jj = ipvt[j];
sing = j > l;
for (i = 0; i <= j; ++i) {
if (sing)
r(i,j) = 0.;
ii = ipvt[i];
if (ii > jj)
r(ii,jj) = r(i,j);
if (ii < jj)
r(jj,ii) = r(i,j);
}
wa[jj] = r(j,j);
}
/* symmetrize the covariance matrix in r. */
r.topLeftCorner(n,n).template triangularView<StrictlyUpper>() = r.topLeftCorner(n,n).transpose();
r.diagonal() = wa;
}
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_LMCOVAR_H

View File

@@ -0,0 +1,202 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Thomas Capricelli <orzel@freehackers.org>
//
// This code initially comes from MINPACK whose original authors are:
// Copyright Jorge More - Argonne National Laboratory
// Copyright Burt Garbow - Argonne National Laboratory
// Copyright Ken Hillstrom - Argonne National Laboratory
//
// This Source Code Form is subject to the terms of the Minpack license
// (a BSD-like license) described in the campaigned CopyrightMINPACK.txt file.
#ifndef EIGEN_LMONESTEP_H
#define EIGEN_LMONESTEP_H
namespace Eigen {
template<typename FunctorType>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType>::minimizeOneStep(FVectorType &x)
{
using std::abs;
using std::sqrt;
RealScalar temp, temp1,temp2;
RealScalar ratio;
RealScalar pnorm, xnorm, fnorm1, actred, dirder, prered;
eigen_assert(x.size()==n); // check the caller is not cheating us
temp = 0.0; xnorm = 0.0;
/* calculate the jacobian matrix. */
Index df_ret = m_functor.df(x, m_fjac);
if (df_ret<0)
return LevenbergMarquardtSpace::UserAsked;
if (df_ret>0)
// numerical diff, we evaluated the function df_ret times
m_nfev += df_ret;
else m_njev++;
/* compute the qr factorization of the jacobian. */
for (int j = 0; j < x.size(); ++j)
m_wa2(j) = m_fjac.col(j).blueNorm();
QRSolver qrfac(m_fjac);
if(qrfac.info() != Success) {
m_info = NumericalIssue;
return LevenbergMarquardtSpace::ImproperInputParameters;
}
// Make a copy of the first factor with the associated permutation
m_rfactor = qrfac.matrixR();
m_permutation = (qrfac.colsPermutation());
/* on the first iteration and if external scaling is not used, scale according */
/* to the norms of the columns of the initial jacobian. */
if (m_iter == 1) {
if (!m_useExternalScaling)
for (Index j = 0; j < n; ++j)
m_diag[j] = (m_wa2[j]==0.)? 1. : m_wa2[j];
/* on the first iteration, calculate the norm of the scaled x */
/* and initialize the step bound m_delta. */
xnorm = m_diag.cwiseProduct(x).stableNorm();
m_delta = m_factor * xnorm;
if (m_delta == 0.)
m_delta = m_factor;
}
/* form (q transpose)*m_fvec and store the first n components in */
/* m_qtf. */
m_wa4 = m_fvec;
m_wa4 = qrfac.matrixQ().adjoint() * m_fvec;
m_qtf = m_wa4.head(n);
/* compute the norm of the scaled gradient. */
m_gnorm = 0.;
if (m_fnorm != 0.)
for (Index j = 0; j < n; ++j)
if (m_wa2[m_permutation.indices()[j]] != 0.)
m_gnorm = (std::max)(m_gnorm, abs( m_rfactor.col(j).head(j+1).dot(m_qtf.head(j+1)/m_fnorm) / m_wa2[m_permutation.indices()[j]]));
/* test for convergence of the gradient norm. */
if (m_gnorm <= m_gtol) {
m_info = Success;
return LevenbergMarquardtSpace::CosinusTooSmall;
}
/* rescale if necessary. */
if (!m_useExternalScaling)
m_diag = m_diag.cwiseMax(m_wa2);
do {
/* determine the levenberg-marquardt parameter. */
internal::lmpar2(qrfac, m_diag, m_qtf, m_delta, m_par, m_wa1);
/* store the direction p and x + p. calculate the norm of p. */
m_wa1 = -m_wa1;
m_wa2 = x + m_wa1;
pnorm = m_diag.cwiseProduct(m_wa1).stableNorm();
/* on the first iteration, adjust the initial step bound. */
if (m_iter == 1)
m_delta = (std::min)(m_delta,pnorm);
/* evaluate the function at x + p and calculate its norm. */
if ( m_functor(m_wa2, m_wa4) < 0)
return LevenbergMarquardtSpace::UserAsked;
++m_nfev;
fnorm1 = m_wa4.stableNorm();
/* compute the scaled actual reduction. */
actred = -1.;
if (Scalar(.1) * fnorm1 < m_fnorm)
actred = 1. - numext::abs2(fnorm1 / m_fnorm);
/* compute the scaled predicted reduction and */
/* the scaled directional derivative. */
m_wa3 = m_rfactor.template triangularView<Upper>() * (m_permutation.inverse() *m_wa1);
temp1 = numext::abs2(m_wa3.stableNorm() / m_fnorm);
temp2 = numext::abs2(sqrt(m_par) * pnorm / m_fnorm);
prered = temp1 + temp2 / Scalar(.5);
dirder = -(temp1 + temp2);
/* compute the ratio of the actual to the predicted */
/* reduction. */
ratio = 0.;
if (prered != 0.)
ratio = actred / prered;
/* update the step bound. */
if (ratio <= Scalar(.25)) {
if (actred >= 0.)
temp = RealScalar(.5);
if (actred < 0.)
temp = RealScalar(.5) * dirder / (dirder + RealScalar(.5) * actred);
if (RealScalar(.1) * fnorm1 >= m_fnorm || temp < RealScalar(.1))
temp = Scalar(.1);
/* Computing MIN */
m_delta = temp * (std::min)(m_delta, pnorm / RealScalar(.1));
m_par /= temp;
} else if (!(m_par != 0. && ratio < RealScalar(.75))) {
m_delta = pnorm / RealScalar(.5);
m_par = RealScalar(.5) * m_par;
}
/* test for successful iteration. */
if (ratio >= RealScalar(1e-4)) {
/* successful iteration. update x, m_fvec, and their norms. */
x = m_wa2;
m_wa2 = m_diag.cwiseProduct(x);
m_fvec = m_wa4;
xnorm = m_wa2.stableNorm();
m_fnorm = fnorm1;
++m_iter;
}
/* tests for convergence. */
if (abs(actred) <= m_ftol && prered <= m_ftol && Scalar(.5) * ratio <= 1. && m_delta <= m_xtol * xnorm)
{
m_info = Success;
return LevenbergMarquardtSpace::RelativeErrorAndReductionTooSmall;
}
if (abs(actred) <= m_ftol && prered <= m_ftol && Scalar(.5) * ratio <= 1.)
{
m_info = Success;
return LevenbergMarquardtSpace::RelativeReductionTooSmall;
}
if (m_delta <= m_xtol * xnorm)
{
m_info = Success;
return LevenbergMarquardtSpace::RelativeErrorTooSmall;
}
/* tests for termination and stringent tolerances. */
if (m_nfev >= m_maxfev)
{
m_info = NoConvergence;
return LevenbergMarquardtSpace::TooManyFunctionEvaluation;
}
if (abs(actred) <= NumTraits<Scalar>::epsilon() && prered <= NumTraits<Scalar>::epsilon() && Scalar(.5) * ratio <= 1.)
{
m_info = Success;
return LevenbergMarquardtSpace::FtolTooSmall;
}
if (m_delta <= NumTraits<Scalar>::epsilon() * xnorm)
{
m_info = Success;
return LevenbergMarquardtSpace::XtolTooSmall;
}
if (m_gnorm <= NumTraits<Scalar>::epsilon())
{
m_info = Success;
return LevenbergMarquardtSpace::GtolTooSmall;
}
} while (ratio < Scalar(1e-4));
return LevenbergMarquardtSpace::Running;
}
} // end namespace Eigen
#endif // EIGEN_LMONESTEP_H

View File

@@ -0,0 +1,160 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// This code initially comes from MINPACK whose original authors are:
// Copyright Jorge More - Argonne National Laboratory
// Copyright Burt Garbow - Argonne National Laboratory
// Copyright Ken Hillstrom - Argonne National Laboratory
//
// This Source Code Form is subject to the terms of the Minpack license
// (a BSD-like license) described in the campaigned CopyrightMINPACK.txt file.
#ifndef EIGEN_LMPAR_H
#define EIGEN_LMPAR_H
namespace Eigen {
namespace internal {
template <typename QRSolver, typename VectorType>
void lmpar2(
const QRSolver &qr,
const VectorType &diag,
const VectorType &qtb,
typename VectorType::Scalar m_delta,
typename VectorType::Scalar &par,
VectorType &x)
{
using std::sqrt;
using std::abs;
typedef typename QRSolver::MatrixType MatrixType;
typedef typename QRSolver::Scalar Scalar;
typedef typename QRSolver::Index Index;
/* Local variables */
Index j;
Scalar fp;
Scalar parc, parl;
Index iter;
Scalar temp, paru;
Scalar gnorm;
Scalar dxnorm;
// Make a copy of the triangular factor.
// This copy is modified during call the qrsolv
MatrixType s;
s = qr.matrixR();
/* Function Body */
const Scalar dwarf = (std::numeric_limits<Scalar>::min)();
const Index n = qr.matrixR().cols();
eigen_assert(n==diag.size());
eigen_assert(n==qtb.size());
VectorType wa1, wa2;
/* compute and store in x the gauss-newton direction. if the */
/* jacobian is rank-deficient, obtain a least squares solution. */
// const Index rank = qr.nonzeroPivots(); // exactly double(0.)
const Index rank = qr.rank(); // use a threshold
wa1 = qtb;
wa1.tail(n-rank).setZero();
//FIXME There is no solve in place for sparse triangularView
wa1.head(rank) = s.topLeftCorner(rank,rank).template triangularView<Upper>().solve(qtb.head(rank));
x = qr.colsPermutation()*wa1;
/* initialize the iteration counter. */
/* evaluate the function at the origin, and test */
/* for acceptance of the gauss-newton direction. */
iter = 0;
wa2 = diag.cwiseProduct(x);
dxnorm = wa2.blueNorm();
fp = dxnorm - m_delta;
if (fp <= Scalar(0.1) * m_delta) {
par = 0;
return;
}
/* if the jacobian is not rank deficient, the newton */
/* step provides a lower bound, parl, for the zero of */
/* the function. otherwise set this bound to zero. */
parl = 0.;
if (rank==n) {
wa1 = qr.colsPermutation().inverse() * diag.cwiseProduct(wa2)/dxnorm;
s.topLeftCorner(n,n).transpose().template triangularView<Lower>().solveInPlace(wa1);
temp = wa1.blueNorm();
parl = fp / m_delta / temp / temp;
}
/* calculate an upper bound, paru, for the zero of the function. */
for (j = 0; j < n; ++j)
wa1[j] = s.col(j).head(j+1).dot(qtb.head(j+1)) / diag[qr.colsPermutation().indices()(j)];
gnorm = wa1.stableNorm();
paru = gnorm / m_delta;
if (paru == 0.)
paru = dwarf / (std::min)(m_delta,Scalar(0.1));
/* if the input par lies outside of the interval (parl,paru), */
/* set par to the closer endpoint. */
par = (std::max)(par,parl);
par = (std::min)(par,paru);
if (par == 0.)
par = gnorm / dxnorm;
/* beginning of an iteration. */
while (true) {
++iter;
/* evaluate the function at the current value of par. */
if (par == 0.)
par = (std::max)(dwarf,Scalar(.001) * paru); /* Computing MAX */
wa1 = sqrt(par)* diag;
VectorType sdiag(n);
lmqrsolv(s, qr.colsPermutation(), wa1, qtb, x, sdiag);
wa2 = diag.cwiseProduct(x);
dxnorm = wa2.blueNorm();
temp = fp;
fp = dxnorm - m_delta;
/* if the function is small enough, accept the current value */
/* of par. also test for the exceptional cases where parl */
/* is zero or the number of iterations has reached 10. */
if (abs(fp) <= Scalar(0.1) * m_delta || (parl == 0. && fp <= temp && temp < 0.) || iter == 10)
break;
/* compute the newton correction. */
wa1 = qr.colsPermutation().inverse() * diag.cwiseProduct(wa2/dxnorm);
// we could almost use this here, but the diagonal is outside qr, in sdiag[]
for (j = 0; j < n; ++j) {
wa1[j] /= sdiag[j];
temp = wa1[j];
for (Index i = j+1; i < n; ++i)
wa1[i] -= s.coeff(i,j) * temp;
}
temp = wa1.blueNorm();
parc = fp / m_delta / temp / temp;
/* depending on the sign of the function, update parl or paru. */
if (fp > 0.)
parl = (std::max)(parl,par);
if (fp < 0.)
paru = (std::min)(paru,par);
/* compute an improved estimate for par. */
par = (std::max)(parl,par+parc);
}
if (iter == 0)
par = 0.;
return;
}
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_LMPAR_H

View File

@@ -0,0 +1,189 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Thomas Capricelli <orzel@freehackers.org>
// Copyright (C) 2012 Desire Nuentsa <desire.nuentsa_wakam@inria.fr>
//
// This code initially comes from MINPACK whose original authors are:
// Copyright Jorge More - Argonne National Laboratory
// Copyright Burt Garbow - Argonne National Laboratory
// Copyright Ken Hillstrom - Argonne National Laboratory
//
// This Source Code Form is subject to the terms of the Minpack license
// (a BSD-like license) described in the campaigned CopyrightMINPACK.txt file.
#ifndef EIGEN_LMQRSOLV_H
#define EIGEN_LMQRSOLV_H
namespace Eigen {
namespace internal {
template <typename Scalar,int Rows, int Cols, typename Index>
void lmqrsolv(
Matrix<Scalar,Rows,Cols> &s,
const PermutationMatrix<Dynamic,Dynamic,Index> &iPerm,
const Matrix<Scalar,Dynamic,1> &diag,
const Matrix<Scalar,Dynamic,1> &qtb,
Matrix<Scalar,Dynamic,1> &x,
Matrix<Scalar,Dynamic,1> &sdiag)
{
/* Local variables */
Index i, j, k, l;
Scalar temp;
Index n = s.cols();
Matrix<Scalar,Dynamic,1> wa(n);
JacobiRotation<Scalar> givens;
/* Function Body */
// the following will only change the lower triangular part of s, including
// the diagonal, though the diagonal is restored afterward
/* copy r and (q transpose)*b to preserve input and initialize s. */
/* in particular, save the diagonal elements of r in x. */
x = s.diagonal();
wa = qtb;
s.topLeftCorner(n,n).template triangularView<StrictlyLower>() = s.topLeftCorner(n,n).transpose();
/* eliminate the diagonal matrix d using a givens rotation. */
for (j = 0; j < n; ++j) {
/* prepare the row of d to be eliminated, locating the */
/* diagonal element using p from the qr factorization. */
l = iPerm.indices()(j);
if (diag[l] == 0.)
break;
sdiag.tail(n-j).setZero();
sdiag[j] = diag[l];
/* the transformations to eliminate the row of d */
/* modify only a single element of (q transpose)*b */
/* beyond the first n, which is initially zero. */
Scalar qtbpj = 0.;
for (k = j; k < n; ++k) {
/* determine a givens rotation which eliminates the */
/* appropriate element in the current row of d. */
givens.makeGivens(-s(k,k), sdiag[k]);
/* compute the modified diagonal element of r and */
/* the modified element of ((q transpose)*b,0). */
s(k,k) = givens.c() * s(k,k) + givens.s() * sdiag[k];
temp = givens.c() * wa[k] + givens.s() * qtbpj;
qtbpj = -givens.s() * wa[k] + givens.c() * qtbpj;
wa[k] = temp;
/* accumulate the tranformation in the row of s. */
for (i = k+1; i<n; ++i) {
temp = givens.c() * s(i,k) + givens.s() * sdiag[i];
sdiag[i] = -givens.s() * s(i,k) + givens.c() * sdiag[i];
s(i,k) = temp;
}
}
}
/* solve the triangular system for z. if the system is */
/* singular, then obtain a least squares solution. */
Index nsing;
for(nsing=0; nsing<n && sdiag[nsing]!=0; nsing++) {}
wa.tail(n-nsing).setZero();
s.topLeftCorner(nsing, nsing).transpose().template triangularView<Upper>().solveInPlace(wa.head(nsing));
// restore
sdiag = s.diagonal();
s.diagonal() = x;
/* permute the components of z back to components of x. */
x = iPerm * wa;
}
template <typename Scalar, int _Options, typename Index>
void lmqrsolv(
SparseMatrix<Scalar,_Options,Index> &s,
const PermutationMatrix<Dynamic,Dynamic> &iPerm,
const Matrix<Scalar,Dynamic,1> &diag,
const Matrix<Scalar,Dynamic,1> &qtb,
Matrix<Scalar,Dynamic,1> &x,
Matrix<Scalar,Dynamic,1> &sdiag)
{
/* Local variables */
typedef SparseMatrix<Scalar,RowMajor,Index> FactorType;
Index i, j, k, l;
Scalar temp;
Index n = s.cols();
Matrix<Scalar,Dynamic,1> wa(n);
JacobiRotation<Scalar> givens;
/* Function Body */
// the following will only change the lower triangular part of s, including
// the diagonal, though the diagonal is restored afterward
/* copy r and (q transpose)*b to preserve input and initialize R. */
wa = qtb;
FactorType R(s);
// Eliminate the diagonal matrix d using a givens rotation
for (j = 0; j < n; ++j)
{
// Prepare the row of d to be eliminated, locating the
// diagonal element using p from the qr factorization
l = iPerm.indices()(j);
if (diag(l) == Scalar(0))
break;
sdiag.tail(n-j).setZero();
sdiag[j] = diag[l];
// the transformations to eliminate the row of d
// modify only a single element of (q transpose)*b
// beyond the first n, which is initially zero.
Scalar qtbpj = 0;
// Browse the nonzero elements of row j of the upper triangular s
for (k = j; k < n; ++k)
{
typename FactorType::InnerIterator itk(R,k);
for (; itk; ++itk){
if (itk.index() < k) continue;
else break;
}
//At this point, we have the diagonal element R(k,k)
// Determine a givens rotation which eliminates
// the appropriate element in the current row of d
givens.makeGivens(-itk.value(), sdiag(k));
// Compute the modified diagonal element of r and
// the modified element of ((q transpose)*b,0).
itk.valueRef() = givens.c() * itk.value() + givens.s() * sdiag(k);
temp = givens.c() * wa(k) + givens.s() * qtbpj;
qtbpj = -givens.s() * wa(k) + givens.c() * qtbpj;
wa(k) = temp;
// Accumulate the transformation in the remaining k row/column of R
for (++itk; itk; ++itk)
{
i = itk.index();
temp = givens.c() * itk.value() + givens.s() * sdiag(i);
sdiag(i) = -givens.s() * itk.value() + givens.c() * sdiag(i);
itk.valueRef() = temp;
}
}
}
// Solve the triangular system for z. If the system is
// singular, then obtain a least squares solution
Index nsing;
for(nsing = 0; nsing<n && sdiag(nsing) !=0; nsing++) {}
wa.tail(n-nsing).setZero();
// x = wa;
wa.head(nsing) = R.topLeftCorner(nsing,nsing).template triangularView<Upper>().solve/*InPlace*/(wa.head(nsing));
sdiag = R.diagonal();
// Permute the components of z back to components of x
x = iPerm * wa;
}
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_LMQRSOLV_H

View File

@@ -0,0 +1,377 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Thomas Capricelli <orzel@freehackers.org>
// Copyright (C) 2012 Desire Nuentsa <desire.nuentsa_wakam@inria.fr>
//
// The algorithm of this class initially comes from MINPACK whose original authors are:
// Copyright Jorge More - Argonne National Laboratory
// Copyright Burt Garbow - Argonne National Laboratory
// Copyright Ken Hillstrom - Argonne National Laboratory
//
// This Source Code Form is subject to the terms of the Minpack license
// (a BSD-like license) described in the campaigned CopyrightMINPACK.txt file.
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_LEVENBERGMARQUARDT_H
#define EIGEN_LEVENBERGMARQUARDT_H
namespace Eigen {
namespace LevenbergMarquardtSpace {
enum Status {
NotStarted = -2,
Running = -1,
ImproperInputParameters = 0,
RelativeReductionTooSmall = 1,
RelativeErrorTooSmall = 2,
RelativeErrorAndReductionTooSmall = 3,
CosinusTooSmall = 4,
TooManyFunctionEvaluation = 5,
FtolTooSmall = 6,
XtolTooSmall = 7,
GtolTooSmall = 8,
UserAsked = 9
};
}
template <typename _Scalar, int NX=Dynamic, int NY=Dynamic>
struct DenseFunctor
{
typedef _Scalar Scalar;
enum {
InputsAtCompileTime = NX,
ValuesAtCompileTime = NY
};
typedef Matrix<Scalar,InputsAtCompileTime,1> InputType;
typedef Matrix<Scalar,ValuesAtCompileTime,1> ValueType;
typedef Matrix<Scalar,ValuesAtCompileTime,InputsAtCompileTime> JacobianType;
typedef ColPivHouseholderQR<JacobianType> QRSolver;
const int m_inputs, m_values;
DenseFunctor() : m_inputs(InputsAtCompileTime), m_values(ValuesAtCompileTime) {}
DenseFunctor(int inputs, int values) : m_inputs(inputs), m_values(values) {}
int inputs() const { return m_inputs; }
int values() const { return m_values; }
//int operator()(const InputType &x, ValueType& fvec) { }
// should be defined in derived classes
//int df(const InputType &x, JacobianType& fjac) { }
// should be defined in derived classes
};
template <typename _Scalar, typename _Index>
struct SparseFunctor
{
typedef _Scalar Scalar;
typedef _Index Index;
typedef Matrix<Scalar,Dynamic,1> InputType;
typedef Matrix<Scalar,Dynamic,1> ValueType;
typedef SparseMatrix<Scalar, ColMajor, Index> JacobianType;
typedef SparseQR<JacobianType, COLAMDOrdering<int> > QRSolver;
enum {
InputsAtCompileTime = Dynamic,
ValuesAtCompileTime = Dynamic
};
SparseFunctor(int inputs, int values) : m_inputs(inputs), m_values(values) {}
int inputs() const { return m_inputs; }
int values() const { return m_values; }
const int m_inputs, m_values;
//int operator()(const InputType &x, ValueType& fvec) { }
// to be defined in the functor
//int df(const InputType &x, JacobianType& fjac) { }
// to be defined in the functor if no automatic differentiation
};
namespace internal {
template <typename QRSolver, typename VectorType>
void lmpar2(const QRSolver &qr, const VectorType &diag, const VectorType &qtb,
typename VectorType::Scalar m_delta, typename VectorType::Scalar &par,
VectorType &x);
}
/**
* \ingroup NonLinearOptimization_Module
* \brief Performs non linear optimization over a non-linear function,
* using a variant of the Levenberg Marquardt algorithm.
*
* Check wikipedia for more information.
* http://en.wikipedia.org/wiki/Levenberg%E2%80%93Marquardt_algorithm
*/
template<typename _FunctorType>
class LevenbergMarquardt : internal::no_assignment_operator
{
public:
typedef _FunctorType FunctorType;
typedef typename FunctorType::QRSolver QRSolver;
typedef typename FunctorType::JacobianType JacobianType;
typedef typename JacobianType::Scalar Scalar;
typedef typename JacobianType::RealScalar RealScalar;
typedef typename JacobianType::Index Index;
typedef typename QRSolver::Index PermIndex;
typedef Matrix<Scalar,Dynamic,1> FVectorType;
typedef PermutationMatrix<Dynamic,Dynamic> PermutationType;
public:
LevenbergMarquardt(FunctorType& functor)
: m_functor(functor),m_nfev(0),m_njev(0),m_fnorm(0.0),m_gnorm(0),
m_isInitialized(false),m_info(InvalidInput)
{
resetParameters();
m_useExternalScaling=false;
}
LevenbergMarquardtSpace::Status minimize(FVectorType &x);
LevenbergMarquardtSpace::Status minimizeInit(FVectorType &x);
LevenbergMarquardtSpace::Status minimizeOneStep(FVectorType &x);
LevenbergMarquardtSpace::Status lmder1(
FVectorType &x,
const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon())
);
static LevenbergMarquardtSpace::Status lmdif1(
FunctorType &functor,
FVectorType &x,
Index *nfev,
const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon())
);
/** Sets the default parameters */
void resetParameters()
{
m_factor = 100.;
m_maxfev = 400;
m_ftol = std::sqrt(NumTraits<RealScalar>::epsilon());
m_xtol = std::sqrt(NumTraits<RealScalar>::epsilon());
m_gtol = 0. ;
m_epsfcn = 0. ;
}
/** Sets the tolerance for the norm of the solution vector*/
void setXtol(RealScalar xtol) { m_xtol = xtol; }
/** Sets the tolerance for the norm of the vector function*/
void setFtol(RealScalar ftol) { m_ftol = ftol; }
/** Sets the tolerance for the norm of the gradient of the error vector*/
void setGtol(RealScalar gtol) { m_gtol = gtol; }
/** Sets the step bound for the diagonal shift */
void setFactor(RealScalar factor) { m_factor = factor; }
/** Sets the error precision */
void setEpsilon (RealScalar epsfcn) { m_epsfcn = epsfcn; }
/** Sets the maximum number of function evaluation */
void setMaxfev(Index maxfev) {m_maxfev = maxfev; }
/** Use an external Scaling. If set to true, pass a nonzero diagonal to diag() */
void setExternalScaling(bool value) {m_useExternalScaling = value; }
/** \returns a reference to the diagonal of the jacobian */
FVectorType& diag() {return m_diag; }
/** \returns the number of iterations performed */
Index iterations() { return m_iter; }
/** \returns the number of functions evaluation */
Index nfev() { return m_nfev; }
/** \returns the number of jacobian evaluation */
Index njev() { return m_njev; }
/** \returns the norm of current vector function */
RealScalar fnorm() {return m_fnorm; }
/** \returns the norm of the gradient of the error */
RealScalar gnorm() {return m_gnorm; }
/** \returns the LevenbergMarquardt parameter */
RealScalar lm_param(void) { return m_par; }
/** \returns a reference to the current vector function
*/
FVectorType& fvec() {return m_fvec; }
/** \returns a reference to the matrix where the current Jacobian matrix is stored
*/
JacobianType& jacobian() {return m_fjac; }
/** \returns a reference to the triangular matrix R from the QR of the jacobian matrix.
* \sa jacobian()
*/
JacobianType& matrixR() {return m_rfactor; }
/** the permutation used in the QR factorization
*/
PermutationType permutation() {return m_permutation; }
/**
* \brief Reports whether the minimization was successful
* \returns \c Success if the minimization was succesful,
* \c NumericalIssue if a numerical problem arises during the
* minimization process, for exemple during the QR factorization
* \c NoConvergence if the minimization did not converge after
* the maximum number of function evaluation allowed
* \c InvalidInput if the input matrix is invalid
*/
ComputationInfo info() const
{
return m_info;
}
private:
JacobianType m_fjac;
JacobianType m_rfactor; // The triangular matrix R from the QR of the jacobian matrix m_fjac
FunctorType &m_functor;
FVectorType m_fvec, m_qtf, m_diag;
Index n;
Index m;
Index m_nfev;
Index m_njev;
RealScalar m_fnorm; // Norm of the current vector function
RealScalar m_gnorm; //Norm of the gradient of the error
RealScalar m_factor; //
Index m_maxfev; // Maximum number of function evaluation
RealScalar m_ftol; //Tolerance in the norm of the vector function
RealScalar m_xtol; //
RealScalar m_gtol; //tolerance of the norm of the error gradient
RealScalar m_epsfcn; //
Index m_iter; // Number of iterations performed
RealScalar m_delta;
bool m_useExternalScaling;
PermutationType m_permutation;
FVectorType m_wa1, m_wa2, m_wa3, m_wa4; //Temporary vectors
RealScalar m_par;
bool m_isInitialized; // Check whether the minimization step has been called
ComputationInfo m_info;
};
template<typename FunctorType>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType>::minimize(FVectorType &x)
{
LevenbergMarquardtSpace::Status status = minimizeInit(x);
if (status==LevenbergMarquardtSpace::ImproperInputParameters) {
m_isInitialized = true;
return status;
}
do {
// std::cout << " uv " << x.transpose() << "\n";
status = minimizeOneStep(x);
} while (status==LevenbergMarquardtSpace::Running);
m_isInitialized = true;
return status;
}
template<typename FunctorType>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType>::minimizeInit(FVectorType &x)
{
n = x.size();
m = m_functor.values();
m_wa1.resize(n); m_wa2.resize(n); m_wa3.resize(n);
m_wa4.resize(m);
m_fvec.resize(m);
//FIXME Sparse Case : Allocate space for the jacobian
m_fjac.resize(m, n);
// m_fjac.reserve(VectorXi::Constant(n,5)); // FIXME Find a better alternative
if (!m_useExternalScaling)
m_diag.resize(n);
eigen_assert( (!m_useExternalScaling || m_diag.size()==n) || "When m_useExternalScaling is set, the caller must provide a valid 'm_diag'");
m_qtf.resize(n);
/* Function Body */
m_nfev = 0;
m_njev = 0;
/* check the input parameters for errors. */
if (n <= 0 || m < n || m_ftol < 0. || m_xtol < 0. || m_gtol < 0. || m_maxfev <= 0 || m_factor <= 0.){
m_info = InvalidInput;
return LevenbergMarquardtSpace::ImproperInputParameters;
}
if (m_useExternalScaling)
for (Index j = 0; j < n; ++j)
if (m_diag[j] <= 0.)
{
m_info = InvalidInput;
return LevenbergMarquardtSpace::ImproperInputParameters;
}
/* evaluate the function at the starting point */
/* and calculate its norm. */
m_nfev = 1;
if ( m_functor(x, m_fvec) < 0)
return LevenbergMarquardtSpace::UserAsked;
m_fnorm = m_fvec.stableNorm();
/* initialize levenberg-marquardt parameter and iteration counter. */
m_par = 0.;
m_iter = 1;
return LevenbergMarquardtSpace::NotStarted;
}
template<typename FunctorType>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType>::lmder1(
FVectorType &x,
const Scalar tol
)
{
n = x.size();
m = m_functor.values();
/* check the input parameters for errors. */
if (n <= 0 || m < n || tol < 0.)
return LevenbergMarquardtSpace::ImproperInputParameters;
resetParameters();
m_ftol = tol;
m_xtol = tol;
m_maxfev = 100*(n+1);
return minimize(x);
}
template<typename FunctorType>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType>::lmdif1(
FunctorType &functor,
FVectorType &x,
Index *nfev,
const Scalar tol
)
{
Index n = x.size();
Index m = functor.values();
/* check the input parameters for errors. */
if (n <= 0 || m < n || tol < 0.)
return LevenbergMarquardtSpace::ImproperInputParameters;
NumericalDiff<FunctorType> numDiff(functor);
// embedded LevenbergMarquardt
LevenbergMarquardt<NumericalDiff<FunctorType> > lm(numDiff);
lm.setFtol(tol);
lm.setXtol(tol);
lm.setMaxfev(200*(n+1));
LevenbergMarquardtSpace::Status info = LevenbergMarquardtSpace::Status(lm.minimize(x));
if (nfev)
* nfev = lm.nfev();
return info;
}
} // end namespace Eigen
#endif // EIGEN_LEVENBERGMARQUARDT_H

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_MatrixFunctions_SRCS "*.h")
INSTALL(FILES
${Eigen_MatrixFunctions_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/MatrixFunctions COMPONENT Devel
)

View File

@@ -0,0 +1,451 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009, 2010 Jitse Niesen <jitse@maths.leeds.ac.uk>
// Copyright (C) 2011 Chen-Pang He <jdh8@ms63.hinet.net>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_MATRIX_EXPONENTIAL
#define EIGEN_MATRIX_EXPONENTIAL
#include "StemFunction.h"
namespace Eigen {
/** \ingroup MatrixFunctions_Module
* \brief Class for computing the matrix exponential.
* \tparam MatrixType type of the argument of the exponential,
* expected to be an instantiation of the Matrix class template.
*/
template <typename MatrixType>
class MatrixExponential {
public:
/** \brief Constructor.
*
* The class stores a reference to \p M, so it should not be
* changed (or destroyed) before compute() is called.
*
* \param[in] M matrix whose exponential is to be computed.
*/
MatrixExponential(const MatrixType &M);
/** \brief Computes the matrix exponential.
*
* \param[out] result the matrix exponential of \p M in the constructor.
*/
template <typename ResultType>
void compute(ResultType &result);
private:
// Prevent copying
MatrixExponential(const MatrixExponential&);
MatrixExponential& operator=(const MatrixExponential&);
/** \brief Compute the (3,3)-Pad&eacute; approximant to the exponential.
*
* After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Pad&eacute;
* approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$.
*
* \param[in] A Argument of matrix exponential
*/
void pade3(const MatrixType &A);
/** \brief Compute the (5,5)-Pad&eacute; approximant to the exponential.
*
* After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Pad&eacute;
* approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$.
*
* \param[in] A Argument of matrix exponential
*/
void pade5(const MatrixType &A);
/** \brief Compute the (7,7)-Pad&eacute; approximant to the exponential.
*
* After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Pad&eacute;
* approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$.
*
* \param[in] A Argument of matrix exponential
*/
void pade7(const MatrixType &A);
/** \brief Compute the (9,9)-Pad&eacute; approximant to the exponential.
*
* After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Pad&eacute;
* approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$.
*
* \param[in] A Argument of matrix exponential
*/
void pade9(const MatrixType &A);
/** \brief Compute the (13,13)-Pad&eacute; approximant to the exponential.
*
* After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Pad&eacute;
* approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$.
*
* \param[in] A Argument of matrix exponential
*/
void pade13(const MatrixType &A);
/** \brief Compute the (17,17)-Pad&eacute; approximant to the exponential.
*
* After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Pad&eacute;
* approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$.
*
* This function activates only if your long double is double-double or quadruple.
*
* \param[in] A Argument of matrix exponential
*/
void pade17(const MatrixType &A);
/** \brief Compute Pad&eacute; approximant to the exponential.
*
* Computes \c m_U, \c m_V and \c m_squarings such that
* \f$ (V+U)(V-U)^{-1} \f$ is a Pad&eacute; of
* \f$ \exp(2^{-\mbox{squarings}}M) \f$ around \f$ M = 0 \f$. The
* degree of the Pad&eacute; approximant and the value of
* squarings are chosen such that the approximation error is no
* more than the round-off error.
*
* The argument of this function should correspond with the (real
* part of) the entries of \c m_M. It is used to select the
* correct implementation using overloading.
*/
void computeUV(double);
/** \brief Compute Pad&eacute; approximant to the exponential.
*
* \sa computeUV(double);
*/
void computeUV(float);
/** \brief Compute Pad&eacute; approximant to the exponential.
*
* \sa computeUV(double);
*/
void computeUV(long double);
typedef typename internal::traits<MatrixType>::Scalar Scalar;
typedef typename NumTraits<Scalar>::Real RealScalar;
typedef typename std::complex<RealScalar> ComplexScalar;
/** \brief Reference to matrix whose exponential is to be computed. */
typename internal::nested<MatrixType>::type m_M;
/** \brief Odd-degree terms in numerator of Pad&eacute; approximant. */
MatrixType m_U;
/** \brief Even-degree terms in numerator of Pad&eacute; approximant. */
MatrixType m_V;
/** \brief Used for temporary storage. */
MatrixType m_tmp1;
/** \brief Used for temporary storage. */
MatrixType m_tmp2;
/** \brief Identity matrix of the same size as \c m_M. */
MatrixType m_Id;
/** \brief Number of squarings required in the last step. */
int m_squarings;
/** \brief L1 norm of m_M. */
RealScalar m_l1norm;
};
template <typename MatrixType>
MatrixExponential<MatrixType>::MatrixExponential(const MatrixType &M) :
m_M(M),
m_U(M.rows(),M.cols()),
m_V(M.rows(),M.cols()),
m_tmp1(M.rows(),M.cols()),
m_tmp2(M.rows(),M.cols()),
m_Id(MatrixType::Identity(M.rows(), M.cols())),
m_squarings(0),
m_l1norm(M.cwiseAbs().colwise().sum().maxCoeff())
{
/* empty body */
}
template <typename MatrixType>
template <typename ResultType>
void MatrixExponential<MatrixType>::compute(ResultType &result)
{
#if LDBL_MANT_DIG > 112 // rarely happens
if(sizeof(RealScalar) > 14) {
result = m_M.matrixFunction(StdStemFunctions<ComplexScalar>::exp);
return;
}
#endif
computeUV(RealScalar());
m_tmp1 = m_U + m_V; // numerator of Pade approximant
m_tmp2 = -m_U + m_V; // denominator of Pade approximant
result = m_tmp2.partialPivLu().solve(m_tmp1);
for (int i=0; i<m_squarings; i++)
result *= result; // undo scaling by repeated squaring
}
template <typename MatrixType>
EIGEN_STRONG_INLINE void MatrixExponential<MatrixType>::pade3(const MatrixType &A)
{
const RealScalar b[] = {120., 60., 12., 1.};
m_tmp1.noalias() = A * A;
m_tmp2 = b[3]*m_tmp1 + b[1]*m_Id;
m_U.noalias() = A * m_tmp2;
m_V = b[2]*m_tmp1 + b[0]*m_Id;
}
template <typename MatrixType>
EIGEN_STRONG_INLINE void MatrixExponential<MatrixType>::pade5(const MatrixType &A)
{
const RealScalar b[] = {30240., 15120., 3360., 420., 30., 1.};
MatrixType A2 = A * A;
m_tmp1.noalias() = A2 * A2;
m_tmp2 = b[5]*m_tmp1 + b[3]*A2 + b[1]*m_Id;
m_U.noalias() = A * m_tmp2;
m_V = b[4]*m_tmp1 + b[2]*A2 + b[0]*m_Id;
}
template <typename MatrixType>
EIGEN_STRONG_INLINE void MatrixExponential<MatrixType>::pade7(const MatrixType &A)
{
const RealScalar b[] = {17297280., 8648640., 1995840., 277200., 25200., 1512., 56., 1.};
MatrixType A2 = A * A;
MatrixType A4 = A2 * A2;
m_tmp1.noalias() = A4 * A2;
m_tmp2 = b[7]*m_tmp1 + b[5]*A4 + b[3]*A2 + b[1]*m_Id;
m_U.noalias() = A * m_tmp2;
m_V = b[6]*m_tmp1 + b[4]*A4 + b[2]*A2 + b[0]*m_Id;
}
template <typename MatrixType>
EIGEN_STRONG_INLINE void MatrixExponential<MatrixType>::pade9(const MatrixType &A)
{
const RealScalar b[] = {17643225600., 8821612800., 2075673600., 302702400., 30270240.,
2162160., 110880., 3960., 90., 1.};
MatrixType A2 = A * A;
MatrixType A4 = A2 * A2;
MatrixType A6 = A4 * A2;
m_tmp1.noalias() = A6 * A2;
m_tmp2 = b[9]*m_tmp1 + b[7]*A6 + b[5]*A4 + b[3]*A2 + b[1]*m_Id;
m_U.noalias() = A * m_tmp2;
m_V = b[8]*m_tmp1 + b[6]*A6 + b[4]*A4 + b[2]*A2 + b[0]*m_Id;
}
template <typename MatrixType>
EIGEN_STRONG_INLINE void MatrixExponential<MatrixType>::pade13(const MatrixType &A)
{
const RealScalar b[] = {64764752532480000., 32382376266240000., 7771770303897600.,
1187353796428800., 129060195264000., 10559470521600., 670442572800.,
33522128640., 1323241920., 40840800., 960960., 16380., 182., 1.};
MatrixType A2 = A * A;
MatrixType A4 = A2 * A2;
m_tmp1.noalias() = A4 * A2;
m_V = b[13]*m_tmp1 + b[11]*A4 + b[9]*A2; // used for temporary storage
m_tmp2.noalias() = m_tmp1 * m_V;
m_tmp2 += b[7]*m_tmp1 + b[5]*A4 + b[3]*A2 + b[1]*m_Id;
m_U.noalias() = A * m_tmp2;
m_tmp2 = b[12]*m_tmp1 + b[10]*A4 + b[8]*A2;
m_V.noalias() = m_tmp1 * m_tmp2;
m_V += b[6]*m_tmp1 + b[4]*A4 + b[2]*A2 + b[0]*m_Id;
}
#if LDBL_MANT_DIG > 64
template <typename MatrixType>
EIGEN_STRONG_INLINE void MatrixExponential<MatrixType>::pade17(const MatrixType &A)
{
const RealScalar b[] = {830034394580628357120000.L, 415017197290314178560000.L,
100610229646136770560000.L, 15720348382208870400000.L,
1774878043152614400000.L, 153822763739893248000.L, 10608466464820224000.L,
595373117923584000.L, 27563570274240000.L, 1060137318240000.L,
33924394183680.L, 899510451840.L, 19554575040.L, 341863200.L, 4651200.L,
46512.L, 306.L, 1.L};
MatrixType A2 = A * A;
MatrixType A4 = A2 * A2;
MatrixType A6 = A4 * A2;
m_tmp1.noalias() = A4 * A4;
m_V = b[17]*m_tmp1 + b[15]*A6 + b[13]*A4 + b[11]*A2; // used for temporary storage
m_tmp2.noalias() = m_tmp1 * m_V;
m_tmp2 += b[9]*m_tmp1 + b[7]*A6 + b[5]*A4 + b[3]*A2 + b[1]*m_Id;
m_U.noalias() = A * m_tmp2;
m_tmp2 = b[16]*m_tmp1 + b[14]*A6 + b[12]*A4 + b[10]*A2;
m_V.noalias() = m_tmp1 * m_tmp2;
m_V += b[8]*m_tmp1 + b[6]*A6 + b[4]*A4 + b[2]*A2 + b[0]*m_Id;
}
#endif
template <typename MatrixType>
void MatrixExponential<MatrixType>::computeUV(float)
{
using std::frexp;
using std::pow;
if (m_l1norm < 4.258730016922831e-001) {
pade3(m_M);
} else if (m_l1norm < 1.880152677804762e+000) {
pade5(m_M);
} else {
const float maxnorm = 3.925724783138660f;
frexp(m_l1norm / maxnorm, &m_squarings);
if (m_squarings < 0) m_squarings = 0;
MatrixType A = m_M / Scalar(pow(2, m_squarings));
pade7(A);
}
}
template <typename MatrixType>
void MatrixExponential<MatrixType>::computeUV(double)
{
using std::frexp;
using std::pow;
if (m_l1norm < 1.495585217958292e-002) {
pade3(m_M);
} else if (m_l1norm < 2.539398330063230e-001) {
pade5(m_M);
} else if (m_l1norm < 9.504178996162932e-001) {
pade7(m_M);
} else if (m_l1norm < 2.097847961257068e+000) {
pade9(m_M);
} else {
const double maxnorm = 5.371920351148152;
frexp(m_l1norm / maxnorm, &m_squarings);
if (m_squarings < 0) m_squarings = 0;
MatrixType A = m_M / Scalar(pow(2, m_squarings));
pade13(A);
}
}
template <typename MatrixType>
void MatrixExponential<MatrixType>::computeUV(long double)
{
using std::frexp;
using std::pow;
#if LDBL_MANT_DIG == 53 // double precision
computeUV(double());
#elif LDBL_MANT_DIG <= 64 // extended precision
if (m_l1norm < 4.1968497232266989671e-003L) {
pade3(m_M);
} else if (m_l1norm < 1.1848116734693823091e-001L) {
pade5(m_M);
} else if (m_l1norm < 5.5170388480686700274e-001L) {
pade7(m_M);
} else if (m_l1norm < 1.3759868875587845383e+000L) {
pade9(m_M);
} else {
const long double maxnorm = 4.0246098906697353063L;
frexp(m_l1norm / maxnorm, &m_squarings);
if (m_squarings < 0) m_squarings = 0;
MatrixType A = m_M / Scalar(pow(2, m_squarings));
pade13(A);
}
#elif LDBL_MANT_DIG <= 106 // double-double
if (m_l1norm < 3.2787892205607026992947488108213e-005L) {
pade3(m_M);
} else if (m_l1norm < 6.4467025060072760084130906076332e-003L) {
pade5(m_M);
} else if (m_l1norm < 6.8988028496595374751374122881143e-002L) {
pade7(m_M);
} else if (m_l1norm < 2.7339737518502231741495857201670e-001L) {
pade9(m_M);
} else if (m_l1norm < 1.3203382096514474905666448850278e+000L) {
pade13(m_M);
} else {
const long double maxnorm = 3.2579440895405400856599663723517L;
frexp(m_l1norm / maxnorm, &m_squarings);
if (m_squarings < 0) m_squarings = 0;
MatrixType A = m_M / pow(Scalar(2), m_squarings);
pade17(A);
}
#elif LDBL_MANT_DIG <= 112 // quadruple precison
if (m_l1norm < 1.639394610288918690547467954466970e-005L) {
pade3(m_M);
} else if (m_l1norm < 4.253237712165275566025884344433009e-003L) {
pade5(m_M);
} else if (m_l1norm < 5.125804063165764409885122032933142e-002L) {
pade7(m_M);
} else if (m_l1norm < 2.170000765161155195453205651889853e-001L) {
pade9(m_M);
} else if (m_l1norm < 1.125358383453143065081397882891878e+000L) {
pade13(m_M);
} else {
const long double maxnorm = 2.884233277829519311757165057717815L;
frexp(m_l1norm / maxnorm, &m_squarings);
if (m_squarings < 0) m_squarings = 0;
MatrixType A = m_M / Scalar(pow(2, m_squarings));
pade17(A);
}
#else
// this case should be handled in compute()
eigen_assert(false && "Bug in MatrixExponential");
#endif // LDBL_MANT_DIG
}
/** \ingroup MatrixFunctions_Module
*
* \brief Proxy for the matrix exponential of some matrix (expression).
*
* \tparam Derived Type of the argument to the matrix exponential.
*
* This class holds the argument to the matrix exponential until it
* is assigned or evaluated for some other reason (so the argument
* should not be changed in the meantime). It is the return type of
* MatrixBase::exp() and most of the time this is the only way it is
* used.
*/
template<typename Derived> struct MatrixExponentialReturnValue
: public ReturnByValue<MatrixExponentialReturnValue<Derived> >
{
typedef typename Derived::Index Index;
public:
/** \brief Constructor.
*
* \param[in] src %Matrix (expression) forming the argument of the
* matrix exponential.
*/
MatrixExponentialReturnValue(const Derived& src) : m_src(src) { }
/** \brief Compute the matrix exponential.
*
* \param[out] result the matrix exponential of \p src in the
* constructor.
*/
template <typename ResultType>
inline void evalTo(ResultType& result) const
{
const typename Derived::PlainObject srcEvaluated = m_src.eval();
MatrixExponential<typename Derived::PlainObject> me(srcEvaluated);
me.compute(result);
}
Index rows() const { return m_src.rows(); }
Index cols() const { return m_src.cols(); }
protected:
const Derived& m_src;
private:
MatrixExponentialReturnValue& operator=(const MatrixExponentialReturnValue&);
};
namespace internal {
template<typename Derived>
struct traits<MatrixExponentialReturnValue<Derived> >
{
typedef typename Derived::PlainObject ReturnType;
};
}
template <typename Derived>
const MatrixExponentialReturnValue<Derived> MatrixBase<Derived>::exp() const
{
eigen_assert(rows() == cols());
return MatrixExponentialReturnValue<Derived>(derived());
}
} // end namespace Eigen
#endif // EIGEN_MATRIX_EXPONENTIAL

View File

@@ -0,0 +1,591 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009-2011 Jitse Niesen <jitse@maths.leeds.ac.uk>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_MATRIX_FUNCTION
#define EIGEN_MATRIX_FUNCTION
#include "StemFunction.h"
#include "MatrixFunctionAtomic.h"
namespace Eigen {
/** \ingroup MatrixFunctions_Module
* \brief Class for computing matrix functions.
* \tparam MatrixType type of the argument of the matrix function,
* expected to be an instantiation of the Matrix class template.
* \tparam AtomicType type for computing matrix function of atomic blocks.
* \tparam IsComplex used internally to select correct specialization.
*
* This class implements the Schur-Parlett algorithm for computing matrix functions. The spectrum of the
* matrix is divided in clustered of eigenvalues that lies close together. This class delegates the
* computation of the matrix function on every block corresponding to these clusters to an object of type
* \p AtomicType and uses these results to compute the matrix function of the whole matrix. The class
* \p AtomicType should have a \p compute() member function for computing the matrix function of a block.
*
* \sa class MatrixFunctionAtomic, class MatrixLogarithmAtomic
*/
template <typename MatrixType,
typename AtomicType,
int IsComplex = NumTraits<typename internal::traits<MatrixType>::Scalar>::IsComplex>
class MatrixFunction
{
public:
/** \brief Constructor.
*
* \param[in] A argument of matrix function, should be a square matrix.
* \param[in] atomic class for computing matrix function of atomic blocks.
*
* The class stores references to \p A and \p atomic, so they should not be
* changed (or destroyed) before compute() is called.
*/
MatrixFunction(const MatrixType& A, AtomicType& atomic);
/** \brief Compute the matrix function.
*
* \param[out] result the function \p f applied to \p A, as
* specified in the constructor.
*
* See MatrixBase::matrixFunction() for details on how this computation
* is implemented.
*/
template <typename ResultType>
void compute(ResultType &result);
};
/** \internal \ingroup MatrixFunctions_Module
* \brief Partial specialization of MatrixFunction for real matrices
*/
template <typename MatrixType, typename AtomicType>
class MatrixFunction<MatrixType, AtomicType, 0>
{
private:
typedef internal::traits<MatrixType> Traits;
typedef typename Traits::Scalar Scalar;
static const int Rows = Traits::RowsAtCompileTime;
static const int Cols = Traits::ColsAtCompileTime;
static const int Options = MatrixType::Options;
static const int MaxRows = Traits::MaxRowsAtCompileTime;
static const int MaxCols = Traits::MaxColsAtCompileTime;
typedef std::complex<Scalar> ComplexScalar;
typedef Matrix<ComplexScalar, Rows, Cols, Options, MaxRows, MaxCols> ComplexMatrix;
public:
/** \brief Constructor.
*
* \param[in] A argument of matrix function, should be a square matrix.
* \param[in] atomic class for computing matrix function of atomic blocks.
*/
MatrixFunction(const MatrixType& A, AtomicType& atomic) : m_A(A), m_atomic(atomic) { }
/** \brief Compute the matrix function.
*
* \param[out] result the function \p f applied to \p A, as
* specified in the constructor.
*
* This function converts the real matrix \c A to a complex matrix,
* uses MatrixFunction<MatrixType,1> and then converts the result back to
* a real matrix.
*/
template <typename ResultType>
void compute(ResultType& result)
{
ComplexMatrix CA = m_A.template cast<ComplexScalar>();
ComplexMatrix Cresult;
MatrixFunction<ComplexMatrix, AtomicType> mf(CA, m_atomic);
mf.compute(Cresult);
result = Cresult.real();
}
private:
typename internal::nested<MatrixType>::type m_A; /**< \brief Reference to argument of matrix function. */
AtomicType& m_atomic; /**< \brief Class for computing matrix function of atomic blocks. */
MatrixFunction& operator=(const MatrixFunction&);
};
/** \internal \ingroup MatrixFunctions_Module
* \brief Partial specialization of MatrixFunction for complex matrices
*/
template <typename MatrixType, typename AtomicType>
class MatrixFunction<MatrixType, AtomicType, 1>
{
private:
typedef internal::traits<MatrixType> Traits;
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::Index Index;
static const int RowsAtCompileTime = Traits::RowsAtCompileTime;
static const int ColsAtCompileTime = Traits::ColsAtCompileTime;
static const int Options = MatrixType::Options;
typedef typename NumTraits<Scalar>::Real RealScalar;
typedef Matrix<Scalar, Traits::RowsAtCompileTime, 1> VectorType;
typedef Matrix<Index, Traits::RowsAtCompileTime, 1> IntVectorType;
typedef Matrix<Index, Dynamic, 1> DynamicIntVectorType;
typedef std::list<Scalar> Cluster;
typedef std::list<Cluster> ListOfClusters;
typedef Matrix<Scalar, Dynamic, Dynamic, Options, RowsAtCompileTime, ColsAtCompileTime> DynMatrixType;
public:
MatrixFunction(const MatrixType& A, AtomicType& atomic);
template <typename ResultType> void compute(ResultType& result);
private:
void computeSchurDecomposition();
void partitionEigenvalues();
typename ListOfClusters::iterator findCluster(Scalar key);
void computeClusterSize();
void computeBlockStart();
void constructPermutation();
void permuteSchur();
void swapEntriesInSchur(Index index);
void computeBlockAtomic();
Block<MatrixType> block(MatrixType& A, Index i, Index j);
void computeOffDiagonal();
DynMatrixType solveTriangularSylvester(const DynMatrixType& A, const DynMatrixType& B, const DynMatrixType& C);
typename internal::nested<MatrixType>::type m_A; /**< \brief Reference to argument of matrix function. */
AtomicType& m_atomic; /**< \brief Class for computing matrix function of atomic blocks. */
MatrixType m_T; /**< \brief Triangular part of Schur decomposition */
MatrixType m_U; /**< \brief Unitary part of Schur decomposition */
MatrixType m_fT; /**< \brief %Matrix function applied to #m_T */
ListOfClusters m_clusters; /**< \brief Partition of eigenvalues into clusters of ei'vals "close" to each other */
DynamicIntVectorType m_eivalToCluster; /**< \brief m_eivalToCluster[i] = j means i-th ei'val is in j-th cluster */
DynamicIntVectorType m_clusterSize; /**< \brief Number of eigenvalues in each clusters */
DynamicIntVectorType m_blockStart; /**< \brief Row index at which block corresponding to i-th cluster starts */
IntVectorType m_permutation; /**< \brief Permutation which groups ei'vals in the same cluster together */
/** \brief Maximum distance allowed between eigenvalues to be considered "close".
*
* This is morally a \c static \c const \c Scalar, but only
* integers can be static constant class members in C++. The
* separation constant is set to 0.1, a value taken from the
* paper by Davies and Higham. */
static const RealScalar separation() { return static_cast<RealScalar>(0.1); }
MatrixFunction& operator=(const MatrixFunction&);
};
/** \brief Constructor.
*
* \param[in] A argument of matrix function, should be a square matrix.
* \param[in] atomic class for computing matrix function of atomic blocks.
*/
template <typename MatrixType, typename AtomicType>
MatrixFunction<MatrixType,AtomicType,1>::MatrixFunction(const MatrixType& A, AtomicType& atomic)
: m_A(A), m_atomic(atomic)
{
/* empty body */
}
/** \brief Compute the matrix function.
*
* \param[out] result the function \p f applied to \p A, as
* specified in the constructor.
*/
template <typename MatrixType, typename AtomicType>
template <typename ResultType>
void MatrixFunction<MatrixType,AtomicType,1>::compute(ResultType& result)
{
computeSchurDecomposition();
partitionEigenvalues();
computeClusterSize();
computeBlockStart();
constructPermutation();
permuteSchur();
computeBlockAtomic();
computeOffDiagonal();
result = m_U * (m_fT.template triangularView<Upper>() * m_U.adjoint());
}
/** \brief Store the Schur decomposition of #m_A in #m_T and #m_U */
template <typename MatrixType, typename AtomicType>
void MatrixFunction<MatrixType,AtomicType,1>::computeSchurDecomposition()
{
const ComplexSchur<MatrixType> schurOfA(m_A);
m_T = schurOfA.matrixT();
m_U = schurOfA.matrixU();
}
/** \brief Partition eigenvalues in clusters of ei'vals close to each other
*
* This function computes #m_clusters. This is a partition of the
* eigenvalues of #m_T in clusters, such that
* # Any eigenvalue in a certain cluster is at most separation() away
* from another eigenvalue in the same cluster.
* # The distance between two eigenvalues in different clusters is
* more than separation().
* The implementation follows Algorithm 4.1 in the paper of Davies
* and Higham.
*/
template <typename MatrixType, typename AtomicType>
void MatrixFunction<MatrixType,AtomicType,1>::partitionEigenvalues()
{
using std::abs;
const Index rows = m_T.rows();
VectorType diag = m_T.diagonal(); // contains eigenvalues of A
for (Index i=0; i<rows; ++i) {
// Find set containing diag(i), adding a new set if necessary
typename ListOfClusters::iterator qi = findCluster(diag(i));
if (qi == m_clusters.end()) {
Cluster l;
l.push_back(diag(i));
m_clusters.push_back(l);
qi = m_clusters.end();
--qi;
}
// Look for other element to add to the set
for (Index j=i+1; j<rows; ++j) {
if (abs(diag(j) - diag(i)) <= separation() && std::find(qi->begin(), qi->end(), diag(j)) == qi->end()) {
typename ListOfClusters::iterator qj = findCluster(diag(j));
if (qj == m_clusters.end()) {
qi->push_back(diag(j));
} else {
qi->insert(qi->end(), qj->begin(), qj->end());
m_clusters.erase(qj);
}
}
}
}
}
/** \brief Find cluster in #m_clusters containing some value
* \param[in] key Value to find
* \returns Iterator to cluster containing \c key, or
* \c m_clusters.end() if no cluster in m_clusters contains \c key.
*/
template <typename MatrixType, typename AtomicType>
typename MatrixFunction<MatrixType,AtomicType,1>::ListOfClusters::iterator MatrixFunction<MatrixType,AtomicType,1>::findCluster(Scalar key)
{
typename Cluster::iterator j;
for (typename ListOfClusters::iterator i = m_clusters.begin(); i != m_clusters.end(); ++i) {
j = std::find(i->begin(), i->end(), key);
if (j != i->end())
return i;
}
return m_clusters.end();
}
/** \brief Compute #m_clusterSize and #m_eivalToCluster using #m_clusters */
template <typename MatrixType, typename AtomicType>
void MatrixFunction<MatrixType,AtomicType,1>::computeClusterSize()
{
const Index rows = m_T.rows();
VectorType diag = m_T.diagonal();
const Index numClusters = static_cast<Index>(m_clusters.size());
m_clusterSize.setZero(numClusters);
m_eivalToCluster.resize(rows);
Index clusterIndex = 0;
for (typename ListOfClusters::const_iterator cluster = m_clusters.begin(); cluster != m_clusters.end(); ++cluster) {
for (Index i = 0; i < diag.rows(); ++i) {
if (std::find(cluster->begin(), cluster->end(), diag(i)) != cluster->end()) {
++m_clusterSize[clusterIndex];
m_eivalToCluster[i] = clusterIndex;
}
}
++clusterIndex;
}
}
/** \brief Compute #m_blockStart using #m_clusterSize */
template <typename MatrixType, typename AtomicType>
void MatrixFunction<MatrixType,AtomicType,1>::computeBlockStart()
{
m_blockStart.resize(m_clusterSize.rows());
m_blockStart(0) = 0;
for (Index i = 1; i < m_clusterSize.rows(); i++) {
m_blockStart(i) = m_blockStart(i-1) + m_clusterSize(i-1);
}
}
/** \brief Compute #m_permutation using #m_eivalToCluster and #m_blockStart */
template <typename MatrixType, typename AtomicType>
void MatrixFunction<MatrixType,AtomicType,1>::constructPermutation()
{
DynamicIntVectorType indexNextEntry = m_blockStart;
m_permutation.resize(m_T.rows());
for (Index i = 0; i < m_T.rows(); i++) {
Index cluster = m_eivalToCluster[i];
m_permutation[i] = indexNextEntry[cluster];
++indexNextEntry[cluster];
}
}
/** \brief Permute Schur decomposition in #m_U and #m_T according to #m_permutation */
template <typename MatrixType, typename AtomicType>
void MatrixFunction<MatrixType,AtomicType,1>::permuteSchur()
{
IntVectorType p = m_permutation;
for (Index i = 0; i < p.rows() - 1; i++) {
Index j;
for (j = i; j < p.rows(); j++) {
if (p(j) == i) break;
}
eigen_assert(p(j) == i);
for (Index k = j-1; k >= i; k--) {
swapEntriesInSchur(k);
std::swap(p.coeffRef(k), p.coeffRef(k+1));
}
}
}
/** \brief Swap rows \a index and \a index+1 in Schur decomposition in #m_U and #m_T */
template <typename MatrixType, typename AtomicType>
void MatrixFunction<MatrixType,AtomicType,1>::swapEntriesInSchur(Index index)
{
JacobiRotation<Scalar> rotation;
rotation.makeGivens(m_T(index, index+1), m_T(index+1, index+1) - m_T(index, index));
m_T.applyOnTheLeft(index, index+1, rotation.adjoint());
m_T.applyOnTheRight(index, index+1, rotation);
m_U.applyOnTheRight(index, index+1, rotation);
}
/** \brief Compute block diagonal part of #m_fT.
*
* This routine computes the matrix function applied to the block diagonal part of #m_T, with the blocking
* given by #m_blockStart. The matrix function of each diagonal block is computed by #m_atomic. The
* off-diagonal parts of #m_fT are set to zero.
*/
template <typename MatrixType, typename AtomicType>
void MatrixFunction<MatrixType,AtomicType,1>::computeBlockAtomic()
{
m_fT.resize(m_T.rows(), m_T.cols());
m_fT.setZero();
for (Index i = 0; i < m_clusterSize.rows(); ++i) {
block(m_fT, i, i) = m_atomic.compute(block(m_T, i, i));
}
}
/** \brief Return block of matrix according to blocking given by #m_blockStart */
template <typename MatrixType, typename AtomicType>
Block<MatrixType> MatrixFunction<MatrixType,AtomicType,1>::block(MatrixType& A, Index i, Index j)
{
return A.block(m_blockStart(i), m_blockStart(j), m_clusterSize(i), m_clusterSize(j));
}
/** \brief Compute part of #m_fT above block diagonal.
*
* This routine assumes that the block diagonal part of #m_fT (which
* equals the matrix function applied to #m_T) has already been computed and computes
* the part above the block diagonal. The part below the diagonal is
* zero, because #m_T is upper triangular.
*/
template <typename MatrixType, typename AtomicType>
void MatrixFunction<MatrixType,AtomicType,1>::computeOffDiagonal()
{
for (Index diagIndex = 1; diagIndex < m_clusterSize.rows(); diagIndex++) {
for (Index blockIndex = 0; blockIndex < m_clusterSize.rows() - diagIndex; blockIndex++) {
// compute (blockIndex, blockIndex+diagIndex) block
DynMatrixType A = block(m_T, blockIndex, blockIndex);
DynMatrixType B = -block(m_T, blockIndex+diagIndex, blockIndex+diagIndex);
DynMatrixType C = block(m_fT, blockIndex, blockIndex) * block(m_T, blockIndex, blockIndex+diagIndex);
C -= block(m_T, blockIndex, blockIndex+diagIndex) * block(m_fT, blockIndex+diagIndex, blockIndex+diagIndex);
for (Index k = blockIndex + 1; k < blockIndex + diagIndex; k++) {
C += block(m_fT, blockIndex, k) * block(m_T, k, blockIndex+diagIndex);
C -= block(m_T, blockIndex, k) * block(m_fT, k, blockIndex+diagIndex);
}
block(m_fT, blockIndex, blockIndex+diagIndex) = solveTriangularSylvester(A, B, C);
}
}
}
/** \brief Solve a triangular Sylvester equation AX + XB = C
*
* \param[in] A the matrix A; should be square and upper triangular
* \param[in] B the matrix B; should be square and upper triangular
* \param[in] C the matrix C; should have correct size.
*
* \returns the solution X.
*
* If A is m-by-m and B is n-by-n, then both C and X are m-by-n.
* The (i,j)-th component of the Sylvester equation is
* \f[
* \sum_{k=i}^m A_{ik} X_{kj} + \sum_{k=1}^j X_{ik} B_{kj} = C_{ij}.
* \f]
* This can be re-arranged to yield:
* \f[
* X_{ij} = \frac{1}{A_{ii} + B_{jj}} \Bigl( C_{ij}
* - \sum_{k=i+1}^m A_{ik} X_{kj} - \sum_{k=1}^{j-1} X_{ik} B_{kj} \Bigr).
* \f]
* It is assumed that A and B are such that the numerator is never
* zero (otherwise the Sylvester equation does not have a unique
* solution). In that case, these equations can be evaluated in the
* order \f$ i=m,\ldots,1 \f$ and \f$ j=1,\ldots,n \f$.
*/
template <typename MatrixType, typename AtomicType>
typename MatrixFunction<MatrixType,AtomicType,1>::DynMatrixType MatrixFunction<MatrixType,AtomicType,1>::solveTriangularSylvester(
const DynMatrixType& A,
const DynMatrixType& B,
const DynMatrixType& C)
{
eigen_assert(A.rows() == A.cols());
eigen_assert(A.isUpperTriangular());
eigen_assert(B.rows() == B.cols());
eigen_assert(B.isUpperTriangular());
eigen_assert(C.rows() == A.rows());
eigen_assert(C.cols() == B.rows());
Index m = A.rows();
Index n = B.rows();
DynMatrixType X(m, n);
for (Index i = m - 1; i >= 0; --i) {
for (Index j = 0; j < n; ++j) {
// Compute AX = \sum_{k=i+1}^m A_{ik} X_{kj}
Scalar AX;
if (i == m - 1) {
AX = 0;
} else {
Matrix<Scalar,1,1> AXmatrix = A.row(i).tail(m-1-i) * X.col(j).tail(m-1-i);
AX = AXmatrix(0,0);
}
// Compute XB = \sum_{k=1}^{j-1} X_{ik} B_{kj}
Scalar XB;
if (j == 0) {
XB = 0;
} else {
Matrix<Scalar,1,1> XBmatrix = X.row(i).head(j) * B.col(j).head(j);
XB = XBmatrix(0,0);
}
X(i,j) = (C(i,j) - AX - XB) / (A(i,i) + B(j,j));
}
}
return X;
}
/** \ingroup MatrixFunctions_Module
*
* \brief Proxy for the matrix function of some matrix (expression).
*
* \tparam Derived Type of the argument to the matrix function.
*
* This class holds the argument to the matrix function until it is
* assigned or evaluated for some other reason (so the argument
* should not be changed in the meantime). It is the return type of
* matrixBase::matrixFunction() and related functions and most of the
* time this is the only way it is used.
*/
template<typename Derived> class MatrixFunctionReturnValue
: public ReturnByValue<MatrixFunctionReturnValue<Derived> >
{
public:
typedef typename Derived::Scalar Scalar;
typedef typename Derived::Index Index;
typedef typename internal::stem_function<Scalar>::type StemFunction;
/** \brief Constructor.
*
* \param[in] A %Matrix (expression) forming the argument of the
* matrix function.
* \param[in] f Stem function for matrix function under consideration.
*/
MatrixFunctionReturnValue(const Derived& A, StemFunction f) : m_A(A), m_f(f) { }
/** \brief Compute the matrix function.
*
* \param[out] result \p f applied to \p A, where \p f and \p A
* are as in the constructor.
*/
template <typename ResultType>
inline void evalTo(ResultType& result) const
{
typedef typename Derived::PlainObject PlainObject;
typedef internal::traits<PlainObject> Traits;
static const int RowsAtCompileTime = Traits::RowsAtCompileTime;
static const int ColsAtCompileTime = Traits::ColsAtCompileTime;
static const int Options = PlainObject::Options;
typedef std::complex<typename NumTraits<Scalar>::Real> ComplexScalar;
typedef Matrix<ComplexScalar, Dynamic, Dynamic, Options, RowsAtCompileTime, ColsAtCompileTime> DynMatrixType;
typedef MatrixFunctionAtomic<DynMatrixType> AtomicType;
AtomicType atomic(m_f);
const PlainObject Aevaluated = m_A.eval();
MatrixFunction<PlainObject, AtomicType> mf(Aevaluated, atomic);
mf.compute(result);
}
Index rows() const { return m_A.rows(); }
Index cols() const { return m_A.cols(); }
private:
typename internal::nested<Derived>::type m_A;
StemFunction *m_f;
MatrixFunctionReturnValue& operator=(const MatrixFunctionReturnValue&);
};
namespace internal {
template<typename Derived>
struct traits<MatrixFunctionReturnValue<Derived> >
{
typedef typename Derived::PlainObject ReturnType;
};
}
/********** MatrixBase methods **********/
template <typename Derived>
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::matrixFunction(typename internal::stem_function<typename internal::traits<Derived>::Scalar>::type f) const
{
eigen_assert(rows() == cols());
return MatrixFunctionReturnValue<Derived>(derived(), f);
}
template <typename Derived>
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sin() const
{
eigen_assert(rows() == cols());
typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
return MatrixFunctionReturnValue<Derived>(derived(), StdStemFunctions<ComplexScalar>::sin);
}
template <typename Derived>
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cos() const
{
eigen_assert(rows() == cols());
typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
return MatrixFunctionReturnValue<Derived>(derived(), StdStemFunctions<ComplexScalar>::cos);
}
template <typename Derived>
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sinh() const
{
eigen_assert(rows() == cols());
typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
return MatrixFunctionReturnValue<Derived>(derived(), StdStemFunctions<ComplexScalar>::sinh);
}
template <typename Derived>
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cosh() const
{
eigen_assert(rows() == cols());
typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
return MatrixFunctionReturnValue<Derived>(derived(), StdStemFunctions<ComplexScalar>::cosh);
}
} // end namespace Eigen
#endif // EIGEN_MATRIX_FUNCTION

View File

@@ -0,0 +1,131 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Jitse Niesen <jitse@maths.leeds.ac.uk>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_MATRIX_FUNCTION_ATOMIC
#define EIGEN_MATRIX_FUNCTION_ATOMIC
namespace Eigen {
/** \ingroup MatrixFunctions_Module
* \class MatrixFunctionAtomic
* \brief Helper class for computing matrix functions of atomic matrices.
*
* \internal
* Here, an atomic matrix is a triangular matrix whose diagonal
* entries are close to each other.
*/
template <typename MatrixType>
class MatrixFunctionAtomic
{
public:
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::Index Index;
typedef typename NumTraits<Scalar>::Real RealScalar;
typedef typename internal::stem_function<Scalar>::type StemFunction;
typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType;
/** \brief Constructor
* \param[in] f matrix function to compute.
*/
MatrixFunctionAtomic(StemFunction f) : m_f(f) { }
/** \brief Compute matrix function of atomic matrix
* \param[in] A argument of matrix function, should be upper triangular and atomic
* \returns f(A), the matrix function evaluated at the given matrix
*/
MatrixType compute(const MatrixType& A);
private:
// Prevent copying
MatrixFunctionAtomic(const MatrixFunctionAtomic&);
MatrixFunctionAtomic& operator=(const MatrixFunctionAtomic&);
void computeMu();
bool taylorConverged(Index s, const MatrixType& F, const MatrixType& Fincr, const MatrixType& P);
/** \brief Pointer to scalar function */
StemFunction* m_f;
/** \brief Size of matrix function */
Index m_Arows;
/** \brief Mean of eigenvalues */
Scalar m_avgEival;
/** \brief Argument shifted by mean of eigenvalues */
MatrixType m_Ashifted;
/** \brief Constant used to determine whether Taylor series has converged */
RealScalar m_mu;
};
template <typename MatrixType>
MatrixType MatrixFunctionAtomic<MatrixType>::compute(const MatrixType& A)
{
// TODO: Use that A is upper triangular
m_Arows = A.rows();
m_avgEival = A.trace() / Scalar(RealScalar(m_Arows));
m_Ashifted = A - m_avgEival * MatrixType::Identity(m_Arows, m_Arows);
computeMu();
MatrixType F = m_f(m_avgEival, 0) * MatrixType::Identity(m_Arows, m_Arows);
MatrixType P = m_Ashifted;
MatrixType Fincr;
for (Index s = 1; s < 1.1 * m_Arows + 10; s++) { // upper limit is fairly arbitrary
Fincr = m_f(m_avgEival, static_cast<int>(s)) * P;
F += Fincr;
P = Scalar(RealScalar(1.0/(s + 1))) * P * m_Ashifted;
if (taylorConverged(s, F, Fincr, P)) {
return F;
}
}
eigen_assert("Taylor series does not converge" && 0);
return F;
}
/** \brief Compute \c m_mu. */
template <typename MatrixType>
void MatrixFunctionAtomic<MatrixType>::computeMu()
{
const MatrixType N = MatrixType::Identity(m_Arows, m_Arows) - m_Ashifted;
VectorType e = VectorType::Ones(m_Arows);
N.template triangularView<Upper>().solveInPlace(e);
m_mu = e.cwiseAbs().maxCoeff();
}
/** \brief Determine whether Taylor series has converged */
template <typename MatrixType>
bool MatrixFunctionAtomic<MatrixType>::taylorConverged(Index s, const MatrixType& F,
const MatrixType& Fincr, const MatrixType& P)
{
const Index n = F.rows();
const RealScalar F_norm = F.cwiseAbs().rowwise().sum().maxCoeff();
const RealScalar Fincr_norm = Fincr.cwiseAbs().rowwise().sum().maxCoeff();
if (Fincr_norm < NumTraits<Scalar>::epsilon() * F_norm) {
RealScalar delta = 0;
RealScalar rfactorial = 1;
for (Index r = 0; r < n; r++) {
RealScalar mx = 0;
for (Index i = 0; i < n; i++)
mx = (std::max)(mx, std::abs(m_f(m_Ashifted(i, i) + m_avgEival, static_cast<int>(s+r))));
if (r != 0)
rfactorial *= RealScalar(r);
delta = (std::max)(delta, mx / rfactorial);
}
const RealScalar P_norm = P.cwiseAbs().rowwise().sum().maxCoeff();
if (m_mu * delta * P_norm < NumTraits<Scalar>::epsilon() * F_norm)
return true;
}
return false;
}
} // end namespace Eigen
#endif // EIGEN_MATRIX_FUNCTION_ATOMIC

View File

@@ -0,0 +1,486 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2011 Jitse Niesen <jitse@maths.leeds.ac.uk>
// Copyright (C) 2011 Chen-Pang He <jdh8@ms63.hinet.net>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_MATRIX_LOGARITHM
#define EIGEN_MATRIX_LOGARITHM
#ifndef M_PI
#define M_PI 3.141592653589793238462643383279503L
#endif
namespace Eigen {
/** \ingroup MatrixFunctions_Module
* \class MatrixLogarithmAtomic
* \brief Helper class for computing matrix logarithm of atomic matrices.
*
* \internal
* Here, an atomic matrix is a triangular matrix whose diagonal
* entries are close to each other.
*
* \sa class MatrixFunctionAtomic, MatrixBase::log()
*/
template <typename MatrixType>
class MatrixLogarithmAtomic
{
public:
typedef typename MatrixType::Scalar Scalar;
// typedef typename MatrixType::Index Index;
typedef typename NumTraits<Scalar>::Real RealScalar;
// typedef typename internal::stem_function<Scalar>::type StemFunction;
// typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType;
/** \brief Constructor. */
MatrixLogarithmAtomic() { }
/** \brief Compute matrix logarithm of atomic matrix
* \param[in] A argument of matrix logarithm, should be upper triangular and atomic
* \returns The logarithm of \p A.
*/
MatrixType compute(const MatrixType& A);
private:
void compute2x2(const MatrixType& A, MatrixType& result);
void computeBig(const MatrixType& A, MatrixType& result);
int getPadeDegree(float normTminusI);
int getPadeDegree(double normTminusI);
int getPadeDegree(long double normTminusI);
void computePade(MatrixType& result, const MatrixType& T, int degree);
void computePade3(MatrixType& result, const MatrixType& T);
void computePade4(MatrixType& result, const MatrixType& T);
void computePade5(MatrixType& result, const MatrixType& T);
void computePade6(MatrixType& result, const MatrixType& T);
void computePade7(MatrixType& result, const MatrixType& T);
void computePade8(MatrixType& result, const MatrixType& T);
void computePade9(MatrixType& result, const MatrixType& T);
void computePade10(MatrixType& result, const MatrixType& T);
void computePade11(MatrixType& result, const MatrixType& T);
static const int minPadeDegree = 3;
static const int maxPadeDegree = std::numeric_limits<RealScalar>::digits<= 24? 5: // single precision
std::numeric_limits<RealScalar>::digits<= 53? 7: // double precision
std::numeric_limits<RealScalar>::digits<= 64? 8: // extended precision
std::numeric_limits<RealScalar>::digits<=106? 10: // double-double
11; // quadruple precision
// Prevent copying
MatrixLogarithmAtomic(const MatrixLogarithmAtomic&);
MatrixLogarithmAtomic& operator=(const MatrixLogarithmAtomic&);
};
/** \brief Compute logarithm of triangular matrix with clustered eigenvalues. */
template <typename MatrixType>
MatrixType MatrixLogarithmAtomic<MatrixType>::compute(const MatrixType& A)
{
using std::log;
MatrixType result(A.rows(), A.rows());
if (A.rows() == 1)
result(0,0) = log(A(0,0));
else if (A.rows() == 2)
compute2x2(A, result);
else
computeBig(A, result);
return result;
}
/** \brief Compute logarithm of 2x2 triangular matrix. */
template <typename MatrixType>
void MatrixLogarithmAtomic<MatrixType>::compute2x2(const MatrixType& A, MatrixType& result)
{
using std::abs;
using std::ceil;
using std::imag;
using std::log;
Scalar logA00 = log(A(0,0));
Scalar logA11 = log(A(1,1));
result(0,0) = logA00;
result(1,0) = Scalar(0);
result(1,1) = logA11;
if (A(0,0) == A(1,1)) {
result(0,1) = A(0,1) / A(0,0);
} else if ((abs(A(0,0)) < 0.5*abs(A(1,1))) || (abs(A(0,0)) > 2*abs(A(1,1)))) {
result(0,1) = A(0,1) * (logA11 - logA00) / (A(1,1) - A(0,0));
} else {
// computation in previous branch is inaccurate if A(1,1) \approx A(0,0)
int unwindingNumber = static_cast<int>(ceil((imag(logA11 - logA00) - M_PI) / (2*M_PI)));
Scalar y = A(1,1) - A(0,0), x = A(1,1) + A(0,0);
result(0,1) = A(0,1) * (Scalar(2) * numext::atanh2(y,x) + Scalar(0,2*M_PI*unwindingNumber)) / y;
}
}
/** \brief Compute logarithm of triangular matrices with size > 2.
* \details This uses a inverse scale-and-square algorithm. */
template <typename MatrixType>
void MatrixLogarithmAtomic<MatrixType>::computeBig(const MatrixType& A, MatrixType& result)
{
using std::pow;
int numberOfSquareRoots = 0;
int numberOfExtraSquareRoots = 0;
int degree;
MatrixType T = A, sqrtT;
const RealScalar maxNormForPade = maxPadeDegree<= 5? 5.3149729967117310e-1: // single precision
maxPadeDegree<= 7? 2.6429608311114350e-1: // double precision
maxPadeDegree<= 8? 2.32777776523703892094e-1L: // extended precision
maxPadeDegree<=10? 1.05026503471351080481093652651105e-1L: // double-double
1.1880960220216759245467951592883642e-1L; // quadruple precision
while (true) {
RealScalar normTminusI = (T - MatrixType::Identity(T.rows(), T.rows())).cwiseAbs().colwise().sum().maxCoeff();
if (normTminusI < maxNormForPade) {
degree = getPadeDegree(normTminusI);
int degree2 = getPadeDegree(normTminusI / RealScalar(2));
if ((degree - degree2 <= 1) || (numberOfExtraSquareRoots == 1))
break;
++numberOfExtraSquareRoots;
}
MatrixSquareRootTriangular<MatrixType>(T).compute(sqrtT);
T = sqrtT.template triangularView<Upper>();
++numberOfSquareRoots;
}
computePade(result, T, degree);
result *= pow(RealScalar(2), numberOfSquareRoots);
}
/* \brief Get suitable degree for Pade approximation. (specialized for RealScalar = float) */
template <typename MatrixType>
int MatrixLogarithmAtomic<MatrixType>::getPadeDegree(float normTminusI)
{
const float maxNormForPade[] = { 2.5111573934555054e-1 /* degree = 3 */ , 4.0535837411880493e-1,
5.3149729967117310e-1 };
int degree = 3;
for (; degree <= maxPadeDegree; ++degree)
if (normTminusI <= maxNormForPade[degree - minPadeDegree])
break;
return degree;
}
/* \brief Get suitable degree for Pade approximation. (specialized for RealScalar = double) */
template <typename MatrixType>
int MatrixLogarithmAtomic<MatrixType>::getPadeDegree(double normTminusI)
{
const double maxNormForPade[] = { 1.6206284795015624e-2 /* degree = 3 */ , 5.3873532631381171e-2,
1.1352802267628681e-1, 1.8662860613541288e-1, 2.642960831111435e-1 };
int degree = 3;
for (; degree <= maxPadeDegree; ++degree)
if (normTminusI <= maxNormForPade[degree - minPadeDegree])
break;
return degree;
}
/* \brief Get suitable degree for Pade approximation. (specialized for RealScalar = long double) */
template <typename MatrixType>
int MatrixLogarithmAtomic<MatrixType>::getPadeDegree(long double normTminusI)
{
#if LDBL_MANT_DIG == 53 // double precision
const long double maxNormForPade[] = { 1.6206284795015624e-2L /* degree = 3 */ , 5.3873532631381171e-2L,
1.1352802267628681e-1L, 1.8662860613541288e-1L, 2.642960831111435e-1L };
#elif LDBL_MANT_DIG <= 64 // extended precision
const long double maxNormForPade[] = { 5.48256690357782863103e-3L /* degree = 3 */, 2.34559162387971167321e-2L,
5.84603923897347449857e-2L, 1.08486423756725170223e-1L, 1.68385767881294446649e-1L,
2.32777776523703892094e-1L };
#elif LDBL_MANT_DIG <= 106 // double-double
const long double maxNormForPade[] = { 8.58970550342939562202529664318890e-5L /* degree = 3 */,
9.34074328446359654039446552677759e-4L, 4.26117194647672175773064114582860e-3L,
1.21546224740281848743149666560464e-2L, 2.61100544998339436713088248557444e-2L,
4.66170074627052749243018566390567e-2L, 7.32585144444135027565872014932387e-2L,
1.05026503471351080481093652651105e-1L };
#else // quadruple precision
const long double maxNormForPade[] = { 4.7419931187193005048501568167858103e-5L /* degree = 3 */,
5.8853168473544560470387769480192666e-4L, 2.9216120366601315391789493628113520e-3L,
8.8415758124319434347116734705174308e-3L, 1.9850836029449446668518049562565291e-2L,
3.6688019729653446926585242192447447e-2L, 5.9290962294020186998954055264528393e-2L,
8.6998436081634343903250580992127677e-2L, 1.1880960220216759245467951592883642e-1L };
#endif
int degree = 3;
for (; degree <= maxPadeDegree; ++degree)
if (normTminusI <= maxNormForPade[degree - minPadeDegree])
break;
return degree;
}
/* \brief Compute Pade approximation to matrix logarithm */
template <typename MatrixType>
void MatrixLogarithmAtomic<MatrixType>::computePade(MatrixType& result, const MatrixType& T, int degree)
{
switch (degree) {
case 3: computePade3(result, T); break;
case 4: computePade4(result, T); break;
case 5: computePade5(result, T); break;
case 6: computePade6(result, T); break;
case 7: computePade7(result, T); break;
case 8: computePade8(result, T); break;
case 9: computePade9(result, T); break;
case 10: computePade10(result, T); break;
case 11: computePade11(result, T); break;
default: assert(false); // should never happen
}
}
template <typename MatrixType>
void MatrixLogarithmAtomic<MatrixType>::computePade3(MatrixType& result, const MatrixType& T)
{
const int degree = 3;
const RealScalar nodes[] = { 0.1127016653792583114820734600217600L, 0.5000000000000000000000000000000000L,
0.8872983346207416885179265399782400L };
const RealScalar weights[] = { 0.2777777777777777777777777777777778L, 0.4444444444444444444444444444444444L,
0.2777777777777777777777777777777778L };
eigen_assert(degree <= maxPadeDegree);
MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows());
result.setZero(T.rows(), T.rows());
for (int k = 0; k < degree; ++k)
result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI)
.template triangularView<Upper>().solve(TminusI);
}
template <typename MatrixType>
void MatrixLogarithmAtomic<MatrixType>::computePade4(MatrixType& result, const MatrixType& T)
{
const int degree = 4;
const RealScalar nodes[] = { 0.0694318442029737123880267555535953L, 0.3300094782075718675986671204483777L,
0.6699905217924281324013328795516223L, 0.9305681557970262876119732444464048L };
const RealScalar weights[] = { 0.1739274225687269286865319746109997L, 0.3260725774312730713134680253890003L,
0.3260725774312730713134680253890003L, 0.1739274225687269286865319746109997L };
eigen_assert(degree <= maxPadeDegree);
MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows());
result.setZero(T.rows(), T.rows());
for (int k = 0; k < degree; ++k)
result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI)
.template triangularView<Upper>().solve(TminusI);
}
template <typename MatrixType>
void MatrixLogarithmAtomic<MatrixType>::computePade5(MatrixType& result, const MatrixType& T)
{
const int degree = 5;
const RealScalar nodes[] = { 0.0469100770306680036011865608503035L, 0.2307653449471584544818427896498956L,
0.5000000000000000000000000000000000L, 0.7692346550528415455181572103501044L,
0.9530899229693319963988134391496965L };
const RealScalar weights[] = { 0.1184634425280945437571320203599587L, 0.2393143352496832340206457574178191L,
0.2844444444444444444444444444444444L, 0.2393143352496832340206457574178191L,
0.1184634425280945437571320203599587L };
eigen_assert(degree <= maxPadeDegree);
MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows());
result.setZero(T.rows(), T.rows());
for (int k = 0; k < degree; ++k)
result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI)
.template triangularView<Upper>().solve(TminusI);
}
template <typename MatrixType>
void MatrixLogarithmAtomic<MatrixType>::computePade6(MatrixType& result, const MatrixType& T)
{
const int degree = 6;
const RealScalar nodes[] = { 0.0337652428984239860938492227530027L, 0.1693953067668677431693002024900473L,
0.3806904069584015456847491391596440L, 0.6193095930415984543152508608403560L,
0.8306046932331322568306997975099527L, 0.9662347571015760139061507772469973L };
const RealScalar weights[] = { 0.0856622461895851725201480710863665L, 0.1803807865240693037849167569188581L,
0.2339569672863455236949351719947755L, 0.2339569672863455236949351719947755L,
0.1803807865240693037849167569188581L, 0.0856622461895851725201480710863665L };
eigen_assert(degree <= maxPadeDegree);
MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows());
result.setZero(T.rows(), T.rows());
for (int k = 0; k < degree; ++k)
result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI)
.template triangularView<Upper>().solve(TminusI);
}
template <typename MatrixType>
void MatrixLogarithmAtomic<MatrixType>::computePade7(MatrixType& result, const MatrixType& T)
{
const int degree = 7;
const RealScalar nodes[] = { 0.0254460438286207377369051579760744L, 0.1292344072003027800680676133596058L,
0.2970774243113014165466967939615193L, 0.5000000000000000000000000000000000L,
0.7029225756886985834533032060384807L, 0.8707655927996972199319323866403942L,
0.9745539561713792622630948420239256L };
const RealScalar weights[] = { 0.0647424830844348466353057163395410L, 0.1398526957446383339507338857118898L,
0.1909150252525594724751848877444876L, 0.2089795918367346938775510204081633L,
0.1909150252525594724751848877444876L, 0.1398526957446383339507338857118898L,
0.0647424830844348466353057163395410L };
eigen_assert(degree <= maxPadeDegree);
MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows());
result.setZero(T.rows(), T.rows());
for (int k = 0; k < degree; ++k)
result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI)
.template triangularView<Upper>().solve(TminusI);
}
template <typename MatrixType>
void MatrixLogarithmAtomic<MatrixType>::computePade8(MatrixType& result, const MatrixType& T)
{
const int degree = 8;
const RealScalar nodes[] = { 0.0198550717512318841582195657152635L, 0.1016667612931866302042230317620848L,
0.2372337950418355070911304754053768L, 0.4082826787521750975302619288199080L,
0.5917173212478249024697380711800920L, 0.7627662049581644929088695245946232L,
0.8983332387068133697957769682379152L, 0.9801449282487681158417804342847365L };
const RealScalar weights[] = { 0.0506142681451881295762656771549811L, 0.1111905172266872352721779972131204L,
0.1568533229389436436689811009933007L, 0.1813418916891809914825752246385978L,
0.1813418916891809914825752246385978L, 0.1568533229389436436689811009933007L,
0.1111905172266872352721779972131204L, 0.0506142681451881295762656771549811L };
eigen_assert(degree <= maxPadeDegree);
MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows());
result.setZero(T.rows(), T.rows());
for (int k = 0; k < degree; ++k)
result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI)
.template triangularView<Upper>().solve(TminusI);
}
template <typename MatrixType>
void MatrixLogarithmAtomic<MatrixType>::computePade9(MatrixType& result, const MatrixType& T)
{
const int degree = 9;
const RealScalar nodes[] = { 0.0159198802461869550822118985481636L, 0.0819844463366821028502851059651326L,
0.1933142836497048013456489803292629L, 0.3378732882980955354807309926783317L,
0.5000000000000000000000000000000000L, 0.6621267117019044645192690073216683L,
0.8066857163502951986543510196707371L, 0.9180155536633178971497148940348674L,
0.9840801197538130449177881014518364L };
const RealScalar weights[] = { 0.0406371941807872059859460790552618L, 0.0903240803474287020292360156214564L,
0.1303053482014677311593714347093164L, 0.1561735385200014200343152032922218L,
0.1651196775006298815822625346434870L, 0.1561735385200014200343152032922218L,
0.1303053482014677311593714347093164L, 0.0903240803474287020292360156214564L,
0.0406371941807872059859460790552618L };
eigen_assert(degree <= maxPadeDegree);
MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows());
result.setZero(T.rows(), T.rows());
for (int k = 0; k < degree; ++k)
result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI)
.template triangularView<Upper>().solve(TminusI);
}
template <typename MatrixType>
void MatrixLogarithmAtomic<MatrixType>::computePade10(MatrixType& result, const MatrixType& T)
{
const int degree = 10;
const RealScalar nodes[] = { 0.0130467357414141399610179939577740L, 0.0674683166555077446339516557882535L,
0.1602952158504877968828363174425632L, 0.2833023029353764046003670284171079L,
0.4255628305091843945575869994351400L, 0.5744371694908156054424130005648600L,
0.7166976970646235953996329715828921L, 0.8397047841495122031171636825574368L,
0.9325316833444922553660483442117465L, 0.9869532642585858600389820060422260L };
const RealScalar weights[] = { 0.0333356721543440687967844049466659L, 0.0747256745752902965728881698288487L,
0.1095431812579910219977674671140816L, 0.1346333596549981775456134607847347L,
0.1477621123573764350869464973256692L, 0.1477621123573764350869464973256692L,
0.1346333596549981775456134607847347L, 0.1095431812579910219977674671140816L,
0.0747256745752902965728881698288487L, 0.0333356721543440687967844049466659L };
eigen_assert(degree <= maxPadeDegree);
MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows());
result.setZero(T.rows(), T.rows());
for (int k = 0; k < degree; ++k)
result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI)
.template triangularView<Upper>().solve(TminusI);
}
template <typename MatrixType>
void MatrixLogarithmAtomic<MatrixType>::computePade11(MatrixType& result, const MatrixType& T)
{
const int degree = 11;
const RealScalar nodes[] = { 0.0108856709269715035980309994385713L, 0.0564687001159523504624211153480364L,
0.1349239972129753379532918739844233L, 0.2404519353965940920371371652706952L,
0.3652284220238275138342340072995692L, 0.5000000000000000000000000000000000L,
0.6347715779761724861657659927004308L, 0.7595480646034059079628628347293048L,
0.8650760027870246620467081260155767L, 0.9435312998840476495375788846519636L,
0.9891143290730284964019690005614287L };
const RealScalar weights[] = { 0.0278342835580868332413768602212743L, 0.0627901847324523123173471496119701L,
0.0931451054638671257130488207158280L, 0.1165968822959952399592618524215876L,
0.1314022722551233310903444349452546L, 0.1364625433889503153572417641681711L,
0.1314022722551233310903444349452546L, 0.1165968822959952399592618524215876L,
0.0931451054638671257130488207158280L, 0.0627901847324523123173471496119701L,
0.0278342835580868332413768602212743L };
eigen_assert(degree <= maxPadeDegree);
MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows());
result.setZero(T.rows(), T.rows());
for (int k = 0; k < degree; ++k)
result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI)
.template triangularView<Upper>().solve(TminusI);
}
/** \ingroup MatrixFunctions_Module
*
* \brief Proxy for the matrix logarithm of some matrix (expression).
*
* \tparam Derived Type of the argument to the matrix function.
*
* This class holds the argument to the matrix function until it is
* assigned or evaluated for some other reason (so the argument
* should not be changed in the meantime). It is the return type of
* MatrixBase::log() and most of the time this is the only way it
* is used.
*/
template<typename Derived> class MatrixLogarithmReturnValue
: public ReturnByValue<MatrixLogarithmReturnValue<Derived> >
{
public:
typedef typename Derived::Scalar Scalar;
typedef typename Derived::Index Index;
/** \brief Constructor.
*
* \param[in] A %Matrix (expression) forming the argument of the matrix logarithm.
*/
MatrixLogarithmReturnValue(const Derived& A) : m_A(A) { }
/** \brief Compute the matrix logarithm.
*
* \param[out] result Logarithm of \p A, where \A is as specified in the constructor.
*/
template <typename ResultType>
inline void evalTo(ResultType& result) const
{
typedef typename Derived::PlainObject PlainObject;
typedef internal::traits<PlainObject> Traits;
static const int RowsAtCompileTime = Traits::RowsAtCompileTime;
static const int ColsAtCompileTime = Traits::ColsAtCompileTime;
static const int Options = PlainObject::Options;
typedef std::complex<typename NumTraits<Scalar>::Real> ComplexScalar;
typedef Matrix<ComplexScalar, Dynamic, Dynamic, Options, RowsAtCompileTime, ColsAtCompileTime> DynMatrixType;
typedef MatrixLogarithmAtomic<DynMatrixType> AtomicType;
AtomicType atomic;
const PlainObject Aevaluated = m_A.eval();
MatrixFunction<PlainObject, AtomicType> mf(Aevaluated, atomic);
mf.compute(result);
}
Index rows() const { return m_A.rows(); }
Index cols() const { return m_A.cols(); }
private:
typename internal::nested<Derived>::type m_A;
MatrixLogarithmReturnValue& operator=(const MatrixLogarithmReturnValue&);
};
namespace internal {
template<typename Derived>
struct traits<MatrixLogarithmReturnValue<Derived> >
{
typedef typename Derived::PlainObject ReturnType;
};
}
/********** MatrixBase method **********/
template <typename Derived>
const MatrixLogarithmReturnValue<Derived> MatrixBase<Derived>::log() const
{
eigen_assert(rows() == cols());
return MatrixLogarithmReturnValue<Derived>(derived());
}
} // end namespace Eigen
#endif // EIGEN_MATRIX_LOGARITHM

View File

@@ -0,0 +1,508 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2012, 2013 Chen-Pang He <jdh8@ms63.hinet.net>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_MATRIX_POWER
#define EIGEN_MATRIX_POWER
namespace Eigen {
template<typename MatrixType> class MatrixPower;
template<typename MatrixType>
class MatrixPowerRetval : public ReturnByValue< MatrixPowerRetval<MatrixType> >
{
public:
typedef typename MatrixType::RealScalar RealScalar;
typedef typename MatrixType::Index Index;
MatrixPowerRetval(MatrixPower<MatrixType>& pow, RealScalar p) : m_pow(pow), m_p(p)
{ }
template<typename ResultType>
inline void evalTo(ResultType& res) const
{ m_pow.compute(res, m_p); }
Index rows() const { return m_pow.rows(); }
Index cols() const { return m_pow.cols(); }
private:
MatrixPower<MatrixType>& m_pow;
const RealScalar m_p;
MatrixPowerRetval& operator=(const MatrixPowerRetval&);
};
template<typename MatrixType>
class MatrixPowerAtomic
{
private:
enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime
};
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::RealScalar RealScalar;
typedef std::complex<RealScalar> ComplexScalar;
typedef typename MatrixType::Index Index;
typedef Array<Scalar, RowsAtCompileTime, 1, ColMajor, MaxRowsAtCompileTime> ArrayType;
const MatrixType& m_A;
RealScalar m_p;
void computePade(int degree, const MatrixType& IminusT, MatrixType& res) const;
void compute2x2(MatrixType& res, RealScalar p) const;
void computeBig(MatrixType& res) const;
static int getPadeDegree(float normIminusT);
static int getPadeDegree(double normIminusT);
static int getPadeDegree(long double normIminusT);
static ComplexScalar computeSuperDiag(const ComplexScalar&, const ComplexScalar&, RealScalar p);
static RealScalar computeSuperDiag(RealScalar, RealScalar, RealScalar p);
public:
MatrixPowerAtomic(const MatrixType& T, RealScalar p);
void compute(MatrixType& res) const;
};
template<typename MatrixType>
MatrixPowerAtomic<MatrixType>::MatrixPowerAtomic(const MatrixType& T, RealScalar p) :
m_A(T), m_p(p)
{ eigen_assert(T.rows() == T.cols()); }
template<typename MatrixType>
void MatrixPowerAtomic<MatrixType>::compute(MatrixType& res) const
{
res.resizeLike(m_A);
switch (m_A.rows()) {
case 0:
break;
case 1:
res(0,0) = std::pow(m_A(0,0), m_p);
break;
case 2:
compute2x2(res, m_p);
break;
default:
computeBig(res);
}
}
template<typename MatrixType>
void MatrixPowerAtomic<MatrixType>::computePade(int degree, const MatrixType& IminusT, MatrixType& res) const
{
int i = degree<<1;
res = (m_p-degree) / ((i-1)<<1) * IminusT;
for (--i; i; --i) {
res = (MatrixType::Identity(IminusT.rows(), IminusT.cols()) + res).template triangularView<Upper>()
.solve((i==1 ? -m_p : i&1 ? (-m_p-(i>>1))/(i<<1) : (m_p-(i>>1))/((i-1)<<1)) * IminusT).eval();
}
res += MatrixType::Identity(IminusT.rows(), IminusT.cols());
}
// This function assumes that res has the correct size (see bug 614)
template<typename MatrixType>
void MatrixPowerAtomic<MatrixType>::compute2x2(MatrixType& res, RealScalar p) const
{
using std::abs;
using std::pow;
res.coeffRef(0,0) = pow(m_A.coeff(0,0), p);
for (Index i=1; i < m_A.cols(); ++i) {
res.coeffRef(i,i) = pow(m_A.coeff(i,i), p);
if (m_A.coeff(i-1,i-1) == m_A.coeff(i,i))
res.coeffRef(i-1,i) = p * pow(m_A.coeff(i,i), p-1);
else if (2*abs(m_A.coeff(i-1,i-1)) < abs(m_A.coeff(i,i)) || 2*abs(m_A.coeff(i,i)) < abs(m_A.coeff(i-1,i-1)))
res.coeffRef(i-1,i) = (res.coeff(i,i)-res.coeff(i-1,i-1)) / (m_A.coeff(i,i)-m_A.coeff(i-1,i-1));
else
res.coeffRef(i-1,i) = computeSuperDiag(m_A.coeff(i,i), m_A.coeff(i-1,i-1), p);
res.coeffRef(i-1,i) *= m_A.coeff(i-1,i);
}
}
template<typename MatrixType>
void MatrixPowerAtomic<MatrixType>::computeBig(MatrixType& res) const
{
const int digits = std::numeric_limits<RealScalar>::digits;
const RealScalar maxNormForPade = digits <= 24? 4.3386528e-1f: // sigle precision
digits <= 53? 2.789358995219730e-1: // double precision
digits <= 64? 2.4471944416607995472e-1L: // extended precision
digits <= 106? 1.1016843812851143391275867258512e-1L: // double-double
9.134603732914548552537150753385375e-2L; // quadruple precision
MatrixType IminusT, sqrtT, T = m_A.template triangularView<Upper>();
RealScalar normIminusT;
int degree, degree2, numberOfSquareRoots = 0;
bool hasExtraSquareRoot = false;
/* FIXME
* For singular T, norm(I - T) >= 1 but maxNormForPade < 1, leads to infinite
* loop. We should move 0 eigenvalues to bottom right corner. We need not
* worry about tiny values (e.g. 1e-300) because they will reach 1 if
* repetitively sqrt'ed.
*
* If the 0 eigenvalues are semisimple, they can form a 0 matrix at the
* bottom right corner.
*
* [ T A ]^p [ T^p (T^-1 T^p A) ]
* [ ] = [ ]
* [ 0 0 ] [ 0 0 ]
*/
for (Index i=0; i < m_A.cols(); ++i)
eigen_assert(m_A(i,i) != RealScalar(0));
while (true) {
IminusT = MatrixType::Identity(m_A.rows(), m_A.cols()) - T;
normIminusT = IminusT.cwiseAbs().colwise().sum().maxCoeff();
if (normIminusT < maxNormForPade) {
degree = getPadeDegree(normIminusT);
degree2 = getPadeDegree(normIminusT/2);
if (degree - degree2 <= 1 || hasExtraSquareRoot)
break;
hasExtraSquareRoot = true;
}
MatrixSquareRootTriangular<MatrixType>(T).compute(sqrtT);
T = sqrtT.template triangularView<Upper>();
++numberOfSquareRoots;
}
computePade(degree, IminusT, res);
for (; numberOfSquareRoots; --numberOfSquareRoots) {
compute2x2(res, std::ldexp(m_p, -numberOfSquareRoots));
res = res.template triangularView<Upper>() * res;
}
compute2x2(res, m_p);
}
template<typename MatrixType>
inline int MatrixPowerAtomic<MatrixType>::getPadeDegree(float normIminusT)
{
const float maxNormForPade[] = { 2.8064004e-1f /* degree = 3 */ , 4.3386528e-1f };
int degree = 3;
for (; degree <= 4; ++degree)
if (normIminusT <= maxNormForPade[degree - 3])
break;
return degree;
}
template<typename MatrixType>
inline int MatrixPowerAtomic<MatrixType>::getPadeDegree(double normIminusT)
{
const double maxNormForPade[] = { 1.884160592658218e-2 /* degree = 3 */ , 6.038881904059573e-2, 1.239917516308172e-1,
1.999045567181744e-1, 2.789358995219730e-1 };
int degree = 3;
for (; degree <= 7; ++degree)
if (normIminusT <= maxNormForPade[degree - 3])
break;
return degree;
}
template<typename MatrixType>
inline int MatrixPowerAtomic<MatrixType>::getPadeDegree(long double normIminusT)
{
#if LDBL_MANT_DIG == 53
const int maxPadeDegree = 7;
const double maxNormForPade[] = { 1.884160592658218e-2L /* degree = 3 */ , 6.038881904059573e-2L, 1.239917516308172e-1L,
1.999045567181744e-1L, 2.789358995219730e-1L };
#elif LDBL_MANT_DIG <= 64
const int maxPadeDegree = 8;
const double maxNormForPade[] = { 6.3854693117491799460e-3L /* degree = 3 */ , 2.6394893435456973676e-2L,
6.4216043030404063729e-2L, 1.1701165502926694307e-1L, 1.7904284231268670284e-1L, 2.4471944416607995472e-1L };
#elif LDBL_MANT_DIG <= 106
const int maxPadeDegree = 10;
const double maxNormForPade[] = { 1.0007161601787493236741409687186e-4L /* degree = 3 */ ,
1.0007161601787493236741409687186e-3L, 4.7069769360887572939882574746264e-3L, 1.3220386624169159689406653101695e-2L,
2.8063482381631737920612944054906e-2L, 4.9625993951953473052385361085058e-2L, 7.7367040706027886224557538328171e-2L,
1.1016843812851143391275867258512e-1L };
#else
const int maxPadeDegree = 10;
const double maxNormForPade[] = { 5.524506147036624377378713555116378e-5L /* degree = 3 */ ,
6.640600568157479679823602193345995e-4L, 3.227716520106894279249709728084626e-3L,
9.619593944683432960546978734646284e-3L, 2.134595382433742403911124458161147e-2L,
3.908166513900489428442993794761185e-2L, 6.266780814639442865832535460550138e-2L,
9.134603732914548552537150753385375e-2L };
#endif
int degree = 3;
for (; degree <= maxPadeDegree; ++degree)
if (normIminusT <= maxNormForPade[degree - 3])
break;
return degree;
}
template<typename MatrixType>
inline typename MatrixPowerAtomic<MatrixType>::ComplexScalar
MatrixPowerAtomic<MatrixType>::computeSuperDiag(const ComplexScalar& curr, const ComplexScalar& prev, RealScalar p)
{
ComplexScalar logCurr = std::log(curr);
ComplexScalar logPrev = std::log(prev);
int unwindingNumber = std::ceil((numext::imag(logCurr - logPrev) - M_PI) / (2*M_PI));
ComplexScalar w = numext::atanh2(curr - prev, curr + prev) + ComplexScalar(0, M_PI*unwindingNumber);
return RealScalar(2) * std::exp(RealScalar(0.5) * p * (logCurr + logPrev)) * std::sinh(p * w) / (curr - prev);
}
template<typename MatrixType>
inline typename MatrixPowerAtomic<MatrixType>::RealScalar
MatrixPowerAtomic<MatrixType>::computeSuperDiag(RealScalar curr, RealScalar prev, RealScalar p)
{
RealScalar w = numext::atanh2(curr - prev, curr + prev);
return 2 * std::exp(p * (std::log(curr) + std::log(prev)) / 2) * std::sinh(p * w) / (curr - prev);
}
/**
* \ingroup MatrixFunctions_Module
*
* \brief Class for computing matrix powers.
*
* \tparam MatrixType type of the base, expected to be an instantiation
* of the Matrix class template.
*
* This class is capable of computing real/complex matrices raised to
* an arbitrary real power. Meanwhile, it saves the result of Schur
* decomposition if an non-integral power has even been calculated.
* Therefore, if you want to compute multiple (>= 2) matrix powers
* for the same matrix, using the class directly is more efficient than
* calling MatrixBase::pow().
*
* Example:
* \include MatrixPower_optimal.cpp
* Output: \verbinclude MatrixPower_optimal.out
*/
template<typename MatrixType>
class MatrixPower
{
private:
enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
};
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::RealScalar RealScalar;
typedef typename MatrixType::Index Index;
public:
/**
* \brief Constructor.
*
* \param[in] A the base of the matrix power.
*
* The class stores a reference to A, so it should not be changed
* (or destroyed) before evaluation.
*/
explicit MatrixPower(const MatrixType& A) : m_A(A), m_conditionNumber(0)
{ eigen_assert(A.rows() == A.cols()); }
/**
* \brief Returns the matrix power.
*
* \param[in] p exponent, a real scalar.
* \return The expression \f$ A^p \f$, where A is specified in the
* constructor.
*/
const MatrixPowerRetval<MatrixType> operator()(RealScalar p)
{ return MatrixPowerRetval<MatrixType>(*this, p); }
/**
* \brief Compute the matrix power.
*
* \param[in] p exponent, a real scalar.
* \param[out] res \f$ A^p \f$ where A is specified in the
* constructor.
*/
template<typename ResultType>
void compute(ResultType& res, RealScalar p);
Index rows() const { return m_A.rows(); }
Index cols() const { return m_A.cols(); }
private:
typedef std::complex<RealScalar> ComplexScalar;
typedef Matrix<ComplexScalar, RowsAtCompileTime, ColsAtCompileTime, MatrixType::Options,
MaxRowsAtCompileTime, MaxColsAtCompileTime> ComplexMatrix;
typename MatrixType::Nested m_A;
MatrixType m_tmp;
ComplexMatrix m_T, m_U, m_fT;
RealScalar m_conditionNumber;
RealScalar modfAndInit(RealScalar, RealScalar*);
template<typename ResultType>
void computeIntPower(ResultType&, RealScalar);
template<typename ResultType>
void computeFracPower(ResultType&, RealScalar);
template<int Rows, int Cols, int Options, int MaxRows, int MaxCols>
static void revertSchur(
Matrix<ComplexScalar, Rows, Cols, Options, MaxRows, MaxCols>& res,
const ComplexMatrix& T,
const ComplexMatrix& U);
template<int Rows, int Cols, int Options, int MaxRows, int MaxCols>
static void revertSchur(
Matrix<RealScalar, Rows, Cols, Options, MaxRows, MaxCols>& res,
const ComplexMatrix& T,
const ComplexMatrix& U);
};
template<typename MatrixType>
template<typename ResultType>
void MatrixPower<MatrixType>::compute(ResultType& res, RealScalar p)
{
switch (cols()) {
case 0:
break;
case 1:
res(0,0) = std::pow(m_A.coeff(0,0), p);
break;
default:
RealScalar intpart, x = modfAndInit(p, &intpart);
computeIntPower(res, intpart);
computeFracPower(res, x);
}
}
template<typename MatrixType>
typename MatrixPower<MatrixType>::RealScalar
MatrixPower<MatrixType>::modfAndInit(RealScalar x, RealScalar* intpart)
{
typedef Array<RealScalar, RowsAtCompileTime, 1, ColMajor, MaxRowsAtCompileTime> RealArray;
*intpart = std::floor(x);
RealScalar res = x - *intpart;
if (!m_conditionNumber && res) {
const ComplexSchur<MatrixType> schurOfA(m_A);
m_T = schurOfA.matrixT();
m_U = schurOfA.matrixU();
const RealArray absTdiag = m_T.diagonal().array().abs();
m_conditionNumber = absTdiag.maxCoeff() / absTdiag.minCoeff();
}
if (res>RealScalar(0.5) && res>(1-res)*std::pow(m_conditionNumber, res)) {
--res;
++*intpart;
}
return res;
}
template<typename MatrixType>
template<typename ResultType>
void MatrixPower<MatrixType>::computeIntPower(ResultType& res, RealScalar p)
{
RealScalar pp = std::abs(p);
if (p<0) m_tmp = m_A.inverse();
else m_tmp = m_A;
res = MatrixType::Identity(rows(), cols());
while (pp >= 1) {
if (std::fmod(pp, 2) >= 1)
res = m_tmp * res;
m_tmp *= m_tmp;
pp /= 2;
}
}
template<typename MatrixType>
template<typename ResultType>
void MatrixPower<MatrixType>::computeFracPower(ResultType& res, RealScalar p)
{
if (p) {
eigen_assert(m_conditionNumber);
MatrixPowerAtomic<ComplexMatrix>(m_T, p).compute(m_fT);
revertSchur(m_tmp, m_fT, m_U);
res = m_tmp * res;
}
}
template<typename MatrixType>
template<int Rows, int Cols, int Options, int MaxRows, int MaxCols>
inline void MatrixPower<MatrixType>::revertSchur(
Matrix<ComplexScalar, Rows, Cols, Options, MaxRows, MaxCols>& res,
const ComplexMatrix& T,
const ComplexMatrix& U)
{ res.noalias() = U * (T.template triangularView<Upper>() * U.adjoint()); }
template<typename MatrixType>
template<int Rows, int Cols, int Options, int MaxRows, int MaxCols>
inline void MatrixPower<MatrixType>::revertSchur(
Matrix<RealScalar, Rows, Cols, Options, MaxRows, MaxCols>& res,
const ComplexMatrix& T,
const ComplexMatrix& U)
{ res.noalias() = (U * (T.template triangularView<Upper>() * U.adjoint())).real(); }
/**
* \ingroup MatrixFunctions_Module
*
* \brief Proxy for the matrix power of some matrix (expression).
*
* \tparam Derived type of the base, a matrix (expression).
*
* This class holds the arguments to the matrix power until it is
* assigned or evaluated for some other reason (so the argument
* should not be changed in the meantime). It is the return type of
* MatrixBase::pow() and related functions and most of the
* time this is the only way it is used.
*/
template<typename Derived>
class MatrixPowerReturnValue : public ReturnByValue< MatrixPowerReturnValue<Derived> >
{
public:
typedef typename Derived::PlainObject PlainObject;
typedef typename Derived::RealScalar RealScalar;
typedef typename Derived::Index Index;
/**
* \brief Constructor.
*
* \param[in] A %Matrix (expression), the base of the matrix power.
* \param[in] p scalar, the exponent of the matrix power.
*/
MatrixPowerReturnValue(const Derived& A, RealScalar p) : m_A(A), m_p(p)
{ }
/**
* \brief Compute the matrix power.
*
* \param[out] result \f$ A^p \f$ where \p A and \p p are as in the
* constructor.
*/
template<typename ResultType>
inline void evalTo(ResultType& res) const
{ MatrixPower<PlainObject>(m_A.eval()).compute(res, m_p); }
Index rows() const { return m_A.rows(); }
Index cols() const { return m_A.cols(); }
private:
const Derived& m_A;
const RealScalar m_p;
MatrixPowerReturnValue& operator=(const MatrixPowerReturnValue&);
};
namespace internal {
template<typename MatrixPowerType>
struct traits< MatrixPowerRetval<MatrixPowerType> >
{ typedef typename MatrixPowerType::PlainObject ReturnType; };
template<typename Derived>
struct traits< MatrixPowerReturnValue<Derived> >
{ typedef typename Derived::PlainObject ReturnType; };
}
template<typename Derived>
const MatrixPowerReturnValue<Derived> MatrixBase<Derived>::pow(const RealScalar& p) const
{ return MatrixPowerReturnValue<Derived>(derived(), p); }
} // namespace Eigen
#endif // EIGEN_MATRIX_POWER

View File

@@ -0,0 +1,466 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2011 Jitse Niesen <jitse@maths.leeds.ac.uk>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_MATRIX_SQUARE_ROOT
#define EIGEN_MATRIX_SQUARE_ROOT
namespace Eigen {
/** \ingroup MatrixFunctions_Module
* \brief Class for computing matrix square roots of upper quasi-triangular matrices.
* \tparam MatrixType type of the argument of the matrix square root,
* expected to be an instantiation of the Matrix class template.
*
* This class computes the square root of the upper quasi-triangular
* matrix stored in the upper Hessenberg part of the matrix passed to
* the constructor.
*
* \sa MatrixSquareRoot, MatrixSquareRootTriangular
*/
template <typename MatrixType>
class MatrixSquareRootQuasiTriangular
{
public:
/** \brief Constructor.
*
* \param[in] A upper quasi-triangular matrix whose square root
* is to be computed.
*
* The class stores a reference to \p A, so it should not be
* changed (or destroyed) before compute() is called.
*/
MatrixSquareRootQuasiTriangular(const MatrixType& A)
: m_A(A)
{
eigen_assert(A.rows() == A.cols());
}
/** \brief Compute the matrix square root
*
* \param[out] result square root of \p A, as specified in the constructor.
*
* Only the upper Hessenberg part of \p result is updated, the
* rest is not touched. See MatrixBase::sqrt() for details on
* how this computation is implemented.
*/
template <typename ResultType> void compute(ResultType &result);
private:
typedef typename MatrixType::Index Index;
typedef typename MatrixType::Scalar Scalar;
void computeDiagonalPartOfSqrt(MatrixType& sqrtT, const MatrixType& T);
void computeOffDiagonalPartOfSqrt(MatrixType& sqrtT, const MatrixType& T);
void compute2x2diagonalBlock(MatrixType& sqrtT, const MatrixType& T, typename MatrixType::Index i);
void compute1x1offDiagonalBlock(MatrixType& sqrtT, const MatrixType& T,
typename MatrixType::Index i, typename MatrixType::Index j);
void compute1x2offDiagonalBlock(MatrixType& sqrtT, const MatrixType& T,
typename MatrixType::Index i, typename MatrixType::Index j);
void compute2x1offDiagonalBlock(MatrixType& sqrtT, const MatrixType& T,
typename MatrixType::Index i, typename MatrixType::Index j);
void compute2x2offDiagonalBlock(MatrixType& sqrtT, const MatrixType& T,
typename MatrixType::Index i, typename MatrixType::Index j);
template <typename SmallMatrixType>
static void solveAuxiliaryEquation(SmallMatrixType& X, const SmallMatrixType& A,
const SmallMatrixType& B, const SmallMatrixType& C);
const MatrixType& m_A;
};
template <typename MatrixType>
template <typename ResultType>
void MatrixSquareRootQuasiTriangular<MatrixType>::compute(ResultType &result)
{
result.resize(m_A.rows(), m_A.cols());
computeDiagonalPartOfSqrt(result, m_A);
computeOffDiagonalPartOfSqrt(result, m_A);
}
// pre: T is quasi-upper-triangular and sqrtT is a zero matrix of the same size
// post: the diagonal blocks of sqrtT are the square roots of the diagonal blocks of T
template <typename MatrixType>
void MatrixSquareRootQuasiTriangular<MatrixType>::computeDiagonalPartOfSqrt(MatrixType& sqrtT,
const MatrixType& T)
{
using std::sqrt;
const Index size = m_A.rows();
for (Index i = 0; i < size; i++) {
if (i == size - 1 || T.coeff(i+1, i) == 0) {
eigen_assert(T(i,i) >= 0);
sqrtT.coeffRef(i,i) = sqrt(T.coeff(i,i));
}
else {
compute2x2diagonalBlock(sqrtT, T, i);
++i;
}
}
}
// pre: T is quasi-upper-triangular and diagonal blocks of sqrtT are square root of diagonal blocks of T.
// post: sqrtT is the square root of T.
template <typename MatrixType>
void MatrixSquareRootQuasiTriangular<MatrixType>::computeOffDiagonalPartOfSqrt(MatrixType& sqrtT,
const MatrixType& T)
{
const Index size = m_A.rows();
for (Index j = 1; j < size; j++) {
if (T.coeff(j, j-1) != 0) // if T(j-1:j, j-1:j) is a 2-by-2 block
continue;
for (Index i = j-1; i >= 0; i--) {
if (i > 0 && T.coeff(i, i-1) != 0) // if T(i-1:i, i-1:i) is a 2-by-2 block
continue;
bool iBlockIs2x2 = (i < size - 1) && (T.coeff(i+1, i) != 0);
bool jBlockIs2x2 = (j < size - 1) && (T.coeff(j+1, j) != 0);
if (iBlockIs2x2 && jBlockIs2x2)
compute2x2offDiagonalBlock(sqrtT, T, i, j);
else if (iBlockIs2x2 && !jBlockIs2x2)
compute2x1offDiagonalBlock(sqrtT, T, i, j);
else if (!iBlockIs2x2 && jBlockIs2x2)
compute1x2offDiagonalBlock(sqrtT, T, i, j);
else if (!iBlockIs2x2 && !jBlockIs2x2)
compute1x1offDiagonalBlock(sqrtT, T, i, j);
}
}
}
// pre: T.block(i,i,2,2) has complex conjugate eigenvalues
// post: sqrtT.block(i,i,2,2) is square root of T.block(i,i,2,2)
template <typename MatrixType>
void MatrixSquareRootQuasiTriangular<MatrixType>
::compute2x2diagonalBlock(MatrixType& sqrtT, const MatrixType& T, typename MatrixType::Index i)
{
// TODO: This case (2-by-2 blocks with complex conjugate eigenvalues) is probably hidden somewhere
// in EigenSolver. If we expose it, we could call it directly from here.
Matrix<Scalar,2,2> block = T.template block<2,2>(i,i);
EigenSolver<Matrix<Scalar,2,2> > es(block);
sqrtT.template block<2,2>(i,i)
= (es.eigenvectors() * es.eigenvalues().cwiseSqrt().asDiagonal() * es.eigenvectors().inverse()).real();
}
// pre: block structure of T is such that (i,j) is a 1x1 block,
// all blocks of sqrtT to left of and below (i,j) are correct
// post: sqrtT(i,j) has the correct value
template <typename MatrixType>
void MatrixSquareRootQuasiTriangular<MatrixType>
::compute1x1offDiagonalBlock(MatrixType& sqrtT, const MatrixType& T,
typename MatrixType::Index i, typename MatrixType::Index j)
{
Scalar tmp = (sqrtT.row(i).segment(i+1,j-i-1) * sqrtT.col(j).segment(i+1,j-i-1)).value();
sqrtT.coeffRef(i,j) = (T.coeff(i,j) - tmp) / (sqrtT.coeff(i,i) + sqrtT.coeff(j,j));
}
// similar to compute1x1offDiagonalBlock()
template <typename MatrixType>
void MatrixSquareRootQuasiTriangular<MatrixType>
::compute1x2offDiagonalBlock(MatrixType& sqrtT, const MatrixType& T,
typename MatrixType::Index i, typename MatrixType::Index j)
{
Matrix<Scalar,1,2> rhs = T.template block<1,2>(i,j);
if (j-i > 1)
rhs -= sqrtT.block(i, i+1, 1, j-i-1) * sqrtT.block(i+1, j, j-i-1, 2);
Matrix<Scalar,2,2> A = sqrtT.coeff(i,i) * Matrix<Scalar,2,2>::Identity();
A += sqrtT.template block<2,2>(j,j).transpose();
sqrtT.template block<1,2>(i,j).transpose() = A.fullPivLu().solve(rhs.transpose());
}
// similar to compute1x1offDiagonalBlock()
template <typename MatrixType>
void MatrixSquareRootQuasiTriangular<MatrixType>
::compute2x1offDiagonalBlock(MatrixType& sqrtT, const MatrixType& T,
typename MatrixType::Index i, typename MatrixType::Index j)
{
Matrix<Scalar,2,1> rhs = T.template block<2,1>(i,j);
if (j-i > 2)
rhs -= sqrtT.block(i, i+2, 2, j-i-2) * sqrtT.block(i+2, j, j-i-2, 1);
Matrix<Scalar,2,2> A = sqrtT.coeff(j,j) * Matrix<Scalar,2,2>::Identity();
A += sqrtT.template block<2,2>(i,i);
sqrtT.template block<2,1>(i,j) = A.fullPivLu().solve(rhs);
}
// similar to compute1x1offDiagonalBlock()
template <typename MatrixType>
void MatrixSquareRootQuasiTriangular<MatrixType>
::compute2x2offDiagonalBlock(MatrixType& sqrtT, const MatrixType& T,
typename MatrixType::Index i, typename MatrixType::Index j)
{
Matrix<Scalar,2,2> A = sqrtT.template block<2,2>(i,i);
Matrix<Scalar,2,2> B = sqrtT.template block<2,2>(j,j);
Matrix<Scalar,2,2> C = T.template block<2,2>(i,j);
if (j-i > 2)
C -= sqrtT.block(i, i+2, 2, j-i-2) * sqrtT.block(i+2, j, j-i-2, 2);
Matrix<Scalar,2,2> X;
solveAuxiliaryEquation(X, A, B, C);
sqrtT.template block<2,2>(i,j) = X;
}
// solves the equation A X + X B = C where all matrices are 2-by-2
template <typename MatrixType>
template <typename SmallMatrixType>
void MatrixSquareRootQuasiTriangular<MatrixType>
::solveAuxiliaryEquation(SmallMatrixType& X, const SmallMatrixType& A,
const SmallMatrixType& B, const SmallMatrixType& C)
{
EIGEN_STATIC_ASSERT((internal::is_same<SmallMatrixType, Matrix<Scalar,2,2> >::value),
EIGEN_INTERNAL_ERROR_PLEASE_FILE_A_BUG_REPORT);
Matrix<Scalar,4,4> coeffMatrix = Matrix<Scalar,4,4>::Zero();
coeffMatrix.coeffRef(0,0) = A.coeff(0,0) + B.coeff(0,0);
coeffMatrix.coeffRef(1,1) = A.coeff(0,0) + B.coeff(1,1);
coeffMatrix.coeffRef(2,2) = A.coeff(1,1) + B.coeff(0,0);
coeffMatrix.coeffRef(3,3) = A.coeff(1,1) + B.coeff(1,1);
coeffMatrix.coeffRef(0,1) = B.coeff(1,0);
coeffMatrix.coeffRef(0,2) = A.coeff(0,1);
coeffMatrix.coeffRef(1,0) = B.coeff(0,1);
coeffMatrix.coeffRef(1,3) = A.coeff(0,1);
coeffMatrix.coeffRef(2,0) = A.coeff(1,0);
coeffMatrix.coeffRef(2,3) = B.coeff(1,0);
coeffMatrix.coeffRef(3,1) = A.coeff(1,0);
coeffMatrix.coeffRef(3,2) = B.coeff(0,1);
Matrix<Scalar,4,1> rhs;
rhs.coeffRef(0) = C.coeff(0,0);
rhs.coeffRef(1) = C.coeff(0,1);
rhs.coeffRef(2) = C.coeff(1,0);
rhs.coeffRef(3) = C.coeff(1,1);
Matrix<Scalar,4,1> result;
result = coeffMatrix.fullPivLu().solve(rhs);
X.coeffRef(0,0) = result.coeff(0);
X.coeffRef(0,1) = result.coeff(1);
X.coeffRef(1,0) = result.coeff(2);
X.coeffRef(1,1) = result.coeff(3);
}
/** \ingroup MatrixFunctions_Module
* \brief Class for computing matrix square roots of upper triangular matrices.
* \tparam MatrixType type of the argument of the matrix square root,
* expected to be an instantiation of the Matrix class template.
*
* This class computes the square root of the upper triangular matrix
* stored in the upper triangular part (including the diagonal) of
* the matrix passed to the constructor.
*
* \sa MatrixSquareRoot, MatrixSquareRootQuasiTriangular
*/
template <typename MatrixType>
class MatrixSquareRootTriangular
{
public:
MatrixSquareRootTriangular(const MatrixType& A)
: m_A(A)
{
eigen_assert(A.rows() == A.cols());
}
/** \brief Compute the matrix square root
*
* \param[out] result square root of \p A, as specified in the constructor.
*
* Only the upper triangular part (including the diagonal) of
* \p result is updated, the rest is not touched. See
* MatrixBase::sqrt() for details on how this computation is
* implemented.
*/
template <typename ResultType> void compute(ResultType &result);
private:
const MatrixType& m_A;
};
template <typename MatrixType>
template <typename ResultType>
void MatrixSquareRootTriangular<MatrixType>::compute(ResultType &result)
{
using std::sqrt;
// Compute square root of m_A and store it in upper triangular part of result
// This uses that the square root of triangular matrices can be computed directly.
result.resize(m_A.rows(), m_A.cols());
typedef typename MatrixType::Index Index;
for (Index i = 0; i < m_A.rows(); i++) {
result.coeffRef(i,i) = sqrt(m_A.coeff(i,i));
}
for (Index j = 1; j < m_A.cols(); j++) {
for (Index i = j-1; i >= 0; i--) {
typedef typename MatrixType::Scalar Scalar;
// if i = j-1, then segment has length 0 so tmp = 0
Scalar tmp = (result.row(i).segment(i+1,j-i-1) * result.col(j).segment(i+1,j-i-1)).value();
// denominator may be zero if original matrix is singular
result.coeffRef(i,j) = (m_A.coeff(i,j) - tmp) / (result.coeff(i,i) + result.coeff(j,j));
}
}
}
/** \ingroup MatrixFunctions_Module
* \brief Class for computing matrix square roots of general matrices.
* \tparam MatrixType type of the argument of the matrix square root,
* expected to be an instantiation of the Matrix class template.
*
* \sa MatrixSquareRootTriangular, MatrixSquareRootQuasiTriangular, MatrixBase::sqrt()
*/
template <typename MatrixType, int IsComplex = NumTraits<typename internal::traits<MatrixType>::Scalar>::IsComplex>
class MatrixSquareRoot
{
public:
/** \brief Constructor.
*
* \param[in] A matrix whose square root is to be computed.
*
* The class stores a reference to \p A, so it should not be
* changed (or destroyed) before compute() is called.
*/
MatrixSquareRoot(const MatrixType& A);
/** \brief Compute the matrix square root
*
* \param[out] result square root of \p A, as specified in the constructor.
*
* See MatrixBase::sqrt() for details on how this computation is
* implemented.
*/
template <typename ResultType> void compute(ResultType &result);
};
// ********** Partial specialization for real matrices **********
template <typename MatrixType>
class MatrixSquareRoot<MatrixType, 0>
{
public:
MatrixSquareRoot(const MatrixType& A)
: m_A(A)
{
eigen_assert(A.rows() == A.cols());
}
template <typename ResultType> void compute(ResultType &result)
{
// Compute Schur decomposition of m_A
const RealSchur<MatrixType> schurOfA(m_A);
const MatrixType& T = schurOfA.matrixT();
const MatrixType& U = schurOfA.matrixU();
// Compute square root of T
MatrixType sqrtT = MatrixType::Zero(m_A.rows(), m_A.cols());
MatrixSquareRootQuasiTriangular<MatrixType>(T).compute(sqrtT);
// Compute square root of m_A
result = U * sqrtT * U.adjoint();
}
private:
const MatrixType& m_A;
};
// ********** Partial specialization for complex matrices **********
template <typename MatrixType>
class MatrixSquareRoot<MatrixType, 1>
{
public:
MatrixSquareRoot(const MatrixType& A)
: m_A(A)
{
eigen_assert(A.rows() == A.cols());
}
template <typename ResultType> void compute(ResultType &result)
{
// Compute Schur decomposition of m_A
const ComplexSchur<MatrixType> schurOfA(m_A);
const MatrixType& T = schurOfA.matrixT();
const MatrixType& U = schurOfA.matrixU();
// Compute square root of T
MatrixType sqrtT;
MatrixSquareRootTriangular<MatrixType>(T).compute(sqrtT);
// Compute square root of m_A
result = U * (sqrtT.template triangularView<Upper>() * U.adjoint());
}
private:
const MatrixType& m_A;
};
/** \ingroup MatrixFunctions_Module
*
* \brief Proxy for the matrix square root of some matrix (expression).
*
* \tparam Derived Type of the argument to the matrix square root.
*
* This class holds the argument to the matrix square root until it
* is assigned or evaluated for some other reason (so the argument
* should not be changed in the meantime). It is the return type of
* MatrixBase::sqrt() and most of the time this is the only way it is
* used.
*/
template<typename Derived> class MatrixSquareRootReturnValue
: public ReturnByValue<MatrixSquareRootReturnValue<Derived> >
{
typedef typename Derived::Index Index;
public:
/** \brief Constructor.
*
* \param[in] src %Matrix (expression) forming the argument of the
* matrix square root.
*/
MatrixSquareRootReturnValue(const Derived& src) : m_src(src) { }
/** \brief Compute the matrix square root.
*
* \param[out] result the matrix square root of \p src in the
* constructor.
*/
template <typename ResultType>
inline void evalTo(ResultType& result) const
{
const typename Derived::PlainObject srcEvaluated = m_src.eval();
MatrixSquareRoot<typename Derived::PlainObject> me(srcEvaluated);
me.compute(result);
}
Index rows() const { return m_src.rows(); }
Index cols() const { return m_src.cols(); }
protected:
const Derived& m_src;
private:
MatrixSquareRootReturnValue& operator=(const MatrixSquareRootReturnValue&);
};
namespace internal {
template<typename Derived>
struct traits<MatrixSquareRootReturnValue<Derived> >
{
typedef typename Derived::PlainObject ReturnType;
};
}
template <typename Derived>
const MatrixSquareRootReturnValue<Derived> MatrixBase<Derived>::sqrt() const
{
eigen_assert(rows() == cols());
return MatrixSquareRootReturnValue<Derived>(derived());
}
} // end namespace Eigen
#endif // EIGEN_MATRIX_FUNCTION

View File

@@ -0,0 +1,105 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2010 Jitse Niesen <jitse@maths.leeds.ac.uk>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_STEM_FUNCTION
#define EIGEN_STEM_FUNCTION
namespace Eigen {
/** \ingroup MatrixFunctions_Module
* \brief Stem functions corresponding to standard mathematical functions.
*/
template <typename Scalar>
class StdStemFunctions
{
public:
/** \brief The exponential function (and its derivatives). */
static Scalar exp(Scalar x, int)
{
return std::exp(x);
}
/** \brief Cosine (and its derivatives). */
static Scalar cos(Scalar x, int n)
{
Scalar res;
switch (n % 4) {
case 0:
res = std::cos(x);
break;
case 1:
res = -std::sin(x);
break;
case 2:
res = -std::cos(x);
break;
case 3:
res = std::sin(x);
break;
}
return res;
}
/** \brief Sine (and its derivatives). */
static Scalar sin(Scalar x, int n)
{
Scalar res;
switch (n % 4) {
case 0:
res = std::sin(x);
break;
case 1:
res = std::cos(x);
break;
case 2:
res = -std::sin(x);
break;
case 3:
res = -std::cos(x);
break;
}
return res;
}
/** \brief Hyperbolic cosine (and its derivatives). */
static Scalar cosh(Scalar x, int n)
{
Scalar res;
switch (n % 2) {
case 0:
res = std::cosh(x);
break;
case 1:
res = std::sinh(x);
break;
}
return res;
}
/** \brief Hyperbolic sine (and its derivatives). */
static Scalar sinh(Scalar x, int n)
{
Scalar res;
switch (n % 2) {
case 0:
res = std::sinh(x);
break;
case 1:
res = std::cosh(x);
break;
}
return res;
}
}; // end of class StdStemFunctions
} // end namespace Eigen
#endif // EIGEN_STEM_FUNCTION

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_MoreVectorization_SRCS "*.h")
INSTALL(FILES
${Eigen_MoreVectorization_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/MoreVectorization COMPONENT Devel
)

View File

@@ -0,0 +1,95 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Rohit Garg <rpg.314@gmail.com>
// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_MOREVECTORIZATION_MATHFUNCTIONS_H
#define EIGEN_MOREVECTORIZATION_MATHFUNCTIONS_H
namespace Eigen {
namespace internal {
/** \internal \returns the arcsin of \a a (coeff-wise) */
template<typename Packet> inline static Packet pasin(Packet a) { return std::asin(a); }
#ifdef EIGEN_VECTORIZE_SSE
template<> EIGEN_DONT_INLINE Packet4f pasin(Packet4f x)
{
_EIGEN_DECLARE_CONST_Packet4f(half, 0.5);
_EIGEN_DECLARE_CONST_Packet4f(minus_half, -0.5);
_EIGEN_DECLARE_CONST_Packet4f(3half, 1.5);
_EIGEN_DECLARE_CONST_Packet4f_FROM_INT(sign_mask, 0x80000000);
_EIGEN_DECLARE_CONST_Packet4f(pi, 3.141592654);
_EIGEN_DECLARE_CONST_Packet4f(pi_over_2, 3.141592654*0.5);
_EIGEN_DECLARE_CONST_Packet4f(asin1, 4.2163199048E-2);
_EIGEN_DECLARE_CONST_Packet4f(asin2, 2.4181311049E-2);
_EIGEN_DECLARE_CONST_Packet4f(asin3, 4.5470025998E-2);
_EIGEN_DECLARE_CONST_Packet4f(asin4, 7.4953002686E-2);
_EIGEN_DECLARE_CONST_Packet4f(asin5, 1.6666752422E-1);
Packet4f a = pabs(x);//got the absolute value
Packet4f sign_bit= _mm_and_ps(x, p4f_sign_mask);//extracted the sign bit
Packet4f z1,z2;//will need them during computation
//will compute the two branches for asin
//so first compare with half
Packet4f branch_mask= _mm_cmpgt_ps(a, p4f_half);//this is to select which branch to take
//both will be taken, and finally results will be merged
//the branch for values >0.5
{
//the core series expansion
z1=pmadd(p4f_minus_half,a,p4f_half);
Packet4f x1=psqrt(z1);
Packet4f s1=pmadd(p4f_asin1, z1, p4f_asin2);
Packet4f s2=pmadd(s1, z1, p4f_asin3);
Packet4f s3=pmadd(s2,z1, p4f_asin4);
Packet4f s4=pmadd(s3,z1, p4f_asin5);
Packet4f temp=pmul(s4,z1);//not really a madd but a mul by z so that the next term can be a madd
z1=pmadd(temp,x1,x1);
z1=padd(z1,z1);
z1=psub(p4f_pi_over_2,z1);
}
{
//the core series expansion
Packet4f x2=a;
z2=pmul(x2,x2);
Packet4f s1=pmadd(p4f_asin1, z2, p4f_asin2);
Packet4f s2=pmadd(s1, z2, p4f_asin3);
Packet4f s3=pmadd(s2,z2, p4f_asin4);
Packet4f s4=pmadd(s3,z2, p4f_asin5);
Packet4f temp=pmul(s4,z2);//not really a madd but a mul by z so that the next term can be a madd
z2=pmadd(temp,x2,x2);
}
/* select the correct result from the two branch evaluations */
z1 = _mm_and_ps(branch_mask, z1);
z2 = _mm_andnot_ps(branch_mask, z2);
Packet4f z = _mm_or_ps(z1,z2);
/* update the sign */
return _mm_xor_ps(z, sign_bit);
}
#endif // EIGEN_VECTORIZE_SSE
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_MOREVECTORIZATION_MATHFUNCTIONS_H

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_NonLinearOptimization_SRCS "*.h")
INSTALL(FILES
${Eigen_NonLinearOptimization_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/NonLinearOptimization COMPONENT Devel
)

View File

@@ -0,0 +1,601 @@
// -*- coding: utf-8
// vim: set fileencoding=utf-8
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Thomas Capricelli <orzel@freehackers.org>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_HYBRIDNONLINEARSOLVER_H
#define EIGEN_HYBRIDNONLINEARSOLVER_H
namespace Eigen {
namespace HybridNonLinearSolverSpace {
enum Status {
Running = -1,
ImproperInputParameters = 0,
RelativeErrorTooSmall = 1,
TooManyFunctionEvaluation = 2,
TolTooSmall = 3,
NotMakingProgressJacobian = 4,
NotMakingProgressIterations = 5,
UserAsked = 6
};
}
/**
* \ingroup NonLinearOptimization_Module
* \brief Finds a zero of a system of n
* nonlinear functions in n variables by a modification of the Powell
* hybrid method ("dogleg").
*
* The user must provide a subroutine which calculates the
* functions. The Jacobian is either provided by the user, or approximated
* using a forward-difference method.
*
*/
template<typename FunctorType, typename Scalar=double>
class HybridNonLinearSolver
{
public:
typedef DenseIndex Index;
HybridNonLinearSolver(FunctorType &_functor)
: functor(_functor) { nfev=njev=iter = 0; fnorm= 0.; useExternalScaling=false;}
struct Parameters {
Parameters()
: factor(Scalar(100.))
, maxfev(1000)
, xtol(std::sqrt(NumTraits<Scalar>::epsilon()))
, nb_of_subdiagonals(-1)
, nb_of_superdiagonals(-1)
, epsfcn(Scalar(0.)) {}
Scalar factor;
Index maxfev; // maximum number of function evaluation
Scalar xtol;
Index nb_of_subdiagonals;
Index nb_of_superdiagonals;
Scalar epsfcn;
};
typedef Matrix< Scalar, Dynamic, 1 > FVectorType;
typedef Matrix< Scalar, Dynamic, Dynamic > JacobianType;
/* TODO: if eigen provides a triangular storage, use it here */
typedef Matrix< Scalar, Dynamic, Dynamic > UpperTriangularType;
HybridNonLinearSolverSpace::Status hybrj1(
FVectorType &x,
const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon())
);
HybridNonLinearSolverSpace::Status solveInit(FVectorType &x);
HybridNonLinearSolverSpace::Status solveOneStep(FVectorType &x);
HybridNonLinearSolverSpace::Status solve(FVectorType &x);
HybridNonLinearSolverSpace::Status hybrd1(
FVectorType &x,
const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon())
);
HybridNonLinearSolverSpace::Status solveNumericalDiffInit(FVectorType &x);
HybridNonLinearSolverSpace::Status solveNumericalDiffOneStep(FVectorType &x);
HybridNonLinearSolverSpace::Status solveNumericalDiff(FVectorType &x);
void resetParameters(void) { parameters = Parameters(); }
Parameters parameters;
FVectorType fvec, qtf, diag;
JacobianType fjac;
UpperTriangularType R;
Index nfev;
Index njev;
Index iter;
Scalar fnorm;
bool useExternalScaling;
private:
FunctorType &functor;
Index n;
Scalar sum;
bool sing;
Scalar temp;
Scalar delta;
bool jeval;
Index ncsuc;
Scalar ratio;
Scalar pnorm, xnorm, fnorm1;
Index nslow1, nslow2;
Index ncfail;
Scalar actred, prered;
FVectorType wa1, wa2, wa3, wa4;
HybridNonLinearSolver& operator=(const HybridNonLinearSolver&);
};
template<typename FunctorType, typename Scalar>
HybridNonLinearSolverSpace::Status
HybridNonLinearSolver<FunctorType,Scalar>::hybrj1(
FVectorType &x,
const Scalar tol
)
{
n = x.size();
/* check the input parameters for errors. */
if (n <= 0 || tol < 0.)
return HybridNonLinearSolverSpace::ImproperInputParameters;
resetParameters();
parameters.maxfev = 100*(n+1);
parameters.xtol = tol;
diag.setConstant(n, 1.);
useExternalScaling = true;
return solve(x);
}
template<typename FunctorType, typename Scalar>
HybridNonLinearSolverSpace::Status
HybridNonLinearSolver<FunctorType,Scalar>::solveInit(FVectorType &x)
{
n = x.size();
wa1.resize(n); wa2.resize(n); wa3.resize(n); wa4.resize(n);
fvec.resize(n);
qtf.resize(n);
fjac.resize(n, n);
if (!useExternalScaling)
diag.resize(n);
eigen_assert( (!useExternalScaling || diag.size()==n) || "When useExternalScaling is set, the caller must provide a valid 'diag'");
/* Function Body */
nfev = 0;
njev = 0;
/* check the input parameters for errors. */
if (n <= 0 || parameters.xtol < 0. || parameters.maxfev <= 0 || parameters.factor <= 0. )
return HybridNonLinearSolverSpace::ImproperInputParameters;
if (useExternalScaling)
for (Index j = 0; j < n; ++j)
if (diag[j] <= 0.)
return HybridNonLinearSolverSpace::ImproperInputParameters;
/* evaluate the function at the starting point */
/* and calculate its norm. */
nfev = 1;
if ( functor(x, fvec) < 0)
return HybridNonLinearSolverSpace::UserAsked;
fnorm = fvec.stableNorm();
/* initialize iteration counter and monitors. */
iter = 1;
ncsuc = 0;
ncfail = 0;
nslow1 = 0;
nslow2 = 0;
return HybridNonLinearSolverSpace::Running;
}
template<typename FunctorType, typename Scalar>
HybridNonLinearSolverSpace::Status
HybridNonLinearSolver<FunctorType,Scalar>::solveOneStep(FVectorType &x)
{
using std::abs;
eigen_assert(x.size()==n); // check the caller is not cheating us
Index j;
std::vector<JacobiRotation<Scalar> > v_givens(n), w_givens(n);
jeval = true;
/* calculate the jacobian matrix. */
if ( functor.df(x, fjac) < 0)
return HybridNonLinearSolverSpace::UserAsked;
++njev;
wa2 = fjac.colwise().blueNorm();
/* on the first iteration and if external scaling is not used, scale according */
/* to the norms of the columns of the initial jacobian. */
if (iter == 1) {
if (!useExternalScaling)
for (j = 0; j < n; ++j)
diag[j] = (wa2[j]==0.) ? 1. : wa2[j];
/* on the first iteration, calculate the norm of the scaled x */
/* and initialize the step bound delta. */
xnorm = diag.cwiseProduct(x).stableNorm();
delta = parameters.factor * xnorm;
if (delta == 0.)
delta = parameters.factor;
}
/* compute the qr factorization of the jacobian. */
HouseholderQR<JacobianType> qrfac(fjac); // no pivoting:
/* copy the triangular factor of the qr factorization into r. */
R = qrfac.matrixQR();
/* accumulate the orthogonal factor in fjac. */
fjac = qrfac.householderQ();
/* form (q transpose)*fvec and store in qtf. */
qtf = fjac.transpose() * fvec;
/* rescale if necessary. */
if (!useExternalScaling)
diag = diag.cwiseMax(wa2);
while (true) {
/* determine the direction p. */
internal::dogleg<Scalar>(R, diag, qtf, delta, wa1);
/* store the direction p and x + p. calculate the norm of p. */
wa1 = -wa1;
wa2 = x + wa1;
pnorm = diag.cwiseProduct(wa1).stableNorm();
/* on the first iteration, adjust the initial step bound. */
if (iter == 1)
delta = (std::min)(delta,pnorm);
/* evaluate the function at x + p and calculate its norm. */
if ( functor(wa2, wa4) < 0)
return HybridNonLinearSolverSpace::UserAsked;
++nfev;
fnorm1 = wa4.stableNorm();
/* compute the scaled actual reduction. */
actred = -1.;
if (fnorm1 < fnorm) /* Computing 2nd power */
actred = 1. - numext::abs2(fnorm1 / fnorm);
/* compute the scaled predicted reduction. */
wa3 = R.template triangularView<Upper>()*wa1 + qtf;
temp = wa3.stableNorm();
prered = 0.;
if (temp < fnorm) /* Computing 2nd power */
prered = 1. - numext::abs2(temp / fnorm);
/* compute the ratio of the actual to the predicted reduction. */
ratio = 0.;
if (prered > 0.)
ratio = actred / prered;
/* update the step bound. */
if (ratio < Scalar(.1)) {
ncsuc = 0;
++ncfail;
delta = Scalar(.5) * delta;
} else {
ncfail = 0;
++ncsuc;
if (ratio >= Scalar(.5) || ncsuc > 1)
delta = (std::max)(delta, pnorm / Scalar(.5));
if (abs(ratio - 1.) <= Scalar(.1)) {
delta = pnorm / Scalar(.5);
}
}
/* test for successful iteration. */
if (ratio >= Scalar(1e-4)) {
/* successful iteration. update x, fvec, and their norms. */
x = wa2;
wa2 = diag.cwiseProduct(x);
fvec = wa4;
xnorm = wa2.stableNorm();
fnorm = fnorm1;
++iter;
}
/* determine the progress of the iteration. */
++nslow1;
if (actred >= Scalar(.001))
nslow1 = 0;
if (jeval)
++nslow2;
if (actred >= Scalar(.1))
nslow2 = 0;
/* test for convergence. */
if (delta <= parameters.xtol * xnorm || fnorm == 0.)
return HybridNonLinearSolverSpace::RelativeErrorTooSmall;
/* tests for termination and stringent tolerances. */
if (nfev >= parameters.maxfev)
return HybridNonLinearSolverSpace::TooManyFunctionEvaluation;
if (Scalar(.1) * (std::max)(Scalar(.1) * delta, pnorm) <= NumTraits<Scalar>::epsilon() * xnorm)
return HybridNonLinearSolverSpace::TolTooSmall;
if (nslow2 == 5)
return HybridNonLinearSolverSpace::NotMakingProgressJacobian;
if (nslow1 == 10)
return HybridNonLinearSolverSpace::NotMakingProgressIterations;
/* criterion for recalculating jacobian. */
if (ncfail == 2)
break; // leave inner loop and go for the next outer loop iteration
/* calculate the rank one modification to the jacobian */
/* and update qtf if necessary. */
wa1 = diag.cwiseProduct( diag.cwiseProduct(wa1)/pnorm );
wa2 = fjac.transpose() * wa4;
if (ratio >= Scalar(1e-4))
qtf = wa2;
wa2 = (wa2-wa3)/pnorm;
/* compute the qr factorization of the updated jacobian. */
internal::r1updt<Scalar>(R, wa1, v_givens, w_givens, wa2, wa3, &sing);
internal::r1mpyq<Scalar>(n, n, fjac.data(), v_givens, w_givens);
internal::r1mpyq<Scalar>(1, n, qtf.data(), v_givens, w_givens);
jeval = false;
}
return HybridNonLinearSolverSpace::Running;
}
template<typename FunctorType, typename Scalar>
HybridNonLinearSolverSpace::Status
HybridNonLinearSolver<FunctorType,Scalar>::solve(FVectorType &x)
{
HybridNonLinearSolverSpace::Status status = solveInit(x);
if (status==HybridNonLinearSolverSpace::ImproperInputParameters)
return status;
while (status==HybridNonLinearSolverSpace::Running)
status = solveOneStep(x);
return status;
}
template<typename FunctorType, typename Scalar>
HybridNonLinearSolverSpace::Status
HybridNonLinearSolver<FunctorType,Scalar>::hybrd1(
FVectorType &x,
const Scalar tol
)
{
n = x.size();
/* check the input parameters for errors. */
if (n <= 0 || tol < 0.)
return HybridNonLinearSolverSpace::ImproperInputParameters;
resetParameters();
parameters.maxfev = 200*(n+1);
parameters.xtol = tol;
diag.setConstant(n, 1.);
useExternalScaling = true;
return solveNumericalDiff(x);
}
template<typename FunctorType, typename Scalar>
HybridNonLinearSolverSpace::Status
HybridNonLinearSolver<FunctorType,Scalar>::solveNumericalDiffInit(FVectorType &x)
{
n = x.size();
if (parameters.nb_of_subdiagonals<0) parameters.nb_of_subdiagonals= n-1;
if (parameters.nb_of_superdiagonals<0) parameters.nb_of_superdiagonals= n-1;
wa1.resize(n); wa2.resize(n); wa3.resize(n); wa4.resize(n);
qtf.resize(n);
fjac.resize(n, n);
fvec.resize(n);
if (!useExternalScaling)
diag.resize(n);
eigen_assert( (!useExternalScaling || diag.size()==n) || "When useExternalScaling is set, the caller must provide a valid 'diag'");
/* Function Body */
nfev = 0;
njev = 0;
/* check the input parameters for errors. */
if (n <= 0 || parameters.xtol < 0. || parameters.maxfev <= 0 || parameters.nb_of_subdiagonals< 0 || parameters.nb_of_superdiagonals< 0 || parameters.factor <= 0. )
return HybridNonLinearSolverSpace::ImproperInputParameters;
if (useExternalScaling)
for (Index j = 0; j < n; ++j)
if (diag[j] <= 0.)
return HybridNonLinearSolverSpace::ImproperInputParameters;
/* evaluate the function at the starting point */
/* and calculate its norm. */
nfev = 1;
if ( functor(x, fvec) < 0)
return HybridNonLinearSolverSpace::UserAsked;
fnorm = fvec.stableNorm();
/* initialize iteration counter and monitors. */
iter = 1;
ncsuc = 0;
ncfail = 0;
nslow1 = 0;
nslow2 = 0;
return HybridNonLinearSolverSpace::Running;
}
template<typename FunctorType, typename Scalar>
HybridNonLinearSolverSpace::Status
HybridNonLinearSolver<FunctorType,Scalar>::solveNumericalDiffOneStep(FVectorType &x)
{
using std::sqrt;
using std::abs;
assert(x.size()==n); // check the caller is not cheating us
Index j;
std::vector<JacobiRotation<Scalar> > v_givens(n), w_givens(n);
jeval = true;
if (parameters.nb_of_subdiagonals<0) parameters.nb_of_subdiagonals= n-1;
if (parameters.nb_of_superdiagonals<0) parameters.nb_of_superdiagonals= n-1;
/* calculate the jacobian matrix. */
if (internal::fdjac1(functor, x, fvec, fjac, parameters.nb_of_subdiagonals, parameters.nb_of_superdiagonals, parameters.epsfcn) <0)
return HybridNonLinearSolverSpace::UserAsked;
nfev += (std::min)(parameters.nb_of_subdiagonals+parameters.nb_of_superdiagonals+ 1, n);
wa2 = fjac.colwise().blueNorm();
/* on the first iteration and if external scaling is not used, scale according */
/* to the norms of the columns of the initial jacobian. */
if (iter == 1) {
if (!useExternalScaling)
for (j = 0; j < n; ++j)
diag[j] = (wa2[j]==0.) ? 1. : wa2[j];
/* on the first iteration, calculate the norm of the scaled x */
/* and initialize the step bound delta. */
xnorm = diag.cwiseProduct(x).stableNorm();
delta = parameters.factor * xnorm;
if (delta == 0.)
delta = parameters.factor;
}
/* compute the qr factorization of the jacobian. */
HouseholderQR<JacobianType> qrfac(fjac); // no pivoting:
/* copy the triangular factor of the qr factorization into r. */
R = qrfac.matrixQR();
/* accumulate the orthogonal factor in fjac. */
fjac = qrfac.householderQ();
/* form (q transpose)*fvec and store in qtf. */
qtf = fjac.transpose() * fvec;
/* rescale if necessary. */
if (!useExternalScaling)
diag = diag.cwiseMax(wa2);
while (true) {
/* determine the direction p. */
internal::dogleg<Scalar>(R, diag, qtf, delta, wa1);
/* store the direction p and x + p. calculate the norm of p. */
wa1 = -wa1;
wa2 = x + wa1;
pnorm = diag.cwiseProduct(wa1).stableNorm();
/* on the first iteration, adjust the initial step bound. */
if (iter == 1)
delta = (std::min)(delta,pnorm);
/* evaluate the function at x + p and calculate its norm. */
if ( functor(wa2, wa4) < 0)
return HybridNonLinearSolverSpace::UserAsked;
++nfev;
fnorm1 = wa4.stableNorm();
/* compute the scaled actual reduction. */
actred = -1.;
if (fnorm1 < fnorm) /* Computing 2nd power */
actred = 1. - numext::abs2(fnorm1 / fnorm);
/* compute the scaled predicted reduction. */
wa3 = R.template triangularView<Upper>()*wa1 + qtf;
temp = wa3.stableNorm();
prered = 0.;
if (temp < fnorm) /* Computing 2nd power */
prered = 1. - numext::abs2(temp / fnorm);
/* compute the ratio of the actual to the predicted reduction. */
ratio = 0.;
if (prered > 0.)
ratio = actred / prered;
/* update the step bound. */
if (ratio < Scalar(.1)) {
ncsuc = 0;
++ncfail;
delta = Scalar(.5) * delta;
} else {
ncfail = 0;
++ncsuc;
if (ratio >= Scalar(.5) || ncsuc > 1)
delta = (std::max)(delta, pnorm / Scalar(.5));
if (abs(ratio - 1.) <= Scalar(.1)) {
delta = pnorm / Scalar(.5);
}
}
/* test for successful iteration. */
if (ratio >= Scalar(1e-4)) {
/* successful iteration. update x, fvec, and their norms. */
x = wa2;
wa2 = diag.cwiseProduct(x);
fvec = wa4;
xnorm = wa2.stableNorm();
fnorm = fnorm1;
++iter;
}
/* determine the progress of the iteration. */
++nslow1;
if (actred >= Scalar(.001))
nslow1 = 0;
if (jeval)
++nslow2;
if (actred >= Scalar(.1))
nslow2 = 0;
/* test for convergence. */
if (delta <= parameters.xtol * xnorm || fnorm == 0.)
return HybridNonLinearSolverSpace::RelativeErrorTooSmall;
/* tests for termination and stringent tolerances. */
if (nfev >= parameters.maxfev)
return HybridNonLinearSolverSpace::TooManyFunctionEvaluation;
if (Scalar(.1) * (std::max)(Scalar(.1) * delta, pnorm) <= NumTraits<Scalar>::epsilon() * xnorm)
return HybridNonLinearSolverSpace::TolTooSmall;
if (nslow2 == 5)
return HybridNonLinearSolverSpace::NotMakingProgressJacobian;
if (nslow1 == 10)
return HybridNonLinearSolverSpace::NotMakingProgressIterations;
/* criterion for recalculating jacobian. */
if (ncfail == 2)
break; // leave inner loop and go for the next outer loop iteration
/* calculate the rank one modification to the jacobian */
/* and update qtf if necessary. */
wa1 = diag.cwiseProduct( diag.cwiseProduct(wa1)/pnorm );
wa2 = fjac.transpose() * wa4;
if (ratio >= Scalar(1e-4))
qtf = wa2;
wa2 = (wa2-wa3)/pnorm;
/* compute the qr factorization of the updated jacobian. */
internal::r1updt<Scalar>(R, wa1, v_givens, w_givens, wa2, wa3, &sing);
internal::r1mpyq<Scalar>(n, n, fjac.data(), v_givens, w_givens);
internal::r1mpyq<Scalar>(1, n, qtf.data(), v_givens, w_givens);
jeval = false;
}
return HybridNonLinearSolverSpace::Running;
}
template<typename FunctorType, typename Scalar>
HybridNonLinearSolverSpace::Status
HybridNonLinearSolver<FunctorType,Scalar>::solveNumericalDiff(FVectorType &x)
{
HybridNonLinearSolverSpace::Status status = solveNumericalDiffInit(x);
if (status==HybridNonLinearSolverSpace::ImproperInputParameters)
return status;
while (status==HybridNonLinearSolverSpace::Running)
status = solveNumericalDiffOneStep(x);
return status;
}
} // end namespace Eigen
#endif // EIGEN_HYBRIDNONLINEARSOLVER_H
//vim: ai ts=4 sts=4 et sw=4

View File

@@ -0,0 +1,650 @@
// -*- coding: utf-8
// vim: set fileencoding=utf-8
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Thomas Capricelli <orzel@freehackers.org>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_LEVENBERGMARQUARDT__H
#define EIGEN_LEVENBERGMARQUARDT__H
namespace Eigen {
namespace LevenbergMarquardtSpace {
enum Status {
NotStarted = -2,
Running = -1,
ImproperInputParameters = 0,
RelativeReductionTooSmall = 1,
RelativeErrorTooSmall = 2,
RelativeErrorAndReductionTooSmall = 3,
CosinusTooSmall = 4,
TooManyFunctionEvaluation = 5,
FtolTooSmall = 6,
XtolTooSmall = 7,
GtolTooSmall = 8,
UserAsked = 9
};
}
/**
* \ingroup NonLinearOptimization_Module
* \brief Performs non linear optimization over a non-linear function,
* using a variant of the Levenberg Marquardt algorithm.
*
* Check wikipedia for more information.
* http://en.wikipedia.org/wiki/Levenberg%E2%80%93Marquardt_algorithm
*/
template<typename FunctorType, typename Scalar=double>
class LevenbergMarquardt
{
public:
LevenbergMarquardt(FunctorType &_functor)
: functor(_functor) { nfev = njev = iter = 0; fnorm = gnorm = 0.; useExternalScaling=false; }
typedef DenseIndex Index;
struct Parameters {
Parameters()
: factor(Scalar(100.))
, maxfev(400)
, ftol(std::sqrt(NumTraits<Scalar>::epsilon()))
, xtol(std::sqrt(NumTraits<Scalar>::epsilon()))
, gtol(Scalar(0.))
, epsfcn(Scalar(0.)) {}
Scalar factor;
Index maxfev; // maximum number of function evaluation
Scalar ftol;
Scalar xtol;
Scalar gtol;
Scalar epsfcn;
};
typedef Matrix< Scalar, Dynamic, 1 > FVectorType;
typedef Matrix< Scalar, Dynamic, Dynamic > JacobianType;
LevenbergMarquardtSpace::Status lmder1(
FVectorType &x,
const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon())
);
LevenbergMarquardtSpace::Status minimize(FVectorType &x);
LevenbergMarquardtSpace::Status minimizeInit(FVectorType &x);
LevenbergMarquardtSpace::Status minimizeOneStep(FVectorType &x);
static LevenbergMarquardtSpace::Status lmdif1(
FunctorType &functor,
FVectorType &x,
Index *nfev,
const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon())
);
LevenbergMarquardtSpace::Status lmstr1(
FVectorType &x,
const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon())
);
LevenbergMarquardtSpace::Status minimizeOptimumStorage(FVectorType &x);
LevenbergMarquardtSpace::Status minimizeOptimumStorageInit(FVectorType &x);
LevenbergMarquardtSpace::Status minimizeOptimumStorageOneStep(FVectorType &x);
void resetParameters(void) { parameters = Parameters(); }
Parameters parameters;
FVectorType fvec, qtf, diag;
JacobianType fjac;
PermutationMatrix<Dynamic,Dynamic> permutation;
Index nfev;
Index njev;
Index iter;
Scalar fnorm, gnorm;
bool useExternalScaling;
Scalar lm_param(void) { return par; }
private:
FunctorType &functor;
Index n;
Index m;
FVectorType wa1, wa2, wa3, wa4;
Scalar par, sum;
Scalar temp, temp1, temp2;
Scalar delta;
Scalar ratio;
Scalar pnorm, xnorm, fnorm1, actred, dirder, prered;
LevenbergMarquardt& operator=(const LevenbergMarquardt&);
};
template<typename FunctorType, typename Scalar>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType,Scalar>::lmder1(
FVectorType &x,
const Scalar tol
)
{
n = x.size();
m = functor.values();
/* check the input parameters for errors. */
if (n <= 0 || m < n || tol < 0.)
return LevenbergMarquardtSpace::ImproperInputParameters;
resetParameters();
parameters.ftol = tol;
parameters.xtol = tol;
parameters.maxfev = 100*(n+1);
return minimize(x);
}
template<typename FunctorType, typename Scalar>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType,Scalar>::minimize(FVectorType &x)
{
LevenbergMarquardtSpace::Status status = minimizeInit(x);
if (status==LevenbergMarquardtSpace::ImproperInputParameters)
return status;
do {
status = minimizeOneStep(x);
} while (status==LevenbergMarquardtSpace::Running);
return status;
}
template<typename FunctorType, typename Scalar>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType,Scalar>::minimizeInit(FVectorType &x)
{
n = x.size();
m = functor.values();
wa1.resize(n); wa2.resize(n); wa3.resize(n);
wa4.resize(m);
fvec.resize(m);
fjac.resize(m, n);
if (!useExternalScaling)
diag.resize(n);
eigen_assert( (!useExternalScaling || diag.size()==n) || "When useExternalScaling is set, the caller must provide a valid 'diag'");
qtf.resize(n);
/* Function Body */
nfev = 0;
njev = 0;
/* check the input parameters for errors. */
if (n <= 0 || m < n || parameters.ftol < 0. || parameters.xtol < 0. || parameters.gtol < 0. || parameters.maxfev <= 0 || parameters.factor <= 0.)
return LevenbergMarquardtSpace::ImproperInputParameters;
if (useExternalScaling)
for (Index j = 0; j < n; ++j)
if (diag[j] <= 0.)
return LevenbergMarquardtSpace::ImproperInputParameters;
/* evaluate the function at the starting point */
/* and calculate its norm. */
nfev = 1;
if ( functor(x, fvec) < 0)
return LevenbergMarquardtSpace::UserAsked;
fnorm = fvec.stableNorm();
/* initialize levenberg-marquardt parameter and iteration counter. */
par = 0.;
iter = 1;
return LevenbergMarquardtSpace::NotStarted;
}
template<typename FunctorType, typename Scalar>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType,Scalar>::minimizeOneStep(FVectorType &x)
{
using std::abs;
using std::sqrt;
eigen_assert(x.size()==n); // check the caller is not cheating us
/* calculate the jacobian matrix. */
Index df_ret = functor.df(x, fjac);
if (df_ret<0)
return LevenbergMarquardtSpace::UserAsked;
if (df_ret>0)
// numerical diff, we evaluated the function df_ret times
nfev += df_ret;
else njev++;
/* compute the qr factorization of the jacobian. */
wa2 = fjac.colwise().blueNorm();
ColPivHouseholderQR<JacobianType> qrfac(fjac);
fjac = qrfac.matrixQR();
permutation = qrfac.colsPermutation();
/* on the first iteration and if external scaling is not used, scale according */
/* to the norms of the columns of the initial jacobian. */
if (iter == 1) {
if (!useExternalScaling)
for (Index j = 0; j < n; ++j)
diag[j] = (wa2[j]==0.)? 1. : wa2[j];
/* on the first iteration, calculate the norm of the scaled x */
/* and initialize the step bound delta. */
xnorm = diag.cwiseProduct(x).stableNorm();
delta = parameters.factor * xnorm;
if (delta == 0.)
delta = parameters.factor;
}
/* form (q transpose)*fvec and store the first n components in */
/* qtf. */
wa4 = fvec;
wa4.applyOnTheLeft(qrfac.householderQ().adjoint());
qtf = wa4.head(n);
/* compute the norm of the scaled gradient. */
gnorm = 0.;
if (fnorm != 0.)
for (Index j = 0; j < n; ++j)
if (wa2[permutation.indices()[j]] != 0.)
gnorm = (std::max)(gnorm, abs( fjac.col(j).head(j+1).dot(qtf.head(j+1)/fnorm) / wa2[permutation.indices()[j]]));
/* test for convergence of the gradient norm. */
if (gnorm <= parameters.gtol)
return LevenbergMarquardtSpace::CosinusTooSmall;
/* rescale if necessary. */
if (!useExternalScaling)
diag = diag.cwiseMax(wa2);
do {
/* determine the levenberg-marquardt parameter. */
internal::lmpar2<Scalar>(qrfac, diag, qtf, delta, par, wa1);
/* store the direction p and x + p. calculate the norm of p. */
wa1 = -wa1;
wa2 = x + wa1;
pnorm = diag.cwiseProduct(wa1).stableNorm();
/* on the first iteration, adjust the initial step bound. */
if (iter == 1)
delta = (std::min)(delta,pnorm);
/* evaluate the function at x + p and calculate its norm. */
if ( functor(wa2, wa4) < 0)
return LevenbergMarquardtSpace::UserAsked;
++nfev;
fnorm1 = wa4.stableNorm();
/* compute the scaled actual reduction. */
actred = -1.;
if (Scalar(.1) * fnorm1 < fnorm)
actred = 1. - numext::abs2(fnorm1 / fnorm);
/* compute the scaled predicted reduction and */
/* the scaled directional derivative. */
wa3 = fjac.template triangularView<Upper>() * (qrfac.colsPermutation().inverse() *wa1);
temp1 = numext::abs2(wa3.stableNorm() / fnorm);
temp2 = numext::abs2(sqrt(par) * pnorm / fnorm);
prered = temp1 + temp2 / Scalar(.5);
dirder = -(temp1 + temp2);
/* compute the ratio of the actual to the predicted */
/* reduction. */
ratio = 0.;
if (prered != 0.)
ratio = actred / prered;
/* update the step bound. */
if (ratio <= Scalar(.25)) {
if (actred >= 0.)
temp = Scalar(.5);
if (actred < 0.)
temp = Scalar(.5) * dirder / (dirder + Scalar(.5) * actred);
if (Scalar(.1) * fnorm1 >= fnorm || temp < Scalar(.1))
temp = Scalar(.1);
/* Computing MIN */
delta = temp * (std::min)(delta, pnorm / Scalar(.1));
par /= temp;
} else if (!(par != 0. && ratio < Scalar(.75))) {
delta = pnorm / Scalar(.5);
par = Scalar(.5) * par;
}
/* test for successful iteration. */
if (ratio >= Scalar(1e-4)) {
/* successful iteration. update x, fvec, and their norms. */
x = wa2;
wa2 = diag.cwiseProduct(x);
fvec = wa4;
xnorm = wa2.stableNorm();
fnorm = fnorm1;
++iter;
}
/* tests for convergence. */
if (abs(actred) <= parameters.ftol && prered <= parameters.ftol && Scalar(.5) * ratio <= 1. && delta <= parameters.xtol * xnorm)
return LevenbergMarquardtSpace::RelativeErrorAndReductionTooSmall;
if (abs(actred) <= parameters.ftol && prered <= parameters.ftol && Scalar(.5) * ratio <= 1.)
return LevenbergMarquardtSpace::RelativeReductionTooSmall;
if (delta <= parameters.xtol * xnorm)
return LevenbergMarquardtSpace::RelativeErrorTooSmall;
/* tests for termination and stringent tolerances. */
if (nfev >= parameters.maxfev)
return LevenbergMarquardtSpace::TooManyFunctionEvaluation;
if (abs(actred) <= NumTraits<Scalar>::epsilon() && prered <= NumTraits<Scalar>::epsilon() && Scalar(.5) * ratio <= 1.)
return LevenbergMarquardtSpace::FtolTooSmall;
if (delta <= NumTraits<Scalar>::epsilon() * xnorm)
return LevenbergMarquardtSpace::XtolTooSmall;
if (gnorm <= NumTraits<Scalar>::epsilon())
return LevenbergMarquardtSpace::GtolTooSmall;
} while (ratio < Scalar(1e-4));
return LevenbergMarquardtSpace::Running;
}
template<typename FunctorType, typename Scalar>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType,Scalar>::lmstr1(
FVectorType &x,
const Scalar tol
)
{
n = x.size();
m = functor.values();
/* check the input parameters for errors. */
if (n <= 0 || m < n || tol < 0.)
return LevenbergMarquardtSpace::ImproperInputParameters;
resetParameters();
parameters.ftol = tol;
parameters.xtol = tol;
parameters.maxfev = 100*(n+1);
return minimizeOptimumStorage(x);
}
template<typename FunctorType, typename Scalar>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType,Scalar>::minimizeOptimumStorageInit(FVectorType &x)
{
n = x.size();
m = functor.values();
wa1.resize(n); wa2.resize(n); wa3.resize(n);
wa4.resize(m);
fvec.resize(m);
// Only R is stored in fjac. Q is only used to compute 'qtf', which is
// Q.transpose()*rhs. qtf will be updated using givens rotation,
// instead of storing them in Q.
// The purpose it to only use a nxn matrix, instead of mxn here, so
// that we can handle cases where m>>n :
fjac.resize(n, n);
if (!useExternalScaling)
diag.resize(n);
eigen_assert( (!useExternalScaling || diag.size()==n) || "When useExternalScaling is set, the caller must provide a valid 'diag'");
qtf.resize(n);
/* Function Body */
nfev = 0;
njev = 0;
/* check the input parameters for errors. */
if (n <= 0 || m < n || parameters.ftol < 0. || parameters.xtol < 0. || parameters.gtol < 0. || parameters.maxfev <= 0 || parameters.factor <= 0.)
return LevenbergMarquardtSpace::ImproperInputParameters;
if (useExternalScaling)
for (Index j = 0; j < n; ++j)
if (diag[j] <= 0.)
return LevenbergMarquardtSpace::ImproperInputParameters;
/* evaluate the function at the starting point */
/* and calculate its norm. */
nfev = 1;
if ( functor(x, fvec) < 0)
return LevenbergMarquardtSpace::UserAsked;
fnorm = fvec.stableNorm();
/* initialize levenberg-marquardt parameter and iteration counter. */
par = 0.;
iter = 1;
return LevenbergMarquardtSpace::NotStarted;
}
template<typename FunctorType, typename Scalar>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType,Scalar>::minimizeOptimumStorageOneStep(FVectorType &x)
{
using std::abs;
using std::sqrt;
eigen_assert(x.size()==n); // check the caller is not cheating us
Index i, j;
bool sing;
/* compute the qr factorization of the jacobian matrix */
/* calculated one row at a time, while simultaneously */
/* forming (q transpose)*fvec and storing the first */
/* n components in qtf. */
qtf.fill(0.);
fjac.fill(0.);
Index rownb = 2;
for (i = 0; i < m; ++i) {
if (functor.df(x, wa3, rownb) < 0) return LevenbergMarquardtSpace::UserAsked;
internal::rwupdt<Scalar>(fjac, wa3, qtf, fvec[i]);
++rownb;
}
++njev;
/* if the jacobian is rank deficient, call qrfac to */
/* reorder its columns and update the components of qtf. */
sing = false;
for (j = 0; j < n; ++j) {
if (fjac(j,j) == 0.)
sing = true;
wa2[j] = fjac.col(j).head(j).stableNorm();
}
permutation.setIdentity(n);
if (sing) {
wa2 = fjac.colwise().blueNorm();
// TODO We have no unit test covering this code path, do not modify
// until it is carefully tested
ColPivHouseholderQR<JacobianType> qrfac(fjac);
fjac = qrfac.matrixQR();
wa1 = fjac.diagonal();
fjac.diagonal() = qrfac.hCoeffs();
permutation = qrfac.colsPermutation();
// TODO : avoid this:
for(Index ii=0; ii< fjac.cols(); ii++) fjac.col(ii).segment(ii+1, fjac.rows()-ii-1) *= fjac(ii,ii); // rescale vectors
for (j = 0; j < n; ++j) {
if (fjac(j,j) != 0.) {
sum = 0.;
for (i = j; i < n; ++i)
sum += fjac(i,j) * qtf[i];
temp = -sum / fjac(j,j);
for (i = j; i < n; ++i)
qtf[i] += fjac(i,j) * temp;
}
fjac(j,j) = wa1[j];
}
}
/* on the first iteration and if external scaling is not used, scale according */
/* to the norms of the columns of the initial jacobian. */
if (iter == 1) {
if (!useExternalScaling)
for (j = 0; j < n; ++j)
diag[j] = (wa2[j]==0.)? 1. : wa2[j];
/* on the first iteration, calculate the norm of the scaled x */
/* and initialize the step bound delta. */
xnorm = diag.cwiseProduct(x).stableNorm();
delta = parameters.factor * xnorm;
if (delta == 0.)
delta = parameters.factor;
}
/* compute the norm of the scaled gradient. */
gnorm = 0.;
if (fnorm != 0.)
for (j = 0; j < n; ++j)
if (wa2[permutation.indices()[j]] != 0.)
gnorm = (std::max)(gnorm, abs( fjac.col(j).head(j+1).dot(qtf.head(j+1)/fnorm) / wa2[permutation.indices()[j]]));
/* test for convergence of the gradient norm. */
if (gnorm <= parameters.gtol)
return LevenbergMarquardtSpace::CosinusTooSmall;
/* rescale if necessary. */
if (!useExternalScaling)
diag = diag.cwiseMax(wa2);
do {
/* determine the levenberg-marquardt parameter. */
internal::lmpar<Scalar>(fjac, permutation.indices(), diag, qtf, delta, par, wa1);
/* store the direction p and x + p. calculate the norm of p. */
wa1 = -wa1;
wa2 = x + wa1;
pnorm = diag.cwiseProduct(wa1).stableNorm();
/* on the first iteration, adjust the initial step bound. */
if (iter == 1)
delta = (std::min)(delta,pnorm);
/* evaluate the function at x + p and calculate its norm. */
if ( functor(wa2, wa4) < 0)
return LevenbergMarquardtSpace::UserAsked;
++nfev;
fnorm1 = wa4.stableNorm();
/* compute the scaled actual reduction. */
actred = -1.;
if (Scalar(.1) * fnorm1 < fnorm)
actred = 1. - numext::abs2(fnorm1 / fnorm);
/* compute the scaled predicted reduction and */
/* the scaled directional derivative. */
wa3 = fjac.topLeftCorner(n,n).template triangularView<Upper>() * (permutation.inverse() * wa1);
temp1 = numext::abs2(wa3.stableNorm() / fnorm);
temp2 = numext::abs2(sqrt(par) * pnorm / fnorm);
prered = temp1 + temp2 / Scalar(.5);
dirder = -(temp1 + temp2);
/* compute the ratio of the actual to the predicted */
/* reduction. */
ratio = 0.;
if (prered != 0.)
ratio = actred / prered;
/* update the step bound. */
if (ratio <= Scalar(.25)) {
if (actred >= 0.)
temp = Scalar(.5);
if (actred < 0.)
temp = Scalar(.5) * dirder / (dirder + Scalar(.5) * actred);
if (Scalar(.1) * fnorm1 >= fnorm || temp < Scalar(.1))
temp = Scalar(.1);
/* Computing MIN */
delta = temp * (std::min)(delta, pnorm / Scalar(.1));
par /= temp;
} else if (!(par != 0. && ratio < Scalar(.75))) {
delta = pnorm / Scalar(.5);
par = Scalar(.5) * par;
}
/* test for successful iteration. */
if (ratio >= Scalar(1e-4)) {
/* successful iteration. update x, fvec, and their norms. */
x = wa2;
wa2 = diag.cwiseProduct(x);
fvec = wa4;
xnorm = wa2.stableNorm();
fnorm = fnorm1;
++iter;
}
/* tests for convergence. */
if (abs(actred) <= parameters.ftol && prered <= parameters.ftol && Scalar(.5) * ratio <= 1. && delta <= parameters.xtol * xnorm)
return LevenbergMarquardtSpace::RelativeErrorAndReductionTooSmall;
if (abs(actred) <= parameters.ftol && prered <= parameters.ftol && Scalar(.5) * ratio <= 1.)
return LevenbergMarquardtSpace::RelativeReductionTooSmall;
if (delta <= parameters.xtol * xnorm)
return LevenbergMarquardtSpace::RelativeErrorTooSmall;
/* tests for termination and stringent tolerances. */
if (nfev >= parameters.maxfev)
return LevenbergMarquardtSpace::TooManyFunctionEvaluation;
if (abs(actred) <= NumTraits<Scalar>::epsilon() && prered <= NumTraits<Scalar>::epsilon() && Scalar(.5) * ratio <= 1.)
return LevenbergMarquardtSpace::FtolTooSmall;
if (delta <= NumTraits<Scalar>::epsilon() * xnorm)
return LevenbergMarquardtSpace::XtolTooSmall;
if (gnorm <= NumTraits<Scalar>::epsilon())
return LevenbergMarquardtSpace::GtolTooSmall;
} while (ratio < Scalar(1e-4));
return LevenbergMarquardtSpace::Running;
}
template<typename FunctorType, typename Scalar>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType,Scalar>::minimizeOptimumStorage(FVectorType &x)
{
LevenbergMarquardtSpace::Status status = minimizeOptimumStorageInit(x);
if (status==LevenbergMarquardtSpace::ImproperInputParameters)
return status;
do {
status = minimizeOptimumStorageOneStep(x);
} while (status==LevenbergMarquardtSpace::Running);
return status;
}
template<typename FunctorType, typename Scalar>
LevenbergMarquardtSpace::Status
LevenbergMarquardt<FunctorType,Scalar>::lmdif1(
FunctorType &functor,
FVectorType &x,
Index *nfev,
const Scalar tol
)
{
Index n = x.size();
Index m = functor.values();
/* check the input parameters for errors. */
if (n <= 0 || m < n || tol < 0.)
return LevenbergMarquardtSpace::ImproperInputParameters;
NumericalDiff<FunctorType> numDiff(functor);
// embedded LevenbergMarquardt
LevenbergMarquardt<NumericalDiff<FunctorType>, Scalar > lm(numDiff);
lm.parameters.ftol = tol;
lm.parameters.xtol = tol;
lm.parameters.maxfev = 200*(n+1);
LevenbergMarquardtSpace::Status info = LevenbergMarquardtSpace::Status(lm.minimize(x));
if (nfev)
* nfev = lm.nfev;
return info;
}
} // end namespace Eigen
#endif // EIGEN_LEVENBERGMARQUARDT__H
//vim: ai ts=4 sts=4 et sw=4

View File

@@ -0,0 +1,66 @@
#define chkder_log10e 0.43429448190325182765
#define chkder_factor 100.
namespace Eigen {
namespace internal {
template<typename Scalar>
void chkder(
const Matrix< Scalar, Dynamic, 1 > &x,
const Matrix< Scalar, Dynamic, 1 > &fvec,
const Matrix< Scalar, Dynamic, Dynamic > &fjac,
Matrix< Scalar, Dynamic, 1 > &xp,
const Matrix< Scalar, Dynamic, 1 > &fvecp,
int mode,
Matrix< Scalar, Dynamic, 1 > &err
)
{
using std::sqrt;
using std::abs;
using std::log;
typedef DenseIndex Index;
const Scalar eps = sqrt(NumTraits<Scalar>::epsilon());
const Scalar epsf = chkder_factor * NumTraits<Scalar>::epsilon();
const Scalar epslog = chkder_log10e * log(eps);
Scalar temp;
const Index m = fvec.size(), n = x.size();
if (mode != 2) {
/* mode = 1. */
xp.resize(n);
for (Index j = 0; j < n; ++j) {
temp = eps * abs(x[j]);
if (temp == 0.)
temp = eps;
xp[j] = x[j] + temp;
}
}
else {
/* mode = 2. */
err.setZero(m);
for (Index j = 0; j < n; ++j) {
temp = abs(x[j]);
if (temp == 0.)
temp = 1.;
err += temp * fjac.col(j);
}
for (Index i = 0; i < m; ++i) {
temp = 1.;
if (fvec[i] != 0. && fvecp[i] != 0. && abs(fvecp[i] - fvec[i]) >= epsf * abs(fvec[i]))
temp = eps * abs((fvecp[i] - fvec[i]) / eps - err[i]) / (abs(fvec[i]) + abs(fvecp[i]));
err[i] = 1.;
if (temp > NumTraits<Scalar>::epsilon() && temp < eps)
err[i] = (chkder_log10e * log(temp) - epslog) / epslog;
if (temp >= eps)
err[i] = 0.;
}
}
}
} // end namespace internal
} // end namespace Eigen

View File

@@ -0,0 +1,70 @@
namespace Eigen {
namespace internal {
template <typename Scalar>
void covar(
Matrix< Scalar, Dynamic, Dynamic > &r,
const VectorXi &ipvt,
Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon()) )
{
using std::abs;
typedef DenseIndex Index;
/* Local variables */
Index i, j, k, l, ii, jj;
bool sing;
Scalar temp;
/* Function Body */
const Index n = r.cols();
const Scalar tolr = tol * abs(r(0,0));
Matrix< Scalar, Dynamic, 1 > wa(n);
eigen_assert(ipvt.size()==n);
/* form the inverse of r in the full upper triangle of r. */
l = -1;
for (k = 0; k < n; ++k)
if (abs(r(k,k)) > tolr) {
r(k,k) = 1. / r(k,k);
for (j = 0; j <= k-1; ++j) {
temp = r(k,k) * r(j,k);
r(j,k) = 0.;
r.col(k).head(j+1) -= r.col(j).head(j+1) * temp;
}
l = k;
}
/* form the full upper triangle of the inverse of (r transpose)*r */
/* in the full upper triangle of r. */
for (k = 0; k <= l; ++k) {
for (j = 0; j <= k-1; ++j)
r.col(j).head(j+1) += r.col(k).head(j+1) * r(j,k);
r.col(k).head(k+1) *= r(k,k);
}
/* form the full lower triangle of the covariance matrix */
/* in the strict lower triangle of r and in wa. */
for (j = 0; j < n; ++j) {
jj = ipvt[j];
sing = j > l;
for (i = 0; i <= j; ++i) {
if (sing)
r(i,j) = 0.;
ii = ipvt[i];
if (ii > jj)
r(ii,jj) = r(i,j);
if (ii < jj)
r(jj,ii) = r(i,j);
}
wa[jj] = r(j,j);
}
/* symmetrize the covariance matrix in r. */
r.topLeftCorner(n,n).template triangularView<StrictlyUpper>() = r.topLeftCorner(n,n).transpose();
r.diagonal() = wa;
}
} // end namespace internal
} // end namespace Eigen

View File

@@ -0,0 +1,107 @@
namespace Eigen {
namespace internal {
template <typename Scalar>
void dogleg(
const Matrix< Scalar, Dynamic, Dynamic > &qrfac,
const Matrix< Scalar, Dynamic, 1 > &diag,
const Matrix< Scalar, Dynamic, 1 > &qtb,
Scalar delta,
Matrix< Scalar, Dynamic, 1 > &x)
{
using std::abs;
using std::sqrt;
typedef DenseIndex Index;
/* Local variables */
Index i, j;
Scalar sum, temp, alpha, bnorm;
Scalar gnorm, qnorm;
Scalar sgnorm;
/* Function Body */
const Scalar epsmch = NumTraits<Scalar>::epsilon();
const Index n = qrfac.cols();
eigen_assert(n==qtb.size());
eigen_assert(n==x.size());
eigen_assert(n==diag.size());
Matrix< Scalar, Dynamic, 1 > wa1(n), wa2(n);
/* first, calculate the gauss-newton direction. */
for (j = n-1; j >=0; --j) {
temp = qrfac(j,j);
if (temp == 0.) {
temp = epsmch * qrfac.col(j).head(j+1).maxCoeff();
if (temp == 0.)
temp = epsmch;
}
if (j==n-1)
x[j] = qtb[j] / temp;
else
x[j] = (qtb[j] - qrfac.row(j).tail(n-j-1).dot(x.tail(n-j-1))) / temp;
}
/* test whether the gauss-newton direction is acceptable. */
qnorm = diag.cwiseProduct(x).stableNorm();
if (qnorm <= delta)
return;
// TODO : this path is not tested by Eigen unit tests
/* the gauss-newton direction is not acceptable. */
/* next, calculate the scaled gradient direction. */
wa1.fill(0.);
for (j = 0; j < n; ++j) {
wa1.tail(n-j) += qrfac.row(j).tail(n-j) * qtb[j];
wa1[j] /= diag[j];
}
/* calculate the norm of the scaled gradient and test for */
/* the special case in which the scaled gradient is zero. */
gnorm = wa1.stableNorm();
sgnorm = 0.;
alpha = delta / qnorm;
if (gnorm == 0.)
goto algo_end;
/* calculate the point along the scaled gradient */
/* at which the quadratic is minimized. */
wa1.array() /= (diag*gnorm).array();
// TODO : once unit tests cover this part,:
// wa2 = qrfac.template triangularView<Upper>() * wa1;
for (j = 0; j < n; ++j) {
sum = 0.;
for (i = j; i < n; ++i) {
sum += qrfac(j,i) * wa1[i];
}
wa2[j] = sum;
}
temp = wa2.stableNorm();
sgnorm = gnorm / temp / temp;
/* test whether the scaled gradient direction is acceptable. */
alpha = 0.;
if (sgnorm >= delta)
goto algo_end;
/* the scaled gradient direction is not acceptable. */
/* finally, calculate the point along the dogleg */
/* at which the quadratic is minimized. */
bnorm = qtb.stableNorm();
temp = bnorm / gnorm * (bnorm / qnorm) * (sgnorm / delta);
temp = temp - delta / qnorm * numext::abs2(sgnorm / delta) + sqrt(numext::abs2(temp - delta / qnorm) + (1.-numext::abs2(delta / qnorm)) * (1.-numext::abs2(sgnorm / delta)));
alpha = delta / qnorm * (1. - numext::abs2(sgnorm / delta)) / temp;
algo_end:
/* form appropriate convex combination of the gauss-newton */
/* direction and the scaled gradient direction. */
temp = (1.-alpha) * (std::min)(sgnorm,delta);
x = temp * wa1 + alpha * x;
}
} // end namespace internal
} // end namespace Eigen

View File

@@ -0,0 +1,79 @@
namespace Eigen {
namespace internal {
template<typename FunctorType, typename Scalar>
DenseIndex fdjac1(
const FunctorType &Functor,
Matrix< Scalar, Dynamic, 1 > &x,
Matrix< Scalar, Dynamic, 1 > &fvec,
Matrix< Scalar, Dynamic, Dynamic > &fjac,
DenseIndex ml, DenseIndex mu,
Scalar epsfcn)
{
using std::sqrt;
using std::abs;
typedef DenseIndex Index;
/* Local variables */
Scalar h;
Index j, k;
Scalar eps, temp;
Index msum;
int iflag;
Index start, length;
/* Function Body */
const Scalar epsmch = NumTraits<Scalar>::epsilon();
const Index n = x.size();
eigen_assert(fvec.size()==n);
Matrix< Scalar, Dynamic, 1 > wa1(n);
Matrix< Scalar, Dynamic, 1 > wa2(n);
eps = sqrt((std::max)(epsfcn,epsmch));
msum = ml + mu + 1;
if (msum >= n) {
/* computation of dense approximate jacobian. */
for (j = 0; j < n; ++j) {
temp = x[j];
h = eps * abs(temp);
if (h == 0.)
h = eps;
x[j] = temp + h;
iflag = Functor(x, wa1);
if (iflag < 0)
return iflag;
x[j] = temp;
fjac.col(j) = (wa1-fvec)/h;
}
}else {
/* computation of banded approximate jacobian. */
for (k = 0; k < msum; ++k) {
for (j = k; (msum<0) ? (j>n): (j<n); j += msum) {
wa2[j] = x[j];
h = eps * abs(wa2[j]);
if (h == 0.) h = eps;
x[j] = wa2[j] + h;
}
iflag = Functor(x, wa1);
if (iflag < 0)
return iflag;
for (j = k; (msum<0) ? (j>n): (j<n); j += msum) {
x[j] = wa2[j];
h = eps * abs(wa2[j]);
if (h == 0.) h = eps;
fjac.col(j).setZero();
start = std::max<Index>(0,j-mu);
length = (std::min)(n-1, j+ml) - start + 1;
fjac.col(j).segment(start, length) = ( wa1.segment(start, length)-fvec.segment(start, length))/h;
}
}
}
return 0;
}
} // end namespace internal
} // end namespace Eigen

View File

@@ -0,0 +1,298 @@
namespace Eigen {
namespace internal {
template <typename Scalar>
void lmpar(
Matrix< Scalar, Dynamic, Dynamic > &r,
const VectorXi &ipvt,
const Matrix< Scalar, Dynamic, 1 > &diag,
const Matrix< Scalar, Dynamic, 1 > &qtb,
Scalar delta,
Scalar &par,
Matrix< Scalar, Dynamic, 1 > &x)
{
using std::abs;
using std::sqrt;
typedef DenseIndex Index;
/* Local variables */
Index i, j, l;
Scalar fp;
Scalar parc, parl;
Index iter;
Scalar temp, paru;
Scalar gnorm;
Scalar dxnorm;
/* Function Body */
const Scalar dwarf = (std::numeric_limits<Scalar>::min)();
const Index n = r.cols();
eigen_assert(n==diag.size());
eigen_assert(n==qtb.size());
eigen_assert(n==x.size());
Matrix< Scalar, Dynamic, 1 > wa1, wa2;
/* compute and store in x the gauss-newton direction. if the */
/* jacobian is rank-deficient, obtain a least squares solution. */
Index nsing = n-1;
wa1 = qtb;
for (j = 0; j < n; ++j) {
if (r(j,j) == 0. && nsing == n-1)
nsing = j - 1;
if (nsing < n-1)
wa1[j] = 0.;
}
for (j = nsing; j>=0; --j) {
wa1[j] /= r(j,j);
temp = wa1[j];
for (i = 0; i < j ; ++i)
wa1[i] -= r(i,j) * temp;
}
for (j = 0; j < n; ++j)
x[ipvt[j]] = wa1[j];
/* initialize the iteration counter. */
/* evaluate the function at the origin, and test */
/* for acceptance of the gauss-newton direction. */
iter = 0;
wa2 = diag.cwiseProduct(x);
dxnorm = wa2.blueNorm();
fp = dxnorm - delta;
if (fp <= Scalar(0.1) * delta) {
par = 0;
return;
}
/* if the jacobian is not rank deficient, the newton */
/* step provides a lower bound, parl, for the zero of */
/* the function. otherwise set this bound to zero. */
parl = 0.;
if (nsing >= n-1) {
for (j = 0; j < n; ++j) {
l = ipvt[j];
wa1[j] = diag[l] * (wa2[l] / dxnorm);
}
// it's actually a triangularView.solveInplace(), though in a weird
// way:
for (j = 0; j < n; ++j) {
Scalar sum = 0.;
for (i = 0; i < j; ++i)
sum += r(i,j) * wa1[i];
wa1[j] = (wa1[j] - sum) / r(j,j);
}
temp = wa1.blueNorm();
parl = fp / delta / temp / temp;
}
/* calculate an upper bound, paru, for the zero of the function. */
for (j = 0; j < n; ++j)
wa1[j] = r.col(j).head(j+1).dot(qtb.head(j+1)) / diag[ipvt[j]];
gnorm = wa1.stableNorm();
paru = gnorm / delta;
if (paru == 0.)
paru = dwarf / (std::min)(delta,Scalar(0.1));
/* if the input par lies outside of the interval (parl,paru), */
/* set par to the closer endpoint. */
par = (std::max)(par,parl);
par = (std::min)(par,paru);
if (par == 0.)
par = gnorm / dxnorm;
/* beginning of an iteration. */
while (true) {
++iter;
/* evaluate the function at the current value of par. */
if (par == 0.)
par = (std::max)(dwarf,Scalar(.001) * paru); /* Computing MAX */
wa1 = sqrt(par)* diag;
Matrix< Scalar, Dynamic, 1 > sdiag(n);
qrsolv<Scalar>(r, ipvt, wa1, qtb, x, sdiag);
wa2 = diag.cwiseProduct(x);
dxnorm = wa2.blueNorm();
temp = fp;
fp = dxnorm - delta;
/* if the function is small enough, accept the current value */
/* of par. also test for the exceptional cases where parl */
/* is zero or the number of iterations has reached 10. */
if (abs(fp) <= Scalar(0.1) * delta || (parl == 0. && fp <= temp && temp < 0.) || iter == 10)
break;
/* compute the newton correction. */
for (j = 0; j < n; ++j) {
l = ipvt[j];
wa1[j] = diag[l] * (wa2[l] / dxnorm);
}
for (j = 0; j < n; ++j) {
wa1[j] /= sdiag[j];
temp = wa1[j];
for (i = j+1; i < n; ++i)
wa1[i] -= r(i,j) * temp;
}
temp = wa1.blueNorm();
parc = fp / delta / temp / temp;
/* depending on the sign of the function, update parl or paru. */
if (fp > 0.)
parl = (std::max)(parl,par);
if (fp < 0.)
paru = (std::min)(paru,par);
/* compute an improved estimate for par. */
/* Computing MAX */
par = (std::max)(parl,par+parc);
/* end of an iteration. */
}
/* termination. */
if (iter == 0)
par = 0.;
return;
}
template <typename Scalar>
void lmpar2(
const ColPivHouseholderQR<Matrix< Scalar, Dynamic, Dynamic> > &qr,
const Matrix< Scalar, Dynamic, 1 > &diag,
const Matrix< Scalar, Dynamic, 1 > &qtb,
Scalar delta,
Scalar &par,
Matrix< Scalar, Dynamic, 1 > &x)
{
using std::sqrt;
using std::abs;
typedef DenseIndex Index;
/* Local variables */
Index j;
Scalar fp;
Scalar parc, parl;
Index iter;
Scalar temp, paru;
Scalar gnorm;
Scalar dxnorm;
/* Function Body */
const Scalar dwarf = (std::numeric_limits<Scalar>::min)();
const Index n = qr.matrixQR().cols();
eigen_assert(n==diag.size());
eigen_assert(n==qtb.size());
Matrix< Scalar, Dynamic, 1 > wa1, wa2;
/* compute and store in x the gauss-newton direction. if the */
/* jacobian is rank-deficient, obtain a least squares solution. */
// const Index rank = qr.nonzeroPivots(); // exactly double(0.)
const Index rank = qr.rank(); // use a threshold
wa1 = qtb;
wa1.tail(n-rank).setZero();
qr.matrixQR().topLeftCorner(rank, rank).template triangularView<Upper>().solveInPlace(wa1.head(rank));
x = qr.colsPermutation()*wa1;
/* initialize the iteration counter. */
/* evaluate the function at the origin, and test */
/* for acceptance of the gauss-newton direction. */
iter = 0;
wa2 = diag.cwiseProduct(x);
dxnorm = wa2.blueNorm();
fp = dxnorm - delta;
if (fp <= Scalar(0.1) * delta) {
par = 0;
return;
}
/* if the jacobian is not rank deficient, the newton */
/* step provides a lower bound, parl, for the zero of */
/* the function. otherwise set this bound to zero. */
parl = 0.;
if (rank==n) {
wa1 = qr.colsPermutation().inverse() * diag.cwiseProduct(wa2)/dxnorm;
qr.matrixQR().topLeftCorner(n, n).transpose().template triangularView<Lower>().solveInPlace(wa1);
temp = wa1.blueNorm();
parl = fp / delta / temp / temp;
}
/* calculate an upper bound, paru, for the zero of the function. */
for (j = 0; j < n; ++j)
wa1[j] = qr.matrixQR().col(j).head(j+1).dot(qtb.head(j+1)) / diag[qr.colsPermutation().indices()(j)];
gnorm = wa1.stableNorm();
paru = gnorm / delta;
if (paru == 0.)
paru = dwarf / (std::min)(delta,Scalar(0.1));
/* if the input par lies outside of the interval (parl,paru), */
/* set par to the closer endpoint. */
par = (std::max)(par,parl);
par = (std::min)(par,paru);
if (par == 0.)
par = gnorm / dxnorm;
/* beginning of an iteration. */
Matrix< Scalar, Dynamic, Dynamic > s = qr.matrixQR();
while (true) {
++iter;
/* evaluate the function at the current value of par. */
if (par == 0.)
par = (std::max)(dwarf,Scalar(.001) * paru); /* Computing MAX */
wa1 = sqrt(par)* diag;
Matrix< Scalar, Dynamic, 1 > sdiag(n);
qrsolv<Scalar>(s, qr.colsPermutation().indices(), wa1, qtb, x, sdiag);
wa2 = diag.cwiseProduct(x);
dxnorm = wa2.blueNorm();
temp = fp;
fp = dxnorm - delta;
/* if the function is small enough, accept the current value */
/* of par. also test for the exceptional cases where parl */
/* is zero or the number of iterations has reached 10. */
if (abs(fp) <= Scalar(0.1) * delta || (parl == 0. && fp <= temp && temp < 0.) || iter == 10)
break;
/* compute the newton correction. */
wa1 = qr.colsPermutation().inverse() * diag.cwiseProduct(wa2/dxnorm);
// we could almost use this here, but the diagonal is outside qr, in sdiag[]
// qr.matrixQR().topLeftCorner(n, n).transpose().template triangularView<Lower>().solveInPlace(wa1);
for (j = 0; j < n; ++j) {
wa1[j] /= sdiag[j];
temp = wa1[j];
for (Index i = j+1; i < n; ++i)
wa1[i] -= s(i,j) * temp;
}
temp = wa1.blueNorm();
parc = fp / delta / temp / temp;
/* depending on the sign of the function, update parl or paru. */
if (fp > 0.)
parl = (std::max)(parl,par);
if (fp < 0.)
paru = (std::min)(paru,par);
/* compute an improved estimate for par. */
par = (std::max)(parl,par+parc);
}
if (iter == 0)
par = 0.;
return;
}
} // end namespace internal
} // end namespace Eigen

View File

@@ -0,0 +1,91 @@
namespace Eigen {
namespace internal {
// TODO : once qrsolv2 is removed, use ColPivHouseholderQR or PermutationMatrix instead of ipvt
template <typename Scalar>
void qrsolv(
Matrix< Scalar, Dynamic, Dynamic > &s,
// TODO : use a PermutationMatrix once lmpar is no more:
const VectorXi &ipvt,
const Matrix< Scalar, Dynamic, 1 > &diag,
const Matrix< Scalar, Dynamic, 1 > &qtb,
Matrix< Scalar, Dynamic, 1 > &x,
Matrix< Scalar, Dynamic, 1 > &sdiag)
{
typedef DenseIndex Index;
/* Local variables */
Index i, j, k, l;
Scalar temp;
Index n = s.cols();
Matrix< Scalar, Dynamic, 1 > wa(n);
JacobiRotation<Scalar> givens;
/* Function Body */
// the following will only change the lower triangular part of s, including
// the diagonal, though the diagonal is restored afterward
/* copy r and (q transpose)*b to preserve input and initialize s. */
/* in particular, save the diagonal elements of r in x. */
x = s.diagonal();
wa = qtb;
s.topLeftCorner(n,n).template triangularView<StrictlyLower>() = s.topLeftCorner(n,n).transpose();
/* eliminate the diagonal matrix d using a givens rotation. */
for (j = 0; j < n; ++j) {
/* prepare the row of d to be eliminated, locating the */
/* diagonal element using p from the qr factorization. */
l = ipvt[j];
if (diag[l] == 0.)
break;
sdiag.tail(n-j).setZero();
sdiag[j] = diag[l];
/* the transformations to eliminate the row of d */
/* modify only a single element of (q transpose)*b */
/* beyond the first n, which is initially zero. */
Scalar qtbpj = 0.;
for (k = j; k < n; ++k) {
/* determine a givens rotation which eliminates the */
/* appropriate element in the current row of d. */
givens.makeGivens(-s(k,k), sdiag[k]);
/* compute the modified diagonal element of r and */
/* the modified element of ((q transpose)*b,0). */
s(k,k) = givens.c() * s(k,k) + givens.s() * sdiag[k];
temp = givens.c() * wa[k] + givens.s() * qtbpj;
qtbpj = -givens.s() * wa[k] + givens.c() * qtbpj;
wa[k] = temp;
/* accumulate the tranformation in the row of s. */
for (i = k+1; i<n; ++i) {
temp = givens.c() * s(i,k) + givens.s() * sdiag[i];
sdiag[i] = -givens.s() * s(i,k) + givens.c() * sdiag[i];
s(i,k) = temp;
}
}
}
/* solve the triangular system for z. if the system is */
/* singular, then obtain a least squares solution. */
Index nsing;
for(nsing=0; nsing<n && sdiag[nsing]!=0; nsing++) {}
wa.tail(n-nsing).setZero();
s.topLeftCorner(nsing, nsing).transpose().template triangularView<Upper>().solveInPlace(wa.head(nsing));
// restore
sdiag = s.diagonal();
s.diagonal() = x;
/* permute the components of z back to components of x. */
for (j = 0; j < n; ++j) x[ipvt[j]] = wa[j];
}
} // end namespace internal
} // end namespace Eigen

View File

@@ -0,0 +1,30 @@
namespace Eigen {
namespace internal {
// TODO : move this to GivensQR once there's such a thing in Eigen
template <typename Scalar>
void r1mpyq(DenseIndex m, DenseIndex n, Scalar *a, const std::vector<JacobiRotation<Scalar> > &v_givens, const std::vector<JacobiRotation<Scalar> > &w_givens)
{
typedef DenseIndex Index;
/* apply the first set of givens rotations to a. */
for (Index j = n-2; j>=0; --j)
for (Index i = 0; i<m; ++i) {
Scalar temp = v_givens[j].c() * a[i+m*j] - v_givens[j].s() * a[i+m*(n-1)];
a[i+m*(n-1)] = v_givens[j].s() * a[i+m*j] + v_givens[j].c() * a[i+m*(n-1)];
a[i+m*j] = temp;
}
/* apply the second set of givens rotations to a. */
for (Index j = 0; j<n-1; ++j)
for (Index i = 0; i<m; ++i) {
Scalar temp = w_givens[j].c() * a[i+m*j] + w_givens[j].s() * a[i+m*(n-1)];
a[i+m*(n-1)] = -w_givens[j].s() * a[i+m*j] + w_givens[j].c() * a[i+m*(n-1)];
a[i+m*j] = temp;
}
}
} // end namespace internal
} // end namespace Eigen

View File

@@ -0,0 +1,99 @@
namespace Eigen {
namespace internal {
template <typename Scalar>
void r1updt(
Matrix< Scalar, Dynamic, Dynamic > &s,
const Matrix< Scalar, Dynamic, 1> &u,
std::vector<JacobiRotation<Scalar> > &v_givens,
std::vector<JacobiRotation<Scalar> > &w_givens,
Matrix< Scalar, Dynamic, 1> &v,
Matrix< Scalar, Dynamic, 1> &w,
bool *sing)
{
typedef DenseIndex Index;
const JacobiRotation<Scalar> IdentityRotation = JacobiRotation<Scalar>(1,0);
/* Local variables */
const Index m = s.rows();
const Index n = s.cols();
Index i, j=1;
Scalar temp;
JacobiRotation<Scalar> givens;
// r1updt had a broader usecase, but we dont use it here. And, more
// importantly, we can not test it.
eigen_assert(m==n);
eigen_assert(u.size()==m);
eigen_assert(v.size()==n);
eigen_assert(w.size()==n);
/* move the nontrivial part of the last column of s into w. */
w[n-1] = s(n-1,n-1);
/* rotate the vector v into a multiple of the n-th unit vector */
/* in such a way that a spike is introduced into w. */
for (j=n-2; j>=0; --j) {
w[j] = 0.;
if (v[j] != 0.) {
/* determine a givens rotation which eliminates the */
/* j-th element of v. */
givens.makeGivens(-v[n-1], v[j]);
/* apply the transformation to v and store the information */
/* necessary to recover the givens rotation. */
v[n-1] = givens.s() * v[j] + givens.c() * v[n-1];
v_givens[j] = givens;
/* apply the transformation to s and extend the spike in w. */
for (i = j; i < m; ++i) {
temp = givens.c() * s(j,i) - givens.s() * w[i];
w[i] = givens.s() * s(j,i) + givens.c() * w[i];
s(j,i) = temp;
}
} else
v_givens[j] = IdentityRotation;
}
/* add the spike from the rank 1 update to w. */
w += v[n-1] * u;
/* eliminate the spike. */
*sing = false;
for (j = 0; j < n-1; ++j) {
if (w[j] != 0.) {
/* determine a givens rotation which eliminates the */
/* j-th element of the spike. */
givens.makeGivens(-s(j,j), w[j]);
/* apply the transformation to s and reduce the spike in w. */
for (i = j; i < m; ++i) {
temp = givens.c() * s(j,i) + givens.s() * w[i];
w[i] = -givens.s() * s(j,i) + givens.c() * w[i];
s(j,i) = temp;
}
/* store the information necessary to recover the */
/* givens rotation. */
w_givens[j] = givens;
} else
v_givens[j] = IdentityRotation;
/* test for zero diagonal elements in the output s. */
if (s(j,j) == 0.) {
*sing = true;
}
}
/* move w back into the last column of the output s. */
s(n-1,n-1) = w[n-1];
if (s(j,j) == 0.) {
*sing = true;
}
return;
}
} // end namespace internal
} // end namespace Eigen

View File

@@ -0,0 +1,49 @@
namespace Eigen {
namespace internal {
template <typename Scalar>
void rwupdt(
Matrix< Scalar, Dynamic, Dynamic > &r,
const Matrix< Scalar, Dynamic, 1> &w,
Matrix< Scalar, Dynamic, 1> &b,
Scalar alpha)
{
typedef DenseIndex Index;
const Index n = r.cols();
eigen_assert(r.rows()>=n);
std::vector<JacobiRotation<Scalar> > givens(n);
/* Local variables */
Scalar temp, rowj;
/* Function Body */
for (Index j = 0; j < n; ++j) {
rowj = w[j];
/* apply the previous transformations to */
/* r(i,j), i=0,1,...,j-1, and to w(j). */
for (Index i = 0; i < j; ++i) {
temp = givens[i].c() * r(i,j) + givens[i].s() * rowj;
rowj = -givens[i].s() * r(i,j) + givens[i].c() * rowj;
r(i,j) = temp;
}
/* determine a givens rotation which eliminates w(j). */
givens[j].makeGivens(-r(j,j), rowj);
if (rowj == 0.)
continue; // givens[j] is identity
/* apply the current transformation to r(j,j), b(j), and alpha. */
r(j,j) = givens[j].c() * r(j,j) + givens[j].s() * rowj;
temp = givens[j].c() * b[j] + givens[j].s() * alpha;
alpha = -givens[j].s() * b[j] + givens[j].c() * alpha;
b[j] = temp;
}
}
} // end namespace internal
} // end namespace Eigen

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_NumericalDiff_SRCS "*.h")
INSTALL(FILES
${Eigen_NumericalDiff_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/NumericalDiff COMPONENT Devel
)

View File

@@ -0,0 +1,130 @@
// -*- coding: utf-8
// vim: set fileencoding=utf-8
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Thomas Capricelli <orzel@freehackers.org>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_NUMERICAL_DIFF_H
#define EIGEN_NUMERICAL_DIFF_H
namespace Eigen {
enum NumericalDiffMode {
Forward,
Central
};
/**
* This class allows you to add a method df() to your functor, which will
* use numerical differentiation to compute an approximate of the
* derivative for the functor. Of course, if you have an analytical form
* for the derivative, you should rather implement df() by yourself.
*
* More information on
* http://en.wikipedia.org/wiki/Numerical_differentiation
*
* Currently only "Forward" and "Central" scheme are implemented.
*/
template<typename _Functor, NumericalDiffMode mode=Forward>
class NumericalDiff : public _Functor
{
public:
typedef _Functor Functor;
typedef typename Functor::Scalar Scalar;
typedef typename Functor::InputType InputType;
typedef typename Functor::ValueType ValueType;
typedef typename Functor::JacobianType JacobianType;
NumericalDiff(Scalar _epsfcn=0.) : Functor(), epsfcn(_epsfcn) {}
NumericalDiff(const Functor& f, Scalar _epsfcn=0.) : Functor(f), epsfcn(_epsfcn) {}
// forward constructors
template<typename T0>
NumericalDiff(const T0& a0) : Functor(a0), epsfcn(0) {}
template<typename T0, typename T1>
NumericalDiff(const T0& a0, const T1& a1) : Functor(a0, a1), epsfcn(0) {}
template<typename T0, typename T1, typename T2>
NumericalDiff(const T0& a0, const T1& a1, const T2& a2) : Functor(a0, a1, a2), epsfcn(0) {}
enum {
InputsAtCompileTime = Functor::InputsAtCompileTime,
ValuesAtCompileTime = Functor::ValuesAtCompileTime
};
/**
* return the number of evaluation of functor
*/
int df(const InputType& _x, JacobianType &jac) const
{
using std::sqrt;
using std::abs;
/* Local variables */
Scalar h;
int nfev=0;
const typename InputType::Index n = _x.size();
const Scalar eps = sqrt(((std::max)(epsfcn,NumTraits<Scalar>::epsilon() )));
ValueType val1, val2;
InputType x = _x;
// TODO : we should do this only if the size is not already known
val1.resize(Functor::values());
val2.resize(Functor::values());
// initialization
switch(mode) {
case Forward:
// compute f(x)
Functor::operator()(x, val1); nfev++;
break;
case Central:
// do nothing
break;
default:
eigen_assert(false);
};
// Function Body
for (int j = 0; j < n; ++j) {
h = eps * abs(x[j]);
if (h == 0.) {
h = eps;
}
switch(mode) {
case Forward:
x[j] += h;
Functor::operator()(x, val2);
nfev++;
x[j] = _x[j];
jac.col(j) = (val2-val1)/h;
break;
case Central:
x[j] += h;
Functor::operator()(x, val2); nfev++;
x[j] -= 2*h;
Functor::operator()(x, val1); nfev++;
x[j] = _x[j];
jac.col(j) = (val2-val1)/(2*h);
break;
default:
eigen_assert(false);
};
}
return nfev;
}
private:
Scalar epsfcn;
NumericalDiff& operator=(const NumericalDiff&);
};
} // end namespace Eigen
//vim: ai ts=4 sts=4 et sw=4
#endif // EIGEN_NUMERICAL_DIFF_H

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_Polynomials_SRCS "*.h")
INSTALL(FILES
${Eigen_Polynomials_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/Polynomials COMPONENT Devel
)

View File

@@ -0,0 +1,276 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2010 Manuel Yguel <manuel.yguel@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_COMPANION_H
#define EIGEN_COMPANION_H
// This file requires the user to include
// * Eigen/Core
// * Eigen/src/PolynomialSolver.h
namespace Eigen {
namespace internal {
#ifndef EIGEN_PARSED_BY_DOXYGEN
template <typename T>
T radix(){ return 2; }
template <typename T>
T radix2(){ return radix<T>()*radix<T>(); }
template<int Size>
struct decrement_if_fixed_size
{
enum {
ret = (Size == Dynamic) ? Dynamic : Size-1 };
};
#endif
template< typename _Scalar, int _Deg >
class companion
{
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(_Scalar,_Deg==Dynamic ? Dynamic : _Deg)
enum {
Deg = _Deg,
Deg_1=decrement_if_fixed_size<Deg>::ret
};
typedef _Scalar Scalar;
typedef typename NumTraits<Scalar>::Real RealScalar;
typedef Matrix<Scalar, Deg, 1> RightColumn;
//typedef DiagonalMatrix< Scalar, Deg_1, Deg_1 > BottomLeftDiagonal;
typedef Matrix<Scalar, Deg_1, 1> BottomLeftDiagonal;
typedef Matrix<Scalar, Deg, Deg> DenseCompanionMatrixType;
typedef Matrix< Scalar, _Deg, Deg_1 > LeftBlock;
typedef Matrix< Scalar, Deg_1, Deg_1 > BottomLeftBlock;
typedef Matrix< Scalar, 1, Deg_1 > LeftBlockFirstRow;
typedef DenseIndex Index;
public:
EIGEN_STRONG_INLINE const _Scalar operator()(Index row, Index col ) const
{
if( m_bl_diag.rows() > col )
{
if( 0 < row ){ return m_bl_diag[col]; }
else{ return 0; }
}
else{ return m_monic[row]; }
}
public:
template<typename VectorType>
void setPolynomial( const VectorType& poly )
{
const Index deg = poly.size()-1;
m_monic = -1/poly[deg] * poly.head(deg);
//m_bl_diag.setIdentity( deg-1 );
m_bl_diag.setOnes(deg-1);
}
template<typename VectorType>
companion( const VectorType& poly ){
setPolynomial( poly ); }
public:
DenseCompanionMatrixType denseMatrix() const
{
const Index deg = m_monic.size();
const Index deg_1 = deg-1;
DenseCompanionMatrixType companion(deg,deg);
companion <<
( LeftBlock(deg,deg_1)
<< LeftBlockFirstRow::Zero(1,deg_1),
BottomLeftBlock::Identity(deg-1,deg-1)*m_bl_diag.asDiagonal() ).finished()
, m_monic;
return companion;
}
protected:
/** Helper function for the balancing algorithm.
* \returns true if the row and the column, having colNorm and rowNorm
* as norms, are balanced, false otherwise.
* colB and rowB are repectively the multipliers for
* the column and the row in order to balance them.
* */
bool balanced( Scalar colNorm, Scalar rowNorm,
bool& isBalanced, Scalar& colB, Scalar& rowB );
/** Helper function for the balancing algorithm.
* \returns true if the row and the column, having colNorm and rowNorm
* as norms, are balanced, false otherwise.
* colB and rowB are repectively the multipliers for
* the column and the row in order to balance them.
* */
bool balancedR( Scalar colNorm, Scalar rowNorm,
bool& isBalanced, Scalar& colB, Scalar& rowB );
public:
/**
* Balancing algorithm from B. N. PARLETT and C. REINSCH (1969)
* "Balancing a matrix for calculation of eigenvalues and eigenvectors"
* adapted to the case of companion matrices.
* A matrix with non zero row and non zero column is balanced
* for a certain norm if the i-th row and the i-th column
* have same norm for all i.
*/
void balance();
protected:
RightColumn m_monic;
BottomLeftDiagonal m_bl_diag;
};
template< typename _Scalar, int _Deg >
inline
bool companion<_Scalar,_Deg>::balanced( Scalar colNorm, Scalar rowNorm,
bool& isBalanced, Scalar& colB, Scalar& rowB )
{
if( Scalar(0) == colNorm || Scalar(0) == rowNorm ){ return true; }
else
{
//To find the balancing coefficients, if the radix is 2,
//one finds \f$ \sigma \f$ such that
// \f$ 2^{2\sigma-1} < rowNorm / colNorm \le 2^{2\sigma+1} \f$
// then the balancing coefficient for the row is \f$ 1/2^{\sigma} \f$
// and the balancing coefficient for the column is \f$ 2^{\sigma} \f$
rowB = rowNorm / radix<Scalar>();
colB = Scalar(1);
const Scalar s = colNorm + rowNorm;
while (colNorm < rowB)
{
colB *= radix<Scalar>();
colNorm *= radix2<Scalar>();
}
rowB = rowNorm * radix<Scalar>();
while (colNorm >= rowB)
{
colB /= radix<Scalar>();
colNorm /= radix2<Scalar>();
}
//This line is used to avoid insubstantial balancing
if ((rowNorm + colNorm) < Scalar(0.95) * s * colB)
{
isBalanced = false;
rowB = Scalar(1) / colB;
return false;
}
else{
return true; }
}
}
template< typename _Scalar, int _Deg >
inline
bool companion<_Scalar,_Deg>::balancedR( Scalar colNorm, Scalar rowNorm,
bool& isBalanced, Scalar& colB, Scalar& rowB )
{
if( Scalar(0) == colNorm || Scalar(0) == rowNorm ){ return true; }
else
{
/**
* Set the norm of the column and the row to the geometric mean
* of the row and column norm
*/
const _Scalar q = colNorm/rowNorm;
if( !isApprox( q, _Scalar(1) ) )
{
rowB = sqrt( colNorm/rowNorm );
colB = Scalar(1)/rowB;
isBalanced = false;
return false;
}
else{
return true; }
}
}
template< typename _Scalar, int _Deg >
void companion<_Scalar,_Deg>::balance()
{
using std::abs;
EIGEN_STATIC_ASSERT( Deg == Dynamic || 1 < Deg, YOU_MADE_A_PROGRAMMING_MISTAKE );
const Index deg = m_monic.size();
const Index deg_1 = deg-1;
bool hasConverged=false;
while( !hasConverged )
{
hasConverged = true;
Scalar colNorm,rowNorm;
Scalar colB,rowB;
//First row, first column excluding the diagonal
//==============================================
colNorm = abs(m_bl_diag[0]);
rowNorm = abs(m_monic[0]);
//Compute balancing of the row and the column
if( !balanced( colNorm, rowNorm, hasConverged, colB, rowB ) )
{
m_bl_diag[0] *= colB;
m_monic[0] *= rowB;
}
//Middle rows and columns excluding the diagonal
//==============================================
for( Index i=1; i<deg_1; ++i )
{
// column norm, excluding the diagonal
colNorm = abs(m_bl_diag[i]);
// row norm, excluding the diagonal
rowNorm = abs(m_bl_diag[i-1]) + abs(m_monic[i]);
//Compute balancing of the row and the column
if( !balanced( colNorm, rowNorm, hasConverged, colB, rowB ) )
{
m_bl_diag[i] *= colB;
m_bl_diag[i-1] *= rowB;
m_monic[i] *= rowB;
}
}
//Last row, last column excluding the diagonal
//============================================
const Index ebl = m_bl_diag.size()-1;
VectorBlock<RightColumn,Deg_1> headMonic( m_monic, 0, deg_1 );
colNorm = headMonic.array().abs().sum();
rowNorm = abs( m_bl_diag[ebl] );
//Compute balancing of the row and the column
if( !balanced( colNorm, rowNorm, hasConverged, colB, rowB ) )
{
headMonic *= colB;
m_bl_diag[ebl] *= rowB;
}
}
}
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_COMPANION_H

View File

@@ -0,0 +1,389 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2010 Manuel Yguel <manuel.yguel@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_POLYNOMIAL_SOLVER_H
#define EIGEN_POLYNOMIAL_SOLVER_H
namespace Eigen {
/** \ingroup Polynomials_Module
* \class PolynomialSolverBase.
*
* \brief Defined to be inherited by polynomial solvers: it provides
* convenient methods such as
* - real roots,
* - greatest, smallest complex roots,
* - real roots with greatest, smallest absolute real value,
* - greatest, smallest real roots.
*
* It stores the set of roots as a vector of complexes.
*
*/
template< typename _Scalar, int _Deg >
class PolynomialSolverBase
{
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(_Scalar,_Deg==Dynamic ? Dynamic : _Deg)
typedef _Scalar Scalar;
typedef typename NumTraits<Scalar>::Real RealScalar;
typedef std::complex<RealScalar> RootType;
typedef Matrix<RootType,_Deg,1> RootsType;
typedef DenseIndex Index;
protected:
template< typename OtherPolynomial >
inline void setPolynomial( const OtherPolynomial& poly ){
m_roots.resize(poly.size()); }
public:
template< typename OtherPolynomial >
inline PolynomialSolverBase( const OtherPolynomial& poly ){
setPolynomial( poly() ); }
inline PolynomialSolverBase(){}
public:
/** \returns the complex roots of the polynomial */
inline const RootsType& roots() const { return m_roots; }
public:
/** Clear and fills the back insertion sequence with the real roots of the polynomial
* i.e. the real part of the complex roots that have an imaginary part which
* absolute value is smaller than absImaginaryThreshold.
* absImaginaryThreshold takes the dummy_precision associated
* with the _Scalar template parameter of the PolynomialSolver class as the default value.
*
* \param[out] bi_seq : the back insertion sequence (stl concept)
* \param[in] absImaginaryThreshold : the maximum bound of the imaginary part of a complex
* number that is considered as real.
* */
template<typename Stl_back_insertion_sequence>
inline void realRoots( Stl_back_insertion_sequence& bi_seq,
const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
{
using std::abs;
bi_seq.clear();
for(Index i=0; i<m_roots.size(); ++i )
{
if( abs( m_roots[i].imag() ) < absImaginaryThreshold ){
bi_seq.push_back( m_roots[i].real() ); }
}
}
protected:
template<typename squaredNormBinaryPredicate>
inline const RootType& selectComplexRoot_withRespectToNorm( squaredNormBinaryPredicate& pred ) const
{
Index res=0;
RealScalar norm2 = numext::abs2( m_roots[0] );
for( Index i=1; i<m_roots.size(); ++i )
{
const RealScalar currNorm2 = numext::abs2( m_roots[i] );
if( pred( currNorm2, norm2 ) ){
res=i; norm2=currNorm2; }
}
return m_roots[res];
}
public:
/**
* \returns the complex root with greatest norm.
*/
inline const RootType& greatestRoot() const
{
std::greater<Scalar> greater;
return selectComplexRoot_withRespectToNorm( greater );
}
/**
* \returns the complex root with smallest norm.
*/
inline const RootType& smallestRoot() const
{
std::less<Scalar> less;
return selectComplexRoot_withRespectToNorm( less );
}
protected:
template<typename squaredRealPartBinaryPredicate>
inline const RealScalar& selectRealRoot_withRespectToAbsRealPart(
squaredRealPartBinaryPredicate& pred,
bool& hasArealRoot,
const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
{
using std::abs;
hasArealRoot = false;
Index res=0;
RealScalar abs2(0);
for( Index i=0; i<m_roots.size(); ++i )
{
if( abs( m_roots[i].imag() ) < absImaginaryThreshold )
{
if( !hasArealRoot )
{
hasArealRoot = true;
res = i;
abs2 = m_roots[i].real() * m_roots[i].real();
}
else
{
const RealScalar currAbs2 = m_roots[i].real() * m_roots[i].real();
if( pred( currAbs2, abs2 ) )
{
abs2 = currAbs2;
res = i;
}
}
}
else
{
if( abs( m_roots[i].imag() ) < abs( m_roots[res].imag() ) ){
res = i; }
}
}
return numext::real_ref(m_roots[res]);
}
template<typename RealPartBinaryPredicate>
inline const RealScalar& selectRealRoot_withRespectToRealPart(
RealPartBinaryPredicate& pred,
bool& hasArealRoot,
const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
{
using std::abs;
hasArealRoot = false;
Index res=0;
RealScalar val(0);
for( Index i=0; i<m_roots.size(); ++i )
{
if( abs( m_roots[i].imag() ) < absImaginaryThreshold )
{
if( !hasArealRoot )
{
hasArealRoot = true;
res = i;
val = m_roots[i].real();
}
else
{
const RealScalar curr = m_roots[i].real();
if( pred( curr, val ) )
{
val = curr;
res = i;
}
}
}
else
{
if( abs( m_roots[i].imag() ) < abs( m_roots[res].imag() ) ){
res = i; }
}
}
return numext::real_ref(m_roots[res]);
}
public:
/**
* \returns a real root with greatest absolute magnitude.
* A real root is defined as the real part of a complex root with absolute imaginary
* part smallest than absImaginaryThreshold.
* absImaginaryThreshold takes the dummy_precision associated
* with the _Scalar template parameter of the PolynomialSolver class as the default value.
* If no real root is found the boolean hasArealRoot is set to false and the real part of
* the root with smallest absolute imaginary part is returned instead.
*
* \param[out] hasArealRoot : boolean true if a real root is found according to the
* absImaginaryThreshold criterion, false otherwise.
* \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
* whether or not a root is real.
*/
inline const RealScalar& absGreatestRealRoot(
bool& hasArealRoot,
const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
{
std::greater<Scalar> greater;
return selectRealRoot_withRespectToAbsRealPart( greater, hasArealRoot, absImaginaryThreshold );
}
/**
* \returns a real root with smallest absolute magnitude.
* A real root is defined as the real part of a complex root with absolute imaginary
* part smallest than absImaginaryThreshold.
* absImaginaryThreshold takes the dummy_precision associated
* with the _Scalar template parameter of the PolynomialSolver class as the default value.
* If no real root is found the boolean hasArealRoot is set to false and the real part of
* the root with smallest absolute imaginary part is returned instead.
*
* \param[out] hasArealRoot : boolean true if a real root is found according to the
* absImaginaryThreshold criterion, false otherwise.
* \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
* whether or not a root is real.
*/
inline const RealScalar& absSmallestRealRoot(
bool& hasArealRoot,
const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
{
std::less<Scalar> less;
return selectRealRoot_withRespectToAbsRealPart( less, hasArealRoot, absImaginaryThreshold );
}
/**
* \returns the real root with greatest value.
* A real root is defined as the real part of a complex root with absolute imaginary
* part smallest than absImaginaryThreshold.
* absImaginaryThreshold takes the dummy_precision associated
* with the _Scalar template parameter of the PolynomialSolver class as the default value.
* If no real root is found the boolean hasArealRoot is set to false and the real part of
* the root with smallest absolute imaginary part is returned instead.
*
* \param[out] hasArealRoot : boolean true if a real root is found according to the
* absImaginaryThreshold criterion, false otherwise.
* \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
* whether or not a root is real.
*/
inline const RealScalar& greatestRealRoot(
bool& hasArealRoot,
const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
{
std::greater<Scalar> greater;
return selectRealRoot_withRespectToRealPart( greater, hasArealRoot, absImaginaryThreshold );
}
/**
* \returns the real root with smallest value.
* A real root is defined as the real part of a complex root with absolute imaginary
* part smallest than absImaginaryThreshold.
* absImaginaryThreshold takes the dummy_precision associated
* with the _Scalar template parameter of the PolynomialSolver class as the default value.
* If no real root is found the boolean hasArealRoot is set to false and the real part of
* the root with smallest absolute imaginary part is returned instead.
*
* \param[out] hasArealRoot : boolean true if a real root is found according to the
* absImaginaryThreshold criterion, false otherwise.
* \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
* whether or not a root is real.
*/
inline const RealScalar& smallestRealRoot(
bool& hasArealRoot,
const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
{
std::less<Scalar> less;
return selectRealRoot_withRespectToRealPart( less, hasArealRoot, absImaginaryThreshold );
}
protected:
RootsType m_roots;
};
#define EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES( BASE ) \
typedef typename BASE::Scalar Scalar; \
typedef typename BASE::RealScalar RealScalar; \
typedef typename BASE::RootType RootType; \
typedef typename BASE::RootsType RootsType;
/** \ingroup Polynomials_Module
*
* \class PolynomialSolver
*
* \brief A polynomial solver
*
* Computes the complex roots of a real polynomial.
*
* \param _Scalar the scalar type, i.e., the type of the polynomial coefficients
* \param _Deg the degree of the polynomial, can be a compile time value or Dynamic.
* Notice that the number of polynomial coefficients is _Deg+1.
*
* This class implements a polynomial solver and provides convenient methods such as
* - real roots,
* - greatest, smallest complex roots,
* - real roots with greatest, smallest absolute real value.
* - greatest, smallest real roots.
*
* WARNING: this polynomial solver is experimental, part of the unsuported Eigen modules.
*
*
* Currently a QR algorithm is used to compute the eigenvalues of the companion matrix of
* the polynomial to compute its roots.
* This supposes that the complex moduli of the roots are all distinct: e.g. there should
* be no multiple roots or conjugate roots for instance.
* With 32bit (float) floating types this problem shows up frequently.
* However, almost always, correct accuracy is reached even in these cases for 64bit
* (double) floating types and small polynomial degree (<20).
*/
template< typename _Scalar, int _Deg >
class PolynomialSolver : public PolynomialSolverBase<_Scalar,_Deg>
{
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(_Scalar,_Deg==Dynamic ? Dynamic : _Deg)
typedef PolynomialSolverBase<_Scalar,_Deg> PS_Base;
EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES( PS_Base )
typedef Matrix<Scalar,_Deg,_Deg> CompanionMatrixType;
typedef EigenSolver<CompanionMatrixType> EigenSolverType;
public:
/** Computes the complex roots of a new polynomial. */
template< typename OtherPolynomial >
void compute( const OtherPolynomial& poly )
{
eigen_assert( Scalar(0) != poly[poly.size()-1] );
internal::companion<Scalar,_Deg> companion( poly );
companion.balance();
m_eigenSolver.compute( companion.denseMatrix() );
m_roots = m_eigenSolver.eigenvalues();
}
public:
template< typename OtherPolynomial >
inline PolynomialSolver( const OtherPolynomial& poly ){
compute( poly ); }
inline PolynomialSolver(){}
protected:
using PS_Base::m_roots;
EigenSolverType m_eigenSolver;
};
template< typename _Scalar >
class PolynomialSolver<_Scalar,1> : public PolynomialSolverBase<_Scalar,1>
{
public:
typedef PolynomialSolverBase<_Scalar,1> PS_Base;
EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES( PS_Base )
public:
/** Computes the complex roots of a new polynomial. */
template< typename OtherPolynomial >
void compute( const OtherPolynomial& poly )
{
eigen_assert( Scalar(0) != poly[poly.size()-1] );
m_roots[0] = -poly[0]/poly[poly.size()-1];
}
protected:
using PS_Base::m_roots;
};
} // end namespace Eigen
#endif // EIGEN_POLYNOMIAL_SOLVER_H

View File

@@ -0,0 +1,143 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2010 Manuel Yguel <manuel.yguel@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_POLYNOMIAL_UTILS_H
#define EIGEN_POLYNOMIAL_UTILS_H
namespace Eigen {
/** \ingroup Polynomials_Module
* \returns the evaluation of the polynomial at x using Horner algorithm.
*
* \param[in] poly : the vector of coefficients of the polynomial ordered
* by degrees i.e. poly[i] is the coefficient of degree i of the polynomial
* e.g. \f$ 1 + 3x^2 \f$ is stored as a vector \f$ [ 1, 0, 3 ] \f$.
* \param[in] x : the value to evaluate the polynomial at.
*
* <i><b>Note for stability:</b></i>
* <dd> \f$ |x| \le 1 \f$ </dd>
*/
template <typename Polynomials, typename T>
inline
T poly_eval_horner( const Polynomials& poly, const T& x )
{
T val=poly[poly.size()-1];
for(DenseIndex i=poly.size()-2; i>=0; --i ){
val = val*x + poly[i]; }
return val;
}
/** \ingroup Polynomials_Module
* \returns the evaluation of the polynomial at x using stabilized Horner algorithm.
*
* \param[in] poly : the vector of coefficients of the polynomial ordered
* by degrees i.e. poly[i] is the coefficient of degree i of the polynomial
* e.g. \f$ 1 + 3x^2 \f$ is stored as a vector \f$ [ 1, 0, 3 ] \f$.
* \param[in] x : the value to evaluate the polynomial at.
*/
template <typename Polynomials, typename T>
inline
T poly_eval( const Polynomials& poly, const T& x )
{
typedef typename NumTraits<T>::Real Real;
if( numext::abs2( x ) <= Real(1) ){
return poly_eval_horner( poly, x ); }
else
{
T val=poly[0];
T inv_x = T(1)/x;
for( DenseIndex i=1; i<poly.size(); ++i ){
val = val*inv_x + poly[i]; }
return std::pow(x,(T)(poly.size()-1)) * val;
}
}
/** \ingroup Polynomials_Module
* \returns a maximum bound for the absolute value of any root of the polynomial.
*
* \param[in] poly : the vector of coefficients of the polynomial ordered
* by degrees i.e. poly[i] is the coefficient of degree i of the polynomial
* e.g. \f$ 1 + 3x^2 \f$ is stored as a vector \f$ [ 1, 0, 3 ] \f$.
*
* <i><b>Precondition:</b></i>
* <dd> the leading coefficient of the input polynomial poly must be non zero </dd>
*/
template <typename Polynomial>
inline
typename NumTraits<typename Polynomial::Scalar>::Real cauchy_max_bound( const Polynomial& poly )
{
using std::abs;
typedef typename Polynomial::Scalar Scalar;
typedef typename NumTraits<Scalar>::Real Real;
eigen_assert( Scalar(0) != poly[poly.size()-1] );
const Scalar inv_leading_coeff = Scalar(1)/poly[poly.size()-1];
Real cb(0);
for( DenseIndex i=0; i<poly.size()-1; ++i ){
cb += abs(poly[i]*inv_leading_coeff); }
return cb + Real(1);
}
/** \ingroup Polynomials_Module
* \returns a minimum bound for the absolute value of any non zero root of the polynomial.
* \param[in] poly : the vector of coefficients of the polynomial ordered
* by degrees i.e. poly[i] is the coefficient of degree i of the polynomial
* e.g. \f$ 1 + 3x^2 \f$ is stored as a vector \f$ [ 1, 0, 3 ] \f$.
*/
template <typename Polynomial>
inline
typename NumTraits<typename Polynomial::Scalar>::Real cauchy_min_bound( const Polynomial& poly )
{
using std::abs;
typedef typename Polynomial::Scalar Scalar;
typedef typename NumTraits<Scalar>::Real Real;
DenseIndex i=0;
while( i<poly.size()-1 && Scalar(0) == poly(i) ){ ++i; }
if( poly.size()-1 == i ){
return Real(1); }
const Scalar inv_min_coeff = Scalar(1)/poly[i];
Real cb(1);
for( DenseIndex j=i+1; j<poly.size(); ++j ){
cb += abs(poly[j]*inv_min_coeff); }
return Real(1)/cb;
}
/** \ingroup Polynomials_Module
* Given the roots of a polynomial compute the coefficients in the
* monomial basis of the monic polynomial with same roots and minimal degree.
* If RootVector is a vector of complexes, Polynomial should also be a vector
* of complexes.
* \param[in] rv : a vector containing the roots of a polynomial.
* \param[out] poly : the vector of coefficients of the polynomial ordered
* by degrees i.e. poly[i] is the coefficient of degree i of the polynomial
* e.g. \f$ 3 + x^2 \f$ is stored as a vector \f$ [ 3, 0, 1 ] \f$.
*/
template <typename RootVector, typename Polynomial>
void roots_to_monicPolynomial( const RootVector& rv, Polynomial& poly )
{
typedef typename Polynomial::Scalar Scalar;
poly.setZero( rv.size()+1 );
poly[0] = -rv[0]; poly[1] = Scalar(1);
for( DenseIndex i=1; i< rv.size(); ++i )
{
for( DenseIndex j=i+1; j>0; --j ){ poly[j] = poly[j-1] - rv[i]*poly[j]; }
poly[0] = -rv[i]*poly[0];
}
}
} // end namespace Eigen
#endif // EIGEN_POLYNOMIAL_UTILS_H

View File

@@ -0,0 +1,748 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// We used the "A Divide-And-Conquer Algorithm for the Bidiagonal SVD"
// research report written by Ming Gu and Stanley C.Eisenstat
// The code variable names correspond to the names they used in their
// report
//
// Copyright (C) 2013 Gauthier Brun <brun.gauthier@gmail.com>
// Copyright (C) 2013 Nicolas Carre <nicolas.carre@ensimag.fr>
// Copyright (C) 2013 Jean Ceccato <jean.ceccato@ensimag.fr>
// Copyright (C) 2013 Pierre Zoppitelli <pierre.zoppitelli@ensimag.fr>
//
// Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_BDCSVD_H
#define EIGEN_BDCSVD_H
#define EPSILON 0.0000000000000001
#define ALGOSWAP 32
namespace Eigen {
/** \ingroup SVD_Module
*
*
* \class BDCSVD
*
* \brief class Bidiagonal Divide and Conquer SVD
*
* \param MatrixType the type of the matrix of which we are computing the SVD decomposition
* We plan to have a very similar interface to JacobiSVD on this class.
* It should be used to speed up the calcul of SVD for big matrices.
*/
template<typename _MatrixType>
class BDCSVD : public SVDBase<_MatrixType>
{
typedef SVDBase<_MatrixType> Base;
public:
using Base::rows;
using Base::cols;
typedef _MatrixType MatrixType;
typedef typename MatrixType::Scalar Scalar;
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
typedef typename MatrixType::Index Index;
enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
DiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_DYNAMIC(RowsAtCompileTime, ColsAtCompileTime),
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
MaxDiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_FIXED(MaxRowsAtCompileTime, MaxColsAtCompileTime),
MatrixOptions = MatrixType::Options
};
typedef Matrix<Scalar, RowsAtCompileTime, RowsAtCompileTime,
MatrixOptions, MaxRowsAtCompileTime, MaxRowsAtCompileTime>
MatrixUType;
typedef Matrix<Scalar, ColsAtCompileTime, ColsAtCompileTime,
MatrixOptions, MaxColsAtCompileTime, MaxColsAtCompileTime>
MatrixVType;
typedef typename internal::plain_diag_type<MatrixType, RealScalar>::type SingularValuesType;
typedef typename internal::plain_row_type<MatrixType>::type RowType;
typedef typename internal::plain_col_type<MatrixType>::type ColType;
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
typedef Matrix<RealScalar, Dynamic, Dynamic> MatrixXr;
typedef Matrix<RealScalar, Dynamic, 1> VectorType;
/** \brief Default Constructor.
*
* The default constructor is useful in cases in which the user intends to
* perform decompositions via BDCSVD::compute(const MatrixType&).
*/
BDCSVD()
: SVDBase<_MatrixType>::SVDBase(),
algoswap(ALGOSWAP)
{}
/** \brief Default Constructor with memory preallocation
*
* Like the default constructor but with preallocation of the internal data
* according to the specified problem size.
* \sa BDCSVD()
*/
BDCSVD(Index rows, Index cols, unsigned int computationOptions = 0)
: SVDBase<_MatrixType>::SVDBase(),
algoswap(ALGOSWAP)
{
allocate(rows, cols, computationOptions);
}
/** \brief Constructor performing the decomposition of given matrix.
*
* \param matrix the matrix to decompose
* \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed.
* By default, none is computed. This is a bit - field, the possible bits are #ComputeFullU, #ComputeThinU,
* #ComputeFullV, #ComputeThinV.
*
* Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not
* available with the (non - default) FullPivHouseholderQR preconditioner.
*/
BDCSVD(const MatrixType& matrix, unsigned int computationOptions = 0)
: SVDBase<_MatrixType>::SVDBase(),
algoswap(ALGOSWAP)
{
compute(matrix, computationOptions);
}
~BDCSVD()
{
}
/** \brief Method performing the decomposition of given matrix using custom options.
*
* \param matrix the matrix to decompose
* \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed.
* By default, none is computed. This is a bit - field, the possible bits are #ComputeFullU, #ComputeThinU,
* #ComputeFullV, #ComputeThinV.
*
* Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not
* available with the (non - default) FullPivHouseholderQR preconditioner.
*/
SVDBase<MatrixType>& compute(const MatrixType& matrix, unsigned int computationOptions);
/** \brief Method performing the decomposition of given matrix using current options.
*
* \param matrix the matrix to decompose
*
* This method uses the current \a computationOptions, as already passed to the constructor or to compute(const MatrixType&, unsigned int).
*/
SVDBase<MatrixType>& compute(const MatrixType& matrix)
{
return compute(matrix, this->m_computationOptions);
}
void setSwitchSize(int s)
{
eigen_assert(s>3 && "BDCSVD the size of the algo switch has to be greater than 4");
algoswap = s;
}
/** \returns a (least squares) solution of \f$ A x = b \f$ using the current SVD decomposition of A.
*
* \param b the right - hand - side of the equation to solve.
*
* \note Solving requires both U and V to be computed. Thin U and V are enough, there is no need for full U or V.
*
* \note SVD solving is implicitly least - squares. Thus, this method serves both purposes of exact solving and least - squares solving.
* In other words, the returned solution is guaranteed to minimize the Euclidean norm \f$ \Vert A x - b \Vert \f$.
*/
template<typename Rhs>
inline const internal::solve_retval<BDCSVD, Rhs>
solve(const MatrixBase<Rhs>& b) const
{
eigen_assert(this->m_isInitialized && "BDCSVD is not initialized.");
eigen_assert(SVDBase<_MatrixType>::computeU() && SVDBase<_MatrixType>::computeV() &&
"BDCSVD::solve() requires both unitaries U and V to be computed (thin unitaries suffice).");
return internal::solve_retval<BDCSVD, Rhs>(*this, b.derived());
}
const MatrixUType& matrixU() const
{
eigen_assert(this->m_isInitialized && "SVD is not initialized.");
if (isTranspose){
eigen_assert(this->computeV() && "This SVD decomposition didn't compute U. Did you ask for it?");
return this->m_matrixV;
}
else
{
eigen_assert(this->computeU() && "This SVD decomposition didn't compute U. Did you ask for it?");
return this->m_matrixU;
}
}
const MatrixVType& matrixV() const
{
eigen_assert(this->m_isInitialized && "SVD is not initialized.");
if (isTranspose){
eigen_assert(this->computeU() && "This SVD decomposition didn't compute V. Did you ask for it?");
return this->m_matrixU;
}
else
{
eigen_assert(this->computeV() && "This SVD decomposition didn't compute V. Did you ask for it?");
return this->m_matrixV;
}
}
private:
void allocate(Index rows, Index cols, unsigned int computationOptions);
void divide (Index firstCol, Index lastCol, Index firstRowW,
Index firstColW, Index shift);
void deflation43(Index firstCol, Index shift, Index i, Index size);
void deflation44(Index firstColu , Index firstColm, Index firstRowW, Index firstColW, Index i, Index j, Index size);
void deflation(Index firstCol, Index lastCol, Index k, Index firstRowW, Index firstColW, Index shift);
void copyUV(MatrixXr naiveU, MatrixXr naiveV, MatrixX householderU, MatrixX houseHolderV);
protected:
MatrixXr m_naiveU, m_naiveV;
MatrixXr m_computed;
Index nRec;
int algoswap;
bool isTranspose, compU, compV;
}; //end class BDCSVD
// Methode to allocate ans initialize matrix and attributs
template<typename MatrixType>
void BDCSVD<MatrixType>::allocate(Index rows, Index cols, unsigned int computationOptions)
{
isTranspose = (cols > rows);
if (SVDBase<MatrixType>::allocate(rows, cols, computationOptions)) return;
m_computed = MatrixXr::Zero(this->m_diagSize + 1, this->m_diagSize );
if (isTranspose){
compU = this->computeU();
compV = this->computeV();
}
else
{
compV = this->computeU();
compU = this->computeV();
}
if (compU) m_naiveU = MatrixXr::Zero(this->m_diagSize + 1, this->m_diagSize + 1 );
else m_naiveU = MatrixXr::Zero(2, this->m_diagSize + 1 );
if (compV) m_naiveV = MatrixXr::Zero(this->m_diagSize, this->m_diagSize);
//should be changed for a cleaner implementation
if (isTranspose){
bool aux;
if (this->computeU()||this->computeV()){
aux = this->m_computeFullU;
this->m_computeFullU = this->m_computeFullV;
this->m_computeFullV = aux;
aux = this->m_computeThinU;
this->m_computeThinU = this->m_computeThinV;
this->m_computeThinV = aux;
}
}
}// end allocate
// Methode which compute the BDCSVD for the int
template<>
SVDBase<Matrix<int, Dynamic, Dynamic> >&
BDCSVD<Matrix<int, Dynamic, Dynamic> >::compute(const MatrixType& matrix, unsigned int computationOptions) {
allocate(matrix.rows(), matrix.cols(), computationOptions);
this->m_nonzeroSingularValues = 0;
m_computed = Matrix<int, Dynamic, Dynamic>::Zero(rows(), cols());
for (int i=0; i<this->m_diagSize; i++) {
this->m_singularValues.coeffRef(i) = 0;
}
if (this->m_computeFullU) this->m_matrixU = Matrix<int, Dynamic, Dynamic>::Zero(rows(), rows());
if (this->m_computeFullV) this->m_matrixV = Matrix<int, Dynamic, Dynamic>::Zero(cols(), cols());
this->m_isInitialized = true;
return *this;
}
// Methode which compute the BDCSVD
template<typename MatrixType>
SVDBase<MatrixType>&
BDCSVD<MatrixType>::compute(const MatrixType& matrix, unsigned int computationOptions)
{
allocate(matrix.rows(), matrix.cols(), computationOptions);
using std::abs;
//**** step 1 Bidiagonalization isTranspose = (matrix.cols()>matrix.rows()) ;
MatrixType copy;
if (isTranspose) copy = matrix.adjoint();
else copy = matrix;
internal::UpperBidiagonalization<MatrixX > bid(copy);
//**** step 2 Divide
// this is ugly and has to be redone (care of complex cast)
MatrixXr temp;
temp = bid.bidiagonal().toDenseMatrix().transpose();
m_computed.setZero();
for (int i=0; i<this->m_diagSize - 1; i++) {
m_computed(i, i) = temp(i, i);
m_computed(i + 1, i) = temp(i + 1, i);
}
m_computed(this->m_diagSize - 1, this->m_diagSize - 1) = temp(this->m_diagSize - 1, this->m_diagSize - 1);
divide(0, this->m_diagSize - 1, 0, 0, 0);
//**** step 3 copy
for (int i=0; i<this->m_diagSize; i++) {
RealScalar a = abs(m_computed.coeff(i, i));
this->m_singularValues.coeffRef(i) = a;
if (a == 0){
this->m_nonzeroSingularValues = i;
break;
}
else if (i == this->m_diagSize - 1)
{
this->m_nonzeroSingularValues = i + 1;
break;
}
}
copyUV(m_naiveV, m_naiveU, bid.householderU(), bid.householderV());
this->m_isInitialized = true;
return *this;
}// end compute
template<typename MatrixType>
void BDCSVD<MatrixType>::copyUV(MatrixXr naiveU, MatrixXr naiveV, MatrixX householderU, MatrixX householderV){
if (this->computeU()){
MatrixX temp = MatrixX::Zero(naiveU.rows(), naiveU.cols());
temp.real() = naiveU;
if (this->m_computeThinU){
this->m_matrixU = MatrixX::Identity(householderU.cols(), this->m_nonzeroSingularValues );
this->m_matrixU.block(0, 0, this->m_diagSize, this->m_nonzeroSingularValues) =
temp.block(0, 0, this->m_diagSize, this->m_nonzeroSingularValues);
this->m_matrixU = householderU * this->m_matrixU ;
}
else
{
this->m_matrixU = MatrixX::Identity(householderU.cols(), householderU.cols());
this->m_matrixU.block(0, 0, this->m_diagSize, this->m_diagSize) = temp.block(0, 0, this->m_diagSize, this->m_diagSize);
this->m_matrixU = householderU * this->m_matrixU ;
}
}
if (this->computeV()){
MatrixX temp = MatrixX::Zero(naiveV.rows(), naiveV.cols());
temp.real() = naiveV;
if (this->m_computeThinV){
this->m_matrixV = MatrixX::Identity(householderV.cols(),this->m_nonzeroSingularValues );
this->m_matrixV.block(0, 0, this->m_nonzeroSingularValues, this->m_nonzeroSingularValues) =
temp.block(0, 0, this->m_nonzeroSingularValues, this->m_nonzeroSingularValues);
this->m_matrixV = householderV * this->m_matrixV ;
}
else
{
this->m_matrixV = MatrixX::Identity(householderV.cols(), householderV.cols());
this->m_matrixV.block(0, 0, this->m_diagSize, this->m_diagSize) = temp.block(0, 0, this->m_diagSize, this->m_diagSize);
this->m_matrixV = householderV * this->m_matrixV;
}
}
}
// The divide algorithm is done "in place", we are always working on subsets of the same matrix. The divide methods takes as argument the
// place of the submatrix we are currently working on.
//@param firstCol : The Index of the first column of the submatrix of m_computed and for m_naiveU;
//@param lastCol : The Index of the last column of the submatrix of m_computed and for m_naiveU;
// lastCol + 1 - firstCol is the size of the submatrix.
//@param firstRowW : The Index of the first row of the matrix W that we are to change. (see the reference paper section 1 for more information on W)
//@param firstRowW : Same as firstRowW with the column.
//@param shift : Each time one takes the left submatrix, one must add 1 to the shift. Why? Because! We actually want the last column of the U submatrix
// to become the first column (*coeff) and to shift all the other columns to the right. There are more details on the reference paper.
template<typename MatrixType>
void BDCSVD<MatrixType>::divide (Index firstCol, Index lastCol, Index firstRowW,
Index firstColW, Index shift)
{
// requires nbRows = nbCols + 1;
using std::pow;
using std::sqrt;
using std::abs;
const Index n = lastCol - firstCol + 1;
const Index k = n/2;
RealScalar alphaK;
RealScalar betaK;
RealScalar r0;
RealScalar lambda, phi, c0, s0;
MatrixXr l, f;
// We use the other algorithm which is more efficient for small
// matrices.
if (n < algoswap){
JacobiSVD<MatrixXr> b(m_computed.block(firstCol, firstCol, n + 1, n),
ComputeFullU | (ComputeFullV * compV)) ;
if (compU) m_naiveU.block(firstCol, firstCol, n + 1, n + 1).real() << b.matrixU();
else
{
m_naiveU.row(0).segment(firstCol, n + 1).real() << b.matrixU().row(0);
m_naiveU.row(1).segment(firstCol, n + 1).real() << b.matrixU().row(n);
}
if (compV) m_naiveV.block(firstRowW, firstColW, n, n).real() << b.matrixV();
m_computed.block(firstCol + shift, firstCol + shift, n + 1, n).setZero();
for (int i=0; i<n; i++)
{
m_computed(firstCol + shift + i, firstCol + shift +i) = b.singularValues().coeffRef(i);
}
return;
}
// We use the divide and conquer algorithm
alphaK = m_computed(firstCol + k, firstCol + k);
betaK = m_computed(firstCol + k + 1, firstCol + k);
// The divide must be done in that order in order to have good results. Divide change the data inside the submatrices
// and the divide of the right submatrice reads one column of the left submatrice. That's why we need to treat the
// right submatrix before the left one.
divide(k + 1 + firstCol, lastCol, k + 1 + firstRowW, k + 1 + firstColW, shift);
divide(firstCol, k - 1 + firstCol, firstRowW, firstColW + 1, shift + 1);
if (compU)
{
lambda = m_naiveU(firstCol + k, firstCol + k);
phi = m_naiveU(firstCol + k + 1, lastCol + 1);
}
else
{
lambda = m_naiveU(1, firstCol + k);
phi = m_naiveU(0, lastCol + 1);
}
r0 = sqrt((abs(alphaK * lambda) * abs(alphaK * lambda))
+ abs(betaK * phi) * abs(betaK * phi));
if (compU)
{
l = m_naiveU.row(firstCol + k).segment(firstCol, k);
f = m_naiveU.row(firstCol + k + 1).segment(firstCol + k + 1, n - k - 1);
}
else
{
l = m_naiveU.row(1).segment(firstCol, k);
f = m_naiveU.row(0).segment(firstCol + k + 1, n - k - 1);
}
if (compV) m_naiveV(firstRowW+k, firstColW) = 1;
if (r0 == 0)
{
c0 = 1;
s0 = 0;
}
else
{
c0 = alphaK * lambda / r0;
s0 = betaK * phi / r0;
}
if (compU)
{
MatrixXr q1 (m_naiveU.col(firstCol + k).segment(firstCol, k + 1));
// we shiftW Q1 to the right
for (Index i = firstCol + k - 1; i >= firstCol; i--)
{
m_naiveU.col(i + 1).segment(firstCol, k + 1) << m_naiveU.col(i).segment(firstCol, k + 1);
}
// we shift q1 at the left with a factor c0
m_naiveU.col(firstCol).segment( firstCol, k + 1) << (q1 * c0);
// last column = q1 * - s0
m_naiveU.col(lastCol + 1).segment(firstCol, k + 1) << (q1 * ( - s0));
// first column = q2 * s0
m_naiveU.col(firstCol).segment(firstCol + k + 1, n - k) <<
m_naiveU.col(lastCol + 1).segment(firstCol + k + 1, n - k) *s0;
// q2 *= c0
m_naiveU.col(lastCol + 1).segment(firstCol + k + 1, n - k) *= c0;
}
else
{
RealScalar q1 = (m_naiveU(0, firstCol + k));
// we shift Q1 to the right
for (Index i = firstCol + k - 1; i >= firstCol; i--)
{
m_naiveU(0, i + 1) = m_naiveU(0, i);
}
// we shift q1 at the left with a factor c0
m_naiveU(0, firstCol) = (q1 * c0);
// last column = q1 * - s0
m_naiveU(0, lastCol + 1) = (q1 * ( - s0));
// first column = q2 * s0
m_naiveU(1, firstCol) = m_naiveU(1, lastCol + 1) *s0;
// q2 *= c0
m_naiveU(1, lastCol + 1) *= c0;
m_naiveU.row(1).segment(firstCol + 1, k).setZero();
m_naiveU.row(0).segment(firstCol + k + 1, n - k - 1).setZero();
}
m_computed(firstCol + shift, firstCol + shift) = r0;
m_computed.col(firstCol + shift).segment(firstCol + shift + 1, k) << alphaK * l.transpose().real();
m_computed.col(firstCol + shift).segment(firstCol + shift + k + 1, n - k - 1) << betaK * f.transpose().real();
// the line below do the deflation of the matrix for the third part of the algorithm
// Here the deflation is commented because the third part of the algorithm is not implemented
// the third part of the algorithm is a fast SVD on the matrix m_computed which works thanks to the deflation
deflation(firstCol, lastCol, k, firstRowW, firstColW, shift);
// Third part of the algorithm, since the real third part of the algorithm is not implemeted we use a JacobiSVD
JacobiSVD<MatrixXr> res= JacobiSVD<MatrixXr>(m_computed.block(firstCol + shift, firstCol +shift, n + 1, n),
ComputeFullU | (ComputeFullV * compV)) ;
if (compU) m_naiveU.block(firstCol, firstCol, n + 1, n + 1) *= res.matrixU();
else m_naiveU.block(0, firstCol, 2, n + 1) *= res.matrixU();
if (compV) m_naiveV.block(firstRowW, firstColW, n, n) *= res.matrixV();
m_computed.block(firstCol + shift, firstCol + shift, n, n) << MatrixXr::Zero(n, n);
for (int i=0; i<n; i++)
m_computed(firstCol + shift + i, firstCol + shift +i) = res.singularValues().coeffRef(i);
// end of the third part
}// end divide
// page 12_13
// i >= 1, di almost null and zi non null.
// We use a rotation to zero out zi applied to the left of M
template <typename MatrixType>
void BDCSVD<MatrixType>::deflation43(Index firstCol, Index shift, Index i, Index size){
using std::abs;
using std::sqrt;
using std::pow;
RealScalar c = m_computed(firstCol + shift, firstCol + shift);
RealScalar s = m_computed(i, firstCol + shift);
RealScalar r = sqrt(pow(abs(c), 2) + pow(abs(s), 2));
if (r == 0){
m_computed(i, i)=0;
return;
}
c/=r;
s/=r;
m_computed(firstCol + shift, firstCol + shift) = r;
m_computed(i, firstCol + shift) = 0;
m_computed(i, i) = 0;
if (compU){
m_naiveU.col(firstCol).segment(firstCol,size) =
c * m_naiveU.col(firstCol).segment(firstCol, size) -
s * m_naiveU.col(i).segment(firstCol, size) ;
m_naiveU.col(i).segment(firstCol, size) =
(c + s*s/c) * m_naiveU.col(i).segment(firstCol, size) +
(s/c) * m_naiveU.col(firstCol).segment(firstCol,size);
}
}// end deflation 43
// page 13
// i,j >= 1, i != j and |di - dj| < epsilon * norm2(M)
// We apply two rotations to have zj = 0;
template <typename MatrixType>
void BDCSVD<MatrixType>::deflation44(Index firstColu , Index firstColm, Index firstRowW, Index firstColW, Index i, Index j, Index size){
using std::abs;
using std::sqrt;
using std::conj;
using std::pow;
RealScalar c = m_computed(firstColm, firstColm + j - 1);
RealScalar s = m_computed(firstColm, firstColm + i - 1);
RealScalar r = sqrt(pow(abs(c), 2) + pow(abs(s), 2));
if (r==0){
m_computed(firstColm + i, firstColm + i) = m_computed(firstColm + j, firstColm + j);
return;
}
c/=r;
s/=r;
m_computed(firstColm + i, firstColm) = r;
m_computed(firstColm + i, firstColm + i) = m_computed(firstColm + j, firstColm + j);
m_computed(firstColm + j, firstColm) = 0;
if (compU){
m_naiveU.col(firstColu + i).segment(firstColu, size) =
c * m_naiveU.col(firstColu + i).segment(firstColu, size) -
s * m_naiveU.col(firstColu + j).segment(firstColu, size) ;
m_naiveU.col(firstColu + j).segment(firstColu, size) =
(c + s*s/c) * m_naiveU.col(firstColu + j).segment(firstColu, size) +
(s/c) * m_naiveU.col(firstColu + i).segment(firstColu, size);
}
if (compV){
m_naiveV.col(firstColW + i).segment(firstRowW, size - 1) =
c * m_naiveV.col(firstColW + i).segment(firstRowW, size - 1) +
s * m_naiveV.col(firstColW + j).segment(firstRowW, size - 1) ;
m_naiveV.col(firstColW + j).segment(firstRowW, size - 1) =
(c + s*s/c) * m_naiveV.col(firstColW + j).segment(firstRowW, size - 1) -
(s/c) * m_naiveV.col(firstColW + i).segment(firstRowW, size - 1);
}
}// end deflation 44
template <typename MatrixType>
void BDCSVD<MatrixType>::deflation(Index firstCol, Index lastCol, Index k, Index firstRowW, Index firstColW, Index shift){
//condition 4.1
RealScalar EPS = EPSILON * (std::max<RealScalar>(m_computed(firstCol + shift + 1, firstCol + shift + 1), m_computed(firstCol + k, firstCol + k)));
const Index length = lastCol + 1 - firstCol;
if (m_computed(firstCol + shift, firstCol + shift) < EPS){
m_computed(firstCol + shift, firstCol + shift) = EPS;
}
//condition 4.2
for (Index i=firstCol + shift + 1;i<=lastCol + shift;i++){
if (std::abs(m_computed(i, firstCol + shift)) < EPS){
m_computed(i, firstCol + shift) = 0;
}
}
//condition 4.3
for (Index i=firstCol + shift + 1;i<=lastCol + shift; i++){
if (m_computed(i, i) < EPS){
deflation43(firstCol, shift, i, length);
}
}
//condition 4.4
Index i=firstCol + shift + 1, j=firstCol + shift + k + 1;
//we stock the final place of each line
Index *permutation = new Index[length];
for (Index p =1; p < length; p++) {
if (i> firstCol + shift + k){
permutation[p] = j;
j++;
} else if (j> lastCol + shift)
{
permutation[p] = i;
i++;
}
else
{
if (m_computed(i, i) < m_computed(j, j)){
permutation[p] = j;
j++;
}
else
{
permutation[p] = i;
i++;
}
}
}
//we do the permutation
RealScalar aux;
//we stock the current index of each col
//and the column of each index
Index *realInd = new Index[length];
Index *realCol = new Index[length];
for (int pos = 0; pos< length; pos++){
realCol[pos] = pos + firstCol + shift;
realInd[pos] = pos;
}
const Index Zero = firstCol + shift;
VectorType temp;
for (int i = 1; i < length - 1; i++){
const Index I = i + Zero;
const Index realI = realInd[i];
const Index j = permutation[length - i] - Zero;
const Index J = realCol[j];
//diag displace
aux = m_computed(I, I);
m_computed(I, I) = m_computed(J, J);
m_computed(J, J) = aux;
//firstrow displace
aux = m_computed(I, Zero);
m_computed(I, Zero) = m_computed(J, Zero);
m_computed(J, Zero) = aux;
// change columns
if (compU) {
temp = m_naiveU.col(I - shift).segment(firstCol, length + 1);
m_naiveU.col(I - shift).segment(firstCol, length + 1) <<
m_naiveU.col(J - shift).segment(firstCol, length + 1);
m_naiveU.col(J - shift).segment(firstCol, length + 1) << temp;
}
else
{
temp = m_naiveU.col(I - shift).segment(0, 2);
m_naiveU.col(I - shift).segment(0, 2) <<
m_naiveU.col(J - shift).segment(0, 2);
m_naiveU.col(J - shift).segment(0, 2) << temp;
}
if (compV) {
const Index CWI = I + firstColW - Zero;
const Index CWJ = J + firstColW - Zero;
temp = m_naiveV.col(CWI).segment(firstRowW, length);
m_naiveV.col(CWI).segment(firstRowW, length) << m_naiveV.col(CWJ).segment(firstRowW, length);
m_naiveV.col(CWJ).segment(firstRowW, length) << temp;
}
//update real pos
realCol[realI] = J;
realCol[j] = I;
realInd[J - Zero] = realI;
realInd[I - Zero] = j;
}
for (Index i = firstCol + shift + 1; i<lastCol + shift;i++){
if ((m_computed(i + 1, i + 1) - m_computed(i, i)) < EPS){
deflation44(firstCol ,
firstCol + shift,
firstRowW,
firstColW,
i - Zero,
i + 1 - Zero,
length);
}
}
delete [] permutation;
delete [] realInd;
delete [] realCol;
}//end deflation
namespace internal{
template<typename _MatrixType, typename Rhs>
struct solve_retval<BDCSVD<_MatrixType>, Rhs>
: solve_retval_base<BDCSVD<_MatrixType>, Rhs>
{
typedef BDCSVD<_MatrixType> BDCSVDType;
EIGEN_MAKE_SOLVE_HELPERS(BDCSVDType, Rhs)
template<typename Dest> void evalTo(Dest& dst) const
{
eigen_assert(rhs().rows() == dec().rows());
// A = U S V^*
// So A^{ - 1} = V S^{ - 1} U^*
Index diagSize = (std::min)(dec().rows(), dec().cols());
typename BDCSVDType::SingularValuesType invertedSingVals(diagSize);
Index nonzeroSingVals = dec().nonzeroSingularValues();
invertedSingVals.head(nonzeroSingVals) = dec().singularValues().head(nonzeroSingVals).array().inverse();
invertedSingVals.tail(diagSize - nonzeroSingVals).setZero();
dst = dec().matrixV().leftCols(diagSize)
* invertedSingVals.asDiagonal()
* dec().matrixU().leftCols(diagSize).adjoint()
* rhs();
return;
}
};
} //end namespace internal
/** \svd_module
*
* \return the singular value decomposition of \c *this computed by
* BDC Algorithm
*
* \sa class BDCSVD
*/
/*
template<typename Derived>
BDCSVD<typename MatrixBase<Derived>::PlainObject>
MatrixBase<Derived>::bdcSvd(unsigned int computationOptions) const
{
return BDCSVD<PlainObject>(*this, computationOptions);
}
*/
} // end namespace Eigen
#endif

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_SVD_SRCS "*.h")
INSTALL(FILES
${Eigen_SVD_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}unsupported/Eigen/src/SVD COMPONENT Devel
)

View File

@@ -0,0 +1,782 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009-2010 Benoit Jacob <jacob.benoit.1@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_JACOBISVD_H
#define EIGEN_JACOBISVD_H
namespace Eigen {
namespace internal {
// forward declaration (needed by ICC)
// the empty body is required by MSVC
template<typename MatrixType, int QRPreconditioner,
bool IsComplex = NumTraits<typename MatrixType::Scalar>::IsComplex>
struct svd_precondition_2x2_block_to_be_real {};
/*** QR preconditioners (R-SVD)
***
*** Their role is to reduce the problem of computing the SVD to the case of a square matrix.
*** This approach, known as R-SVD, is an optimization for rectangular-enough matrices, and is a requirement for
*** JacobiSVD which by itself is only able to work on square matrices.
***/
enum { PreconditionIfMoreColsThanRows, PreconditionIfMoreRowsThanCols };
template<typename MatrixType, int QRPreconditioner, int Case>
struct qr_preconditioner_should_do_anything
{
enum { a = MatrixType::RowsAtCompileTime != Dynamic &&
MatrixType::ColsAtCompileTime != Dynamic &&
MatrixType::ColsAtCompileTime <= MatrixType::RowsAtCompileTime,
b = MatrixType::RowsAtCompileTime != Dynamic &&
MatrixType::ColsAtCompileTime != Dynamic &&
MatrixType::RowsAtCompileTime <= MatrixType::ColsAtCompileTime,
ret = !( (QRPreconditioner == NoQRPreconditioner) ||
(Case == PreconditionIfMoreColsThanRows && bool(a)) ||
(Case == PreconditionIfMoreRowsThanCols && bool(b)) )
};
};
template<typename MatrixType, int QRPreconditioner, int Case,
bool DoAnything = qr_preconditioner_should_do_anything<MatrixType, QRPreconditioner, Case>::ret
> struct qr_preconditioner_impl {};
template<typename MatrixType, int QRPreconditioner, int Case>
class qr_preconditioner_impl<MatrixType, QRPreconditioner, Case, false>
{
public:
typedef typename MatrixType::Index Index;
void allocate(const JacobiSVD<MatrixType, QRPreconditioner>&) {}
bool run(JacobiSVD<MatrixType, QRPreconditioner>&, const MatrixType&)
{
return false;
}
};
/*** preconditioner using FullPivHouseholderQR ***/
template<typename MatrixType>
class qr_preconditioner_impl<MatrixType, FullPivHouseholderQRPreconditioner, PreconditionIfMoreRowsThanCols, true>
{
public:
typedef typename MatrixType::Index Index;
typedef typename MatrixType::Scalar Scalar;
enum
{
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime
};
typedef Matrix<Scalar, 1, RowsAtCompileTime, RowMajor, 1, MaxRowsAtCompileTime> WorkspaceType;
void allocate(const JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner>& svd)
{
if (svd.rows() != m_qr.rows() || svd.cols() != m_qr.cols())
{
m_qr.~QRType();
::new (&m_qr) QRType(svd.rows(), svd.cols());
}
if (svd.m_computeFullU) m_workspace.resize(svd.rows());
}
bool run(JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner>& svd, const MatrixType& matrix)
{
if(matrix.rows() > matrix.cols())
{
m_qr.compute(matrix);
svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.cols(),matrix.cols()).template triangularView<Upper>();
if(svd.m_computeFullU) m_qr.matrixQ().evalTo(svd.m_matrixU, m_workspace);
if(svd.computeV()) svd.m_matrixV = m_qr.colsPermutation();
return true;
}
return false;
}
private:
typedef FullPivHouseholderQR<MatrixType> QRType;
QRType m_qr;
WorkspaceType m_workspace;
};
template<typename MatrixType>
class qr_preconditioner_impl<MatrixType, FullPivHouseholderQRPreconditioner, PreconditionIfMoreColsThanRows, true>
{
public:
typedef typename MatrixType::Index Index;
typedef typename MatrixType::Scalar Scalar;
enum
{
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
Options = MatrixType::Options
};
typedef Matrix<Scalar, ColsAtCompileTime, RowsAtCompileTime, Options, MaxColsAtCompileTime, MaxRowsAtCompileTime>
TransposeTypeWithSameStorageOrder;
void allocate(const JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner>& svd)
{
if (svd.cols() != m_qr.rows() || svd.rows() != m_qr.cols())
{
m_qr.~QRType();
::new (&m_qr) QRType(svd.cols(), svd.rows());
}
m_adjoint.resize(svd.cols(), svd.rows());
if (svd.m_computeFullV) m_workspace.resize(svd.cols());
}
bool run(JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner>& svd, const MatrixType& matrix)
{
if(matrix.cols() > matrix.rows())
{
m_adjoint = matrix.adjoint();
m_qr.compute(m_adjoint);
svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.rows(),matrix.rows()).template triangularView<Upper>().adjoint();
if(svd.m_computeFullV) m_qr.matrixQ().evalTo(svd.m_matrixV, m_workspace);
if(svd.computeU()) svd.m_matrixU = m_qr.colsPermutation();
return true;
}
else return false;
}
private:
typedef FullPivHouseholderQR<TransposeTypeWithSameStorageOrder> QRType;
QRType m_qr;
TransposeTypeWithSameStorageOrder m_adjoint;
typename internal::plain_row_type<MatrixType>::type m_workspace;
};
/*** preconditioner using ColPivHouseholderQR ***/
template<typename MatrixType>
class qr_preconditioner_impl<MatrixType, ColPivHouseholderQRPreconditioner, PreconditionIfMoreRowsThanCols, true>
{
public:
typedef typename MatrixType::Index Index;
void allocate(const JacobiSVD<MatrixType, ColPivHouseholderQRPreconditioner>& svd)
{
if (svd.rows() != m_qr.rows() || svd.cols() != m_qr.cols())
{
m_qr.~QRType();
::new (&m_qr) QRType(svd.rows(), svd.cols());
}
if (svd.m_computeFullU) m_workspace.resize(svd.rows());
else if (svd.m_computeThinU) m_workspace.resize(svd.cols());
}
bool run(JacobiSVD<MatrixType, ColPivHouseholderQRPreconditioner>& svd, const MatrixType& matrix)
{
if(matrix.rows() > matrix.cols())
{
m_qr.compute(matrix);
svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.cols(),matrix.cols()).template triangularView<Upper>();
if(svd.m_computeFullU) m_qr.householderQ().evalTo(svd.m_matrixU, m_workspace);
else if(svd.m_computeThinU)
{
svd.m_matrixU.setIdentity(matrix.rows(), matrix.cols());
m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixU, m_workspace);
}
if(svd.computeV()) svd.m_matrixV = m_qr.colsPermutation();
return true;
}
return false;
}
private:
typedef ColPivHouseholderQR<MatrixType> QRType;
QRType m_qr;
typename internal::plain_col_type<MatrixType>::type m_workspace;
};
template<typename MatrixType>
class qr_preconditioner_impl<MatrixType, ColPivHouseholderQRPreconditioner, PreconditionIfMoreColsThanRows, true>
{
public:
typedef typename MatrixType::Index Index;
typedef typename MatrixType::Scalar Scalar;
enum
{
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
Options = MatrixType::Options
};
typedef Matrix<Scalar, ColsAtCompileTime, RowsAtCompileTime, Options, MaxColsAtCompileTime, MaxRowsAtCompileTime>
TransposeTypeWithSameStorageOrder;
void allocate(const JacobiSVD<MatrixType, ColPivHouseholderQRPreconditioner>& svd)
{
if (svd.cols() != m_qr.rows() || svd.rows() != m_qr.cols())
{
m_qr.~QRType();
::new (&m_qr) QRType(svd.cols(), svd.rows());
}
if (svd.m_computeFullV) m_workspace.resize(svd.cols());
else if (svd.m_computeThinV) m_workspace.resize(svd.rows());
m_adjoint.resize(svd.cols(), svd.rows());
}
bool run(JacobiSVD<MatrixType, ColPivHouseholderQRPreconditioner>& svd, const MatrixType& matrix)
{
if(matrix.cols() > matrix.rows())
{
m_adjoint = matrix.adjoint();
m_qr.compute(m_adjoint);
svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.rows(),matrix.rows()).template triangularView<Upper>().adjoint();
if(svd.m_computeFullV) m_qr.householderQ().evalTo(svd.m_matrixV, m_workspace);
else if(svd.m_computeThinV)
{
svd.m_matrixV.setIdentity(matrix.cols(), matrix.rows());
m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixV, m_workspace);
}
if(svd.computeU()) svd.m_matrixU = m_qr.colsPermutation();
return true;
}
else return false;
}
private:
typedef ColPivHouseholderQR<TransposeTypeWithSameStorageOrder> QRType;
QRType m_qr;
TransposeTypeWithSameStorageOrder m_adjoint;
typename internal::plain_row_type<MatrixType>::type m_workspace;
};
/*** preconditioner using HouseholderQR ***/
template<typename MatrixType>
class qr_preconditioner_impl<MatrixType, HouseholderQRPreconditioner, PreconditionIfMoreRowsThanCols, true>
{
public:
typedef typename MatrixType::Index Index;
void allocate(const JacobiSVD<MatrixType, HouseholderQRPreconditioner>& svd)
{
if (svd.rows() != m_qr.rows() || svd.cols() != m_qr.cols())
{
m_qr.~QRType();
::new (&m_qr) QRType(svd.rows(), svd.cols());
}
if (svd.m_computeFullU) m_workspace.resize(svd.rows());
else if (svd.m_computeThinU) m_workspace.resize(svd.cols());
}
bool run(JacobiSVD<MatrixType, HouseholderQRPreconditioner>& svd, const MatrixType& matrix)
{
if(matrix.rows() > matrix.cols())
{
m_qr.compute(matrix);
svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.cols(),matrix.cols()).template triangularView<Upper>();
if(svd.m_computeFullU) m_qr.householderQ().evalTo(svd.m_matrixU, m_workspace);
else if(svd.m_computeThinU)
{
svd.m_matrixU.setIdentity(matrix.rows(), matrix.cols());
m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixU, m_workspace);
}
if(svd.computeV()) svd.m_matrixV.setIdentity(matrix.cols(), matrix.cols());
return true;
}
return false;
}
private:
typedef HouseholderQR<MatrixType> QRType;
QRType m_qr;
typename internal::plain_col_type<MatrixType>::type m_workspace;
};
template<typename MatrixType>
class qr_preconditioner_impl<MatrixType, HouseholderQRPreconditioner, PreconditionIfMoreColsThanRows, true>
{
public:
typedef typename MatrixType::Index Index;
typedef typename MatrixType::Scalar Scalar;
enum
{
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
Options = MatrixType::Options
};
typedef Matrix<Scalar, ColsAtCompileTime, RowsAtCompileTime, Options, MaxColsAtCompileTime, MaxRowsAtCompileTime>
TransposeTypeWithSameStorageOrder;
void allocate(const JacobiSVD<MatrixType, HouseholderQRPreconditioner>& svd)
{
if (svd.cols() != m_qr.rows() || svd.rows() != m_qr.cols())
{
m_qr.~QRType();
::new (&m_qr) QRType(svd.cols(), svd.rows());
}
if (svd.m_computeFullV) m_workspace.resize(svd.cols());
else if (svd.m_computeThinV) m_workspace.resize(svd.rows());
m_adjoint.resize(svd.cols(), svd.rows());
}
bool run(JacobiSVD<MatrixType, HouseholderQRPreconditioner>& svd, const MatrixType& matrix)
{
if(matrix.cols() > matrix.rows())
{
m_adjoint = matrix.adjoint();
m_qr.compute(m_adjoint);
svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.rows(),matrix.rows()).template triangularView<Upper>().adjoint();
if(svd.m_computeFullV) m_qr.householderQ().evalTo(svd.m_matrixV, m_workspace);
else if(svd.m_computeThinV)
{
svd.m_matrixV.setIdentity(matrix.cols(), matrix.rows());
m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixV, m_workspace);
}
if(svd.computeU()) svd.m_matrixU.setIdentity(matrix.rows(), matrix.rows());
return true;
}
else return false;
}
private:
typedef HouseholderQR<TransposeTypeWithSameStorageOrder> QRType;
QRType m_qr;
TransposeTypeWithSameStorageOrder m_adjoint;
typename internal::plain_row_type<MatrixType>::type m_workspace;
};
/*** 2x2 SVD implementation
***
*** JacobiSVD consists in performing a series of 2x2 SVD subproblems
***/
template<typename MatrixType, int QRPreconditioner>
struct svd_precondition_2x2_block_to_be_real<MatrixType, QRPreconditioner, false>
{
typedef JacobiSVD<MatrixType, QRPreconditioner> SVD;
typedef typename SVD::Index Index;
static void run(typename SVD::WorkMatrixType&, SVD&, Index, Index) {}
};
template<typename MatrixType, int QRPreconditioner>
struct svd_precondition_2x2_block_to_be_real<MatrixType, QRPreconditioner, true>
{
typedef JacobiSVD<MatrixType, QRPreconditioner> SVD;
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::RealScalar RealScalar;
typedef typename SVD::Index Index;
static void run(typename SVD::WorkMatrixType& work_matrix, SVD& svd, Index p, Index q)
{
using std::sqrt;
Scalar z;
JacobiRotation<Scalar> rot;
RealScalar n = sqrt(numext::abs2(work_matrix.coeff(p,p)) + numext::abs2(work_matrix.coeff(q,p)));
if(n==0)
{
z = abs(work_matrix.coeff(p,q)) / work_matrix.coeff(p,q);
work_matrix.row(p) *= z;
if(svd.computeU()) svd.m_matrixU.col(p) *= conj(z);
z = abs(work_matrix.coeff(q,q)) / work_matrix.coeff(q,q);
work_matrix.row(q) *= z;
if(svd.computeU()) svd.m_matrixU.col(q) *= conj(z);
}
else
{
rot.c() = conj(work_matrix.coeff(p,p)) / n;
rot.s() = work_matrix.coeff(q,p) / n;
work_matrix.applyOnTheLeft(p,q,rot);
if(svd.computeU()) svd.m_matrixU.applyOnTheRight(p,q,rot.adjoint());
if(work_matrix.coeff(p,q) != Scalar(0))
{
Scalar z = abs(work_matrix.coeff(p,q)) / work_matrix.coeff(p,q);
work_matrix.col(q) *= z;
if(svd.computeV()) svd.m_matrixV.col(q) *= z;
}
if(work_matrix.coeff(q,q) != Scalar(0))
{
z = abs(work_matrix.coeff(q,q)) / work_matrix.coeff(q,q);
work_matrix.row(q) *= z;
if(svd.computeU()) svd.m_matrixU.col(q) *= conj(z);
}
}
}
};
template<typename MatrixType, typename RealScalar, typename Index>
void real_2x2_jacobi_svd(const MatrixType& matrix, Index p, Index q,
JacobiRotation<RealScalar> *j_left,
JacobiRotation<RealScalar> *j_right)
{
using std::sqrt;
Matrix<RealScalar,2,2> m;
m << numext::real(matrix.coeff(p,p)), numext::real(matrix.coeff(p,q)),
numext::real(matrix.coeff(q,p)), numext::real(matrix.coeff(q,q));
JacobiRotation<RealScalar> rot1;
RealScalar t = m.coeff(0,0) + m.coeff(1,1);
RealScalar d = m.coeff(1,0) - m.coeff(0,1);
if(t == RealScalar(0))
{
rot1.c() = RealScalar(0);
rot1.s() = d > RealScalar(0) ? RealScalar(1) : RealScalar(-1);
}
else
{
RealScalar u = d / t;
rot1.c() = RealScalar(1) / sqrt(RealScalar(1) + numext::abs2(u));
rot1.s() = rot1.c() * u;
}
m.applyOnTheLeft(0,1,rot1);
j_right->makeJacobi(m,0,1);
*j_left = rot1 * j_right->transpose();
}
} // end namespace internal
/** \ingroup SVD_Module
*
*
* \class JacobiSVD
*
* \brief Two-sided Jacobi SVD decomposition of a rectangular matrix
*
* \param MatrixType the type of the matrix of which we are computing the SVD decomposition
* \param QRPreconditioner this optional parameter allows to specify the type of QR decomposition that will be used internally
* for the R-SVD step for non-square matrices. See discussion of possible values below.
*
* SVD decomposition consists in decomposing any n-by-p matrix \a A as a product
* \f[ A = U S V^* \f]
* where \a U is a n-by-n unitary, \a V is a p-by-p unitary, and \a S is a n-by-p real positive matrix which is zero outside of its main diagonal;
* the diagonal entries of S are known as the \em singular \em values of \a A and the columns of \a U and \a V are known as the left
* and right \em singular \em vectors of \a A respectively.
*
* Singular values are always sorted in decreasing order.
*
* This JacobiSVD decomposition computes only the singular values by default. If you want \a U or \a V, you need to ask for them explicitly.
*
* You can ask for only \em thin \a U or \a V to be computed, meaning the following. In case of a rectangular n-by-p matrix, letting \a m be the
* smaller value among \a n and \a p, there are only \a m singular vectors; the remaining columns of \a U and \a V do not correspond to actual
* singular vectors. Asking for \em thin \a U or \a V means asking for only their \a m first columns to be formed. So \a U is then a n-by-m matrix,
* and \a V is then a p-by-m matrix. Notice that thin \a U and \a V are all you need for (least squares) solving.
*
* Here's an example demonstrating basic usage:
* \include JacobiSVD_basic.cpp
* Output: \verbinclude JacobiSVD_basic.out
*
* This JacobiSVD class is a two-sided Jacobi R-SVD decomposition, ensuring optimal reliability and accuracy. The downside is that it's slower than
* bidiagonalizing SVD algorithms for large square matrices; however its complexity is still \f$ O(n^2p) \f$ where \a n is the smaller dimension and
* \a p is the greater dimension, meaning that it is still of the same order of complexity as the faster bidiagonalizing R-SVD algorithms.
* In particular, like any R-SVD, it takes advantage of non-squareness in that its complexity is only linear in the greater dimension.
*
* If the input matrix has inf or nan coefficients, the result of the computation is undefined, but the computation is guaranteed to
* terminate in finite (and reasonable) time.
*
* The possible values for QRPreconditioner are:
* \li ColPivHouseholderQRPreconditioner is the default. In practice it's very safe. It uses column-pivoting QR.
* \li FullPivHouseholderQRPreconditioner, is the safest and slowest. It uses full-pivoting QR.
* Contrary to other QRs, it doesn't allow computing thin unitaries.
* \li HouseholderQRPreconditioner is the fastest, and less safe and accurate than the pivoting variants. It uses non-pivoting QR.
* This is very similar in safety and accuracy to the bidiagonalization process used by bidiagonalizing SVD algorithms (since bidiagonalization
* is inherently non-pivoting). However the resulting SVD is still more reliable than bidiagonalizing SVDs because the Jacobi-based iterarive
* process is more reliable than the optimized bidiagonal SVD iterations.
* \li NoQRPreconditioner allows not to use a QR preconditioner at all. This is useful if you know that you will only be computing
* JacobiSVD decompositions of square matrices. Non-square matrices require a QR preconditioner. Using this option will result in
* faster compilation and smaller executable code. It won't significantly speed up computation, since JacobiSVD is always checking
* if QR preconditioning is needed before applying it anyway.
*
* \sa MatrixBase::jacobiSvd()
*/
template<typename _MatrixType, int QRPreconditioner>
class JacobiSVD : public SVDBase<_MatrixType>
{
public:
typedef _MatrixType MatrixType;
typedef typename MatrixType::Scalar Scalar;
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
typedef typename MatrixType::Index Index;
enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
DiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_DYNAMIC(RowsAtCompileTime,ColsAtCompileTime),
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
MaxDiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_FIXED(MaxRowsAtCompileTime,MaxColsAtCompileTime),
MatrixOptions = MatrixType::Options
};
typedef Matrix<Scalar, RowsAtCompileTime, RowsAtCompileTime,
MatrixOptions, MaxRowsAtCompileTime, MaxRowsAtCompileTime>
MatrixUType;
typedef Matrix<Scalar, ColsAtCompileTime, ColsAtCompileTime,
MatrixOptions, MaxColsAtCompileTime, MaxColsAtCompileTime>
MatrixVType;
typedef typename internal::plain_diag_type<MatrixType, RealScalar>::type SingularValuesType;
typedef typename internal::plain_row_type<MatrixType>::type RowType;
typedef typename internal::plain_col_type<MatrixType>::type ColType;
typedef Matrix<Scalar, DiagSizeAtCompileTime, DiagSizeAtCompileTime,
MatrixOptions, MaxDiagSizeAtCompileTime, MaxDiagSizeAtCompileTime>
WorkMatrixType;
/** \brief Default Constructor.
*
* The default constructor is useful in cases in which the user intends to
* perform decompositions via JacobiSVD::compute(const MatrixType&).
*/
JacobiSVD()
: SVDBase<_MatrixType>::SVDBase()
{}
/** \brief Default Constructor with memory preallocation
*
* Like the default constructor but with preallocation of the internal data
* according to the specified problem size.
* \sa JacobiSVD()
*/
JacobiSVD(Index rows, Index cols, unsigned int computationOptions = 0)
: SVDBase<_MatrixType>::SVDBase()
{
allocate(rows, cols, computationOptions);
}
/** \brief Constructor performing the decomposition of given matrix.
*
* \param matrix the matrix to decompose
* \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed.
* By default, none is computed. This is a bit-field, the possible bits are #ComputeFullU, #ComputeThinU,
* #ComputeFullV, #ComputeThinV.
*
* Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not
* available with the (non-default) FullPivHouseholderQR preconditioner.
*/
JacobiSVD(const MatrixType& matrix, unsigned int computationOptions = 0)
: SVDBase<_MatrixType>::SVDBase()
{
compute(matrix, computationOptions);
}
/** \brief Method performing the decomposition of given matrix using custom options.
*
* \param matrix the matrix to decompose
* \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed.
* By default, none is computed. This is a bit-field, the possible bits are #ComputeFullU, #ComputeThinU,
* #ComputeFullV, #ComputeThinV.
*
* Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not
* available with the (non-default) FullPivHouseholderQR preconditioner.
*/
SVDBase<MatrixType>& compute(const MatrixType& matrix, unsigned int computationOptions);
/** \brief Method performing the decomposition of given matrix using current options.
*
* \param matrix the matrix to decompose
*
* This method uses the current \a computationOptions, as already passed to the constructor or to compute(const MatrixType&, unsigned int).
*/
SVDBase<MatrixType>& compute(const MatrixType& matrix)
{
return compute(matrix, this->m_computationOptions);
}
/** \returns a (least squares) solution of \f$ A x = b \f$ using the current SVD decomposition of A.
*
* \param b the right-hand-side of the equation to solve.
*
* \note Solving requires both U and V to be computed. Thin U and V are enough, there is no need for full U or V.
*
* \note SVD solving is implicitly least-squares. Thus, this method serves both purposes of exact solving and least-squares solving.
* In other words, the returned solution is guaranteed to minimize the Euclidean norm \f$ \Vert A x - b \Vert \f$.
*/
template<typename Rhs>
inline const internal::solve_retval<JacobiSVD, Rhs>
solve(const MatrixBase<Rhs>& b) const
{
eigen_assert(this->m_isInitialized && "JacobiSVD is not initialized.");
eigen_assert(SVDBase<MatrixType>::computeU() && SVDBase<MatrixType>::computeV() && "JacobiSVD::solve() requires both unitaries U and V to be computed (thin unitaries suffice).");
return internal::solve_retval<JacobiSVD, Rhs>(*this, b.derived());
}
private:
void allocate(Index rows, Index cols, unsigned int computationOptions);
protected:
WorkMatrixType m_workMatrix;
template<typename __MatrixType, int _QRPreconditioner, bool _IsComplex>
friend struct internal::svd_precondition_2x2_block_to_be_real;
template<typename __MatrixType, int _QRPreconditioner, int _Case, bool _DoAnything>
friend struct internal::qr_preconditioner_impl;
internal::qr_preconditioner_impl<MatrixType, QRPreconditioner, internal::PreconditionIfMoreColsThanRows> m_qr_precond_morecols;
internal::qr_preconditioner_impl<MatrixType, QRPreconditioner, internal::PreconditionIfMoreRowsThanCols> m_qr_precond_morerows;
};
template<typename MatrixType, int QRPreconditioner>
void JacobiSVD<MatrixType, QRPreconditioner>::allocate(Index rows, Index cols, unsigned int computationOptions)
{
if (SVDBase<MatrixType>::allocate(rows, cols, computationOptions)) return;
if (QRPreconditioner == FullPivHouseholderQRPreconditioner)
{
eigen_assert(!(this->m_computeThinU || this->m_computeThinV) &&
"JacobiSVD: can't compute thin U or thin V with the FullPivHouseholderQR preconditioner. "
"Use the ColPivHouseholderQR preconditioner instead.");
}
m_workMatrix.resize(this->m_diagSize, this->m_diagSize);
if(this->m_cols>this->m_rows) m_qr_precond_morecols.allocate(*this);
if(this->m_rows>this->m_cols) m_qr_precond_morerows.allocate(*this);
}
template<typename MatrixType, int QRPreconditioner>
SVDBase<MatrixType>&
JacobiSVD<MatrixType, QRPreconditioner>::compute(const MatrixType& matrix, unsigned int computationOptions)
{
using std::abs;
allocate(matrix.rows(), matrix.cols(), computationOptions);
// currently we stop when we reach precision 2*epsilon as the last bit of precision can require an unreasonable number of iterations,
// only worsening the precision of U and V as we accumulate more rotations
const RealScalar precision = RealScalar(2) * NumTraits<Scalar>::epsilon();
// limit for very small denormal numbers to be considered zero in order to avoid infinite loops (see bug 286)
const RealScalar considerAsZero = RealScalar(2) * std::numeric_limits<RealScalar>::denorm_min();
/*** step 1. The R-SVD step: we use a QR decomposition to reduce to the case of a square matrix */
if(!m_qr_precond_morecols.run(*this, matrix) && !m_qr_precond_morerows.run(*this, matrix))
{
m_workMatrix = matrix.block(0,0,this->m_diagSize,this->m_diagSize);
if(this->m_computeFullU) this->m_matrixU.setIdentity(this->m_rows,this->m_rows);
if(this->m_computeThinU) this->m_matrixU.setIdentity(this->m_rows,this->m_diagSize);
if(this->m_computeFullV) this->m_matrixV.setIdentity(this->m_cols,this->m_cols);
if(this->m_computeThinV) this->m_matrixV.setIdentity(this->m_cols, this->m_diagSize);
}
/*** step 2. The main Jacobi SVD iteration. ***/
bool finished = false;
while(!finished)
{
finished = true;
// do a sweep: for all index pairs (p,q), perform SVD of the corresponding 2x2 sub-matrix
for(Index p = 1; p < this->m_diagSize; ++p)
{
for(Index q = 0; q < p; ++q)
{
// if this 2x2 sub-matrix is not diagonal already...
// notice that this comparison will evaluate to false if any NaN is involved, ensuring that NaN's don't
// keep us iterating forever. Similarly, small denormal numbers are considered zero.
using std::max;
RealScalar threshold = (max)(considerAsZero, precision * (max)(abs(m_workMatrix.coeff(p,p)),
abs(m_workMatrix.coeff(q,q))));
if((max)(abs(m_workMatrix.coeff(p,q)),abs(m_workMatrix.coeff(q,p))) > threshold)
{
finished = false;
// perform SVD decomposition of 2x2 sub-matrix corresponding to indices p,q to make it diagonal
internal::svd_precondition_2x2_block_to_be_real<MatrixType, QRPreconditioner>::run(m_workMatrix, *this, p, q);
JacobiRotation<RealScalar> j_left, j_right;
internal::real_2x2_jacobi_svd(m_workMatrix, p, q, &j_left, &j_right);
// accumulate resulting Jacobi rotations
m_workMatrix.applyOnTheLeft(p,q,j_left);
if(SVDBase<MatrixType>::computeU()) this->m_matrixU.applyOnTheRight(p,q,j_left.transpose());
m_workMatrix.applyOnTheRight(p,q,j_right);
if(SVDBase<MatrixType>::computeV()) this->m_matrixV.applyOnTheRight(p,q,j_right);
}
}
}
}
/*** step 3. The work matrix is now diagonal, so ensure it's positive so its diagonal entries are the singular values ***/
for(Index i = 0; i < this->m_diagSize; ++i)
{
RealScalar a = abs(m_workMatrix.coeff(i,i));
this->m_singularValues.coeffRef(i) = a;
if(SVDBase<MatrixType>::computeU() && (a!=RealScalar(0))) this->m_matrixU.col(i) *= this->m_workMatrix.coeff(i,i)/a;
}
/*** step 4. Sort singular values in descending order and compute the number of nonzero singular values ***/
this->m_nonzeroSingularValues = this->m_diagSize;
for(Index i = 0; i < this->m_diagSize; i++)
{
Index pos;
RealScalar maxRemainingSingularValue = this->m_singularValues.tail(this->m_diagSize-i).maxCoeff(&pos);
if(maxRemainingSingularValue == RealScalar(0))
{
this->m_nonzeroSingularValues = i;
break;
}
if(pos)
{
pos += i;
std::swap(this->m_singularValues.coeffRef(i), this->m_singularValues.coeffRef(pos));
if(SVDBase<MatrixType>::computeU()) this->m_matrixU.col(pos).swap(this->m_matrixU.col(i));
if(SVDBase<MatrixType>::computeV()) this->m_matrixV.col(pos).swap(this->m_matrixV.col(i));
}
}
this->m_isInitialized = true;
return *this;
}
namespace internal {
template<typename _MatrixType, int QRPreconditioner, typename Rhs>
struct solve_retval<JacobiSVD<_MatrixType, QRPreconditioner>, Rhs>
: solve_retval_base<JacobiSVD<_MatrixType, QRPreconditioner>, Rhs>
{
typedef JacobiSVD<_MatrixType, QRPreconditioner> JacobiSVDType;
EIGEN_MAKE_SOLVE_HELPERS(JacobiSVDType,Rhs)
template<typename Dest> void evalTo(Dest& dst) const
{
eigen_assert(rhs().rows() == dec().rows());
// A = U S V^*
// So A^{-1} = V S^{-1} U^*
Index diagSize = (std::min)(dec().rows(), dec().cols());
typename JacobiSVDType::SingularValuesType invertedSingVals(diagSize);
Index nonzeroSingVals = dec().nonzeroSingularValues();
invertedSingVals.head(nonzeroSingVals) = dec().singularValues().head(nonzeroSingVals).array().inverse();
invertedSingVals.tail(diagSize - nonzeroSingVals).setZero();
dst = dec().matrixV().leftCols(diagSize)
* invertedSingVals.asDiagonal()
* dec().matrixU().leftCols(diagSize).adjoint()
* rhs();
}
};
} // end namespace internal
/** \svd_module
*
* \return the singular value decomposition of \c *this computed by two-sided
* Jacobi transformations.
*
* \sa class JacobiSVD
*/
template<typename Derived>
JacobiSVD<typename MatrixBase<Derived>::PlainObject>
MatrixBase<Derived>::jacobiSvd(unsigned int computationOptions) const
{
return JacobiSVD<PlainObject>(*this, computationOptions);
}
} // end namespace Eigen
#endif // EIGEN_JACOBISVD_H

View File

@@ -0,0 +1,236 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009-2010 Benoit Jacob <jacob.benoit.1@gmail.com>
//
// Copyright (C) 2013 Gauthier Brun <brun.gauthier@gmail.com>
// Copyright (C) 2013 Nicolas Carre <nicolas.carre@ensimag.fr>
// Copyright (C) 2013 Jean Ceccato <jean.ceccato@ensimag.fr>
// Copyright (C) 2013 Pierre Zoppitelli <pierre.zoppitelli@ensimag.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SVD_H
#define EIGEN_SVD_H
namespace Eigen {
/** \ingroup SVD_Module
*
*
* \class SVDBase
*
* \brief Mother class of SVD classes algorithms
*
* \param MatrixType the type of the matrix of which we are computing the SVD decomposition
* SVD decomposition consists in decomposing any n-by-p matrix \a A as a product
* \f[ A = U S V^* \f]
* where \a U is a n-by-n unitary, \a V is a p-by-p unitary, and \a S is a n-by-p real positive matrix which is zero outside of its main diagonal;
* the diagonal entries of S are known as the \em singular \em values of \a A and the columns of \a U and \a V are known as the left
* and right \em singular \em vectors of \a A respectively.
*
* Singular values are always sorted in decreasing order.
*
*
* You can ask for only \em thin \a U or \a V to be computed, meaning the following. In case of a rectangular n-by-p matrix, letting \a m be the
* smaller value among \a n and \a p, there are only \a m singular vectors; the remaining columns of \a U and \a V do not correspond to actual
* singular vectors. Asking for \em thin \a U or \a V means asking for only their \a m first columns to be formed. So \a U is then a n-by-m matrix,
* and \a V is then a p-by-m matrix. Notice that thin \a U and \a V are all you need for (least squares) solving.
*
* If the input matrix has inf or nan coefficients, the result of the computation is undefined, but the computation is guaranteed to
* terminate in finite (and reasonable) time.
* \sa MatrixBase::genericSvd()
*/
template<typename _MatrixType>
class SVDBase
{
public:
typedef _MatrixType MatrixType;
typedef typename MatrixType::Scalar Scalar;
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
typedef typename MatrixType::Index Index;
enum {
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
DiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_DYNAMIC(RowsAtCompileTime,ColsAtCompileTime),
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime,
MaxDiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_FIXED(MaxRowsAtCompileTime,MaxColsAtCompileTime),
MatrixOptions = MatrixType::Options
};
typedef Matrix<Scalar, RowsAtCompileTime, RowsAtCompileTime,
MatrixOptions, MaxRowsAtCompileTime, MaxRowsAtCompileTime>
MatrixUType;
typedef Matrix<Scalar, ColsAtCompileTime, ColsAtCompileTime,
MatrixOptions, MaxColsAtCompileTime, MaxColsAtCompileTime>
MatrixVType;
typedef typename internal::plain_diag_type<MatrixType, RealScalar>::type SingularValuesType;
typedef typename internal::plain_row_type<MatrixType>::type RowType;
typedef typename internal::plain_col_type<MatrixType>::type ColType;
typedef Matrix<Scalar, DiagSizeAtCompileTime, DiagSizeAtCompileTime,
MatrixOptions, MaxDiagSizeAtCompileTime, MaxDiagSizeAtCompileTime>
WorkMatrixType;
/** \brief Method performing the decomposition of given matrix using custom options.
*
* \param matrix the matrix to decompose
* \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed.
* By default, none is computed. This is a bit-field, the possible bits are #ComputeFullU, #ComputeThinU,
* #ComputeFullV, #ComputeThinV.
*
* Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not
* available with the (non-default) FullPivHouseholderQR preconditioner.
*/
SVDBase& compute(const MatrixType& matrix, unsigned int computationOptions);
/** \brief Method performing the decomposition of given matrix using current options.
*
* \param matrix the matrix to decompose
*
* This method uses the current \a computationOptions, as already passed to the constructor or to compute(const MatrixType&, unsigned int).
*/
//virtual SVDBase& compute(const MatrixType& matrix) = 0;
SVDBase& compute(const MatrixType& matrix);
/** \returns the \a U matrix.
*
* For the SVDBase decomposition of a n-by-p matrix, letting \a m be the minimum of \a n and \a p,
* the U matrix is n-by-n if you asked for #ComputeFullU, and is n-by-m if you asked for #ComputeThinU.
*
* The \a m first columns of \a U are the left singular vectors of the matrix being decomposed.
*
* This method asserts that you asked for \a U to be computed.
*/
const MatrixUType& matrixU() const
{
eigen_assert(m_isInitialized && "SVD is not initialized.");
eigen_assert(computeU() && "This SVD decomposition didn't compute U. Did you ask for it?");
return m_matrixU;
}
/** \returns the \a V matrix.
*
* For the SVD decomposition of a n-by-p matrix, letting \a m be the minimum of \a n and \a p,
* the V matrix is p-by-p if you asked for #ComputeFullV, and is p-by-m if you asked for ComputeThinV.
*
* The \a m first columns of \a V are the right singular vectors of the matrix being decomposed.
*
* This method asserts that you asked for \a V to be computed.
*/
const MatrixVType& matrixV() const
{
eigen_assert(m_isInitialized && "SVD is not initialized.");
eigen_assert(computeV() && "This SVD decomposition didn't compute V. Did you ask for it?");
return m_matrixV;
}
/** \returns the vector of singular values.
*
* For the SVD decomposition of a n-by-p matrix, letting \a m be the minimum of \a n and \a p, the
* returned vector has size \a m. Singular values are always sorted in decreasing order.
*/
const SingularValuesType& singularValues() const
{
eigen_assert(m_isInitialized && "SVD is not initialized.");
return m_singularValues;
}
/** \returns the number of singular values that are not exactly 0 */
Index nonzeroSingularValues() const
{
eigen_assert(m_isInitialized && "SVD is not initialized.");
return m_nonzeroSingularValues;
}
/** \returns true if \a U (full or thin) is asked for in this SVD decomposition */
inline bool computeU() const { return m_computeFullU || m_computeThinU; }
/** \returns true if \a V (full or thin) is asked for in this SVD decomposition */
inline bool computeV() const { return m_computeFullV || m_computeThinV; }
inline Index rows() const { return m_rows; }
inline Index cols() const { return m_cols; }
protected:
// return true if already allocated
bool allocate(Index rows, Index cols, unsigned int computationOptions) ;
MatrixUType m_matrixU;
MatrixVType m_matrixV;
SingularValuesType m_singularValues;
bool m_isInitialized, m_isAllocated;
bool m_computeFullU, m_computeThinU;
bool m_computeFullV, m_computeThinV;
unsigned int m_computationOptions;
Index m_nonzeroSingularValues, m_rows, m_cols, m_diagSize;
/** \brief Default Constructor.
*
* Default constructor of SVDBase
*/
SVDBase()
: m_isInitialized(false),
m_isAllocated(false),
m_computationOptions(0),
m_rows(-1), m_cols(-1)
{}
};
template<typename MatrixType>
bool SVDBase<MatrixType>::allocate(Index rows, Index cols, unsigned int computationOptions)
{
eigen_assert(rows >= 0 && cols >= 0);
if (m_isAllocated &&
rows == m_rows &&
cols == m_cols &&
computationOptions == m_computationOptions)
{
return true;
}
m_rows = rows;
m_cols = cols;
m_isInitialized = false;
m_isAllocated = true;
m_computationOptions = computationOptions;
m_computeFullU = (computationOptions & ComputeFullU) != 0;
m_computeThinU = (computationOptions & ComputeThinU) != 0;
m_computeFullV = (computationOptions & ComputeFullV) != 0;
m_computeThinV = (computationOptions & ComputeThinV) != 0;
eigen_assert(!(m_computeFullU && m_computeThinU) && "SVDBase: you can't ask for both full and thin U");
eigen_assert(!(m_computeFullV && m_computeThinV) && "SVDBase: you can't ask for both full and thin V");
eigen_assert(EIGEN_IMPLIES(m_computeThinU || m_computeThinV, MatrixType::ColsAtCompileTime==Dynamic) &&
"SVDBase: thin U and V are only available when your matrix has a dynamic number of columns.");
m_diagSize = (std::min)(m_rows, m_cols);
m_singularValues.resize(m_diagSize);
if(RowsAtCompileTime==Dynamic)
m_matrixU.resize(m_rows, m_computeFullU ? m_rows
: m_computeThinU ? m_diagSize
: 0);
if(ColsAtCompileTime==Dynamic)
m_matrixV.resize(m_cols, m_computeFullV ? m_cols
: m_computeThinV ? m_diagSize
: 0);
return false;
}
}// end namespace
#endif // EIGEN_SVD_H

View File

@@ -0,0 +1,29 @@
TO DO LIST
(optional optimization) - do all the allocations in the allocate part
- support static matrices
- return a error at compilation time when using integer matrices (int, long, std::complex<int>, ...)
to finish the algorithm :
-implement the last part of the algorithm as described on the reference paper.
You may find more information on that part on this paper
-to replace the call to JacobiSVD at the end of the divide algorithm, just after the call to
deflation.
(suggested step by step resolution)
0) comment the call to Jacobi in the last part of the divide method and everything right after
until the end of the method. What is commented can be a guideline to steps 3) 4) and 6)
1) solve the secular equation (Characteristic equation) on the values that are not null (zi!=0 and di!=0), after the deflation
wich should be uncommented in the divide method
2) remember the values of the singular values that are already computed (zi=0)
3) assign the singular values found in m_computed at the right places (with the ones found in step 2) )
in decreasing order
4) set the firstcol to zero (except the first element) in m_computed
5) compute all the singular vectors when CompV is set to true and only the left vectors when
CompV is set to false
6) multiply naiveU and naiveV to the right by the matrices found, only naiveU when CompV is set to
false, /!\ if CompU is false NaiveU has only 2 rows
7) delete everything commented in step 0)

View File

@@ -0,0 +1,21 @@
This unsupported package is about a divide and conquer algorithm to compute SVD.
The implementation follows as closely as possible the following reference paper :
http://www.cs.yale.edu/publications/techreports/tr933.pdf
The code documentation uses the same names for variables as the reference paper. The code, deflation included, is
working but there are a few things that could be optimised as explained in the TODOBdsvd.
In the code comments were put at the line where would be the third step of the algorithm so one could simply add the call
of a function doing the last part of the algorithm and that would not require any knowledge of the part we implemented.
In the TODOBdcsvd we explain what is the main difficulty of the last part and suggest a reference paper to help solve it.
The implemented has trouble with fixed size matrices.
In the actual implementation, it returns matrices of zero when ask to do a svd on an int matrix.
Paper for the third part:
http://www.stat.uchicago.edu/~lekheng/courses/302/classics/greengard-rokhlin.pdf

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_Skyline_SRCS "*.h")
INSTALL(FILES
${Eigen_Skyline_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/Skyline COMPONENT Devel
)

View File

@@ -0,0 +1,352 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008 Guillaume Saupin <guillaume.saupin@cea.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SKYLINEINPLACELU_H
#define EIGEN_SKYLINEINPLACELU_H
namespace Eigen {
/** \ingroup Skyline_Module
*
* \class SkylineInplaceLU
*
* \brief Inplace LU decomposition of a skyline matrix and associated features
*
* \param MatrixType the type of the matrix of which we are computing the LU factorization
*
*/
template<typename MatrixType>
class SkylineInplaceLU {
protected:
typedef typename MatrixType::Scalar Scalar;
typedef typename MatrixType::Index Index;
typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar;
public:
/** Creates a LU object and compute the respective factorization of \a matrix using
* flags \a flags. */
SkylineInplaceLU(MatrixType& matrix, int flags = 0)
: /*m_matrix(matrix.rows(), matrix.cols()),*/ m_flags(flags), m_status(0), m_lu(matrix) {
m_precision = RealScalar(0.1) * Eigen::dummy_precision<RealScalar > ();
m_lu.IsRowMajor ? computeRowMajor() : compute();
}
/** Sets the relative threshold value used to prune zero coefficients during the decomposition.
*
* Setting a value greater than zero speeds up computation, and yields to an imcomplete
* factorization with fewer non zero coefficients. Such approximate factors are especially
* useful to initialize an iterative solver.
*
* Note that the exact meaning of this parameter might depends on the actual
* backend. Moreover, not all backends support this feature.
*
* \sa precision() */
void setPrecision(RealScalar v) {
m_precision = v;
}
/** \returns the current precision.
*
* \sa setPrecision() */
RealScalar precision() const {
return m_precision;
}
/** Sets the flags. Possible values are:
* - CompleteFactorization
* - IncompleteFactorization
* - MemoryEfficient
* - one of the ordering methods
* - etc...
*
* \sa flags() */
void setFlags(int f) {
m_flags = f;
}
/** \returns the current flags */
int flags() const {
return m_flags;
}
void setOrderingMethod(int m) {
m_flags = m;
}
int orderingMethod() const {
return m_flags;
}
/** Computes/re-computes the LU factorization */
void compute();
void computeRowMajor();
/** \returns the lower triangular matrix L */
//inline const MatrixType& matrixL() const { return m_matrixL; }
/** \returns the upper triangular matrix U */
//inline const MatrixType& matrixU() const { return m_matrixU; }
template<typename BDerived, typename XDerived>
bool solve(const MatrixBase<BDerived> &b, MatrixBase<XDerived>* x,
const int transposed = 0) const;
/** \returns true if the factorization succeeded */
inline bool succeeded(void) const {
return m_succeeded;
}
protected:
RealScalar m_precision;
int m_flags;
mutable int m_status;
bool m_succeeded;
MatrixType& m_lu;
};
/** Computes / recomputes the in place LU decomposition of the SkylineInplaceLU.
* using the default algorithm.
*/
template<typename MatrixType>
//template<typename _Scalar>
void SkylineInplaceLU<MatrixType>::compute() {
const size_t rows = m_lu.rows();
const size_t cols = m_lu.cols();
eigen_assert(rows == cols && "We do not (yet) support rectangular LU.");
eigen_assert(!m_lu.IsRowMajor && "LU decomposition does not work with rowMajor Storage");
for (Index row = 0; row < rows; row++) {
const double pivot = m_lu.coeffDiag(row);
//Lower matrix Columns update
const Index& col = row;
for (typename MatrixType::InnerLowerIterator lIt(m_lu, col); lIt; ++lIt) {
lIt.valueRef() /= pivot;
}
//Upper matrix update -> contiguous memory access
typename MatrixType::InnerLowerIterator lIt(m_lu, col);
for (Index rrow = row + 1; rrow < m_lu.rows(); rrow++) {
typename MatrixType::InnerUpperIterator uItPivot(m_lu, row);
typename MatrixType::InnerUpperIterator uIt(m_lu, rrow);
const double coef = lIt.value();
uItPivot += (rrow - row - 1);
//update upper part -> contiguous memory access
for (++uItPivot; uIt && uItPivot;) {
uIt.valueRef() -= uItPivot.value() * coef;
++uIt;
++uItPivot;
}
++lIt;
}
//Upper matrix update -> non contiguous memory access
typename MatrixType::InnerLowerIterator lIt3(m_lu, col);
for (Index rrow = row + 1; rrow < m_lu.rows(); rrow++) {
typename MatrixType::InnerUpperIterator uItPivot(m_lu, row);
const double coef = lIt3.value();
//update lower part -> non contiguous memory access
for (Index i = 0; i < rrow - row - 1; i++) {
m_lu.coeffRefLower(rrow, row + i + 1) -= uItPivot.value() * coef;
++uItPivot;
}
++lIt3;
}
//update diag -> contiguous
typename MatrixType::InnerLowerIterator lIt2(m_lu, col);
for (Index rrow = row + 1; rrow < m_lu.rows(); rrow++) {
typename MatrixType::InnerUpperIterator uItPivot(m_lu, row);
typename MatrixType::InnerUpperIterator uIt(m_lu, rrow);
const double coef = lIt2.value();
uItPivot += (rrow - row - 1);
m_lu.coeffRefDiag(rrow) -= uItPivot.value() * coef;
++lIt2;
}
}
}
template<typename MatrixType>
void SkylineInplaceLU<MatrixType>::computeRowMajor() {
const size_t rows = m_lu.rows();
const size_t cols = m_lu.cols();
eigen_assert(rows == cols && "We do not (yet) support rectangular LU.");
eigen_assert(m_lu.IsRowMajor && "You're trying to apply rowMajor decomposition on a ColMajor matrix !");
for (Index row = 0; row < rows; row++) {
typename MatrixType::InnerLowerIterator llIt(m_lu, row);
for (Index col = llIt.col(); col < row; col++) {
if (m_lu.coeffExistLower(row, col)) {
const double diag = m_lu.coeffDiag(col);
typename MatrixType::InnerLowerIterator lIt(m_lu, row);
typename MatrixType::InnerUpperIterator uIt(m_lu, col);
const Index offset = lIt.col() - uIt.row();
Index stop = offset > 0 ? col - lIt.col() : col - uIt.row();
//#define VECTORIZE
#ifdef VECTORIZE
Map<VectorXd > rowVal(lIt.valuePtr() + (offset > 0 ? 0 : -offset), stop);
Map<VectorXd > colVal(uIt.valuePtr() + (offset > 0 ? offset : 0), stop);
Scalar newCoeff = m_lu.coeffLower(row, col) - rowVal.dot(colVal);
#else
if (offset > 0) //Skip zero value of lIt
uIt += offset;
else //Skip zero values of uIt
lIt += -offset;
Scalar newCoeff = m_lu.coeffLower(row, col);
for (Index k = 0; k < stop; ++k) {
const Scalar tmp = newCoeff;
newCoeff = tmp - lIt.value() * uIt.value();
++lIt;
++uIt;
}
#endif
m_lu.coeffRefLower(row, col) = newCoeff / diag;
}
}
//Upper matrix update
const Index col = row;
typename MatrixType::InnerUpperIterator uuIt(m_lu, col);
for (Index rrow = uuIt.row(); rrow < col; rrow++) {
typename MatrixType::InnerLowerIterator lIt(m_lu, rrow);
typename MatrixType::InnerUpperIterator uIt(m_lu, col);
const Index offset = lIt.col() - uIt.row();
Index stop = offset > 0 ? rrow - lIt.col() : rrow - uIt.row();
#ifdef VECTORIZE
Map<VectorXd > rowVal(lIt.valuePtr() + (offset > 0 ? 0 : -offset), stop);
Map<VectorXd > colVal(uIt.valuePtr() + (offset > 0 ? offset : 0), stop);
Scalar newCoeff = m_lu.coeffUpper(rrow, col) - rowVal.dot(colVal);
#else
if (offset > 0) //Skip zero value of lIt
uIt += offset;
else //Skip zero values of uIt
lIt += -offset;
Scalar newCoeff = m_lu.coeffUpper(rrow, col);
for (Index k = 0; k < stop; ++k) {
const Scalar tmp = newCoeff;
newCoeff = tmp - lIt.value() * uIt.value();
++lIt;
++uIt;
}
#endif
m_lu.coeffRefUpper(rrow, col) = newCoeff;
}
//Diag matrix update
typename MatrixType::InnerLowerIterator lIt(m_lu, row);
typename MatrixType::InnerUpperIterator uIt(m_lu, row);
const Index offset = lIt.col() - uIt.row();
Index stop = offset > 0 ? lIt.size() : uIt.size();
#ifdef VECTORIZE
Map<VectorXd > rowVal(lIt.valuePtr() + (offset > 0 ? 0 : -offset), stop);
Map<VectorXd > colVal(uIt.valuePtr() + (offset > 0 ? offset : 0), stop);
Scalar newCoeff = m_lu.coeffDiag(row) - rowVal.dot(colVal);
#else
if (offset > 0) //Skip zero value of lIt
uIt += offset;
else //Skip zero values of uIt
lIt += -offset;
Scalar newCoeff = m_lu.coeffDiag(row);
for (Index k = 0; k < stop; ++k) {
const Scalar tmp = newCoeff;
newCoeff = tmp - lIt.value() * uIt.value();
++lIt;
++uIt;
}
#endif
m_lu.coeffRefDiag(row) = newCoeff;
}
}
/** Computes *x = U^-1 L^-1 b
*
* If \a transpose is set to SvTranspose or SvAdjoint, the solution
* of the transposed/adjoint system is computed instead.
*
* Not all backends implement the solution of the transposed or
* adjoint system.
*/
template<typename MatrixType>
template<typename BDerived, typename XDerived>
bool SkylineInplaceLU<MatrixType>::solve(const MatrixBase<BDerived> &b, MatrixBase<XDerived>* x, const int transposed) const {
const size_t rows = m_lu.rows();
const size_t cols = m_lu.cols();
for (Index row = 0; row < rows; row++) {
x->coeffRef(row) = b.coeff(row);
Scalar newVal = x->coeff(row);
typename MatrixType::InnerLowerIterator lIt(m_lu, row);
Index col = lIt.col();
while (lIt.col() < row) {
newVal -= x->coeff(col++) * lIt.value();
++lIt;
}
x->coeffRef(row) = newVal;
}
for (Index col = rows - 1; col > 0; col--) {
x->coeffRef(col) = x->coeff(col) / m_lu.coeffDiag(col);
const Scalar x_col = x->coeff(col);
typename MatrixType::InnerUpperIterator uIt(m_lu, col);
uIt += uIt.size()-1;
while (uIt) {
x->coeffRef(uIt.row()) -= x_col * uIt.value();
//TODO : introduce --operator
uIt += -1;
}
}
x->coeffRef(0) = x->coeff(0) / m_lu.coeffDiag(0);
return true;
}
} // end namespace Eigen
#endif // EIGEN_SKYLINELU_H

View File

@@ -0,0 +1,862 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Guillaume Saupin <guillaume.saupin@cea.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SKYLINEMATRIX_H
#define EIGEN_SKYLINEMATRIX_H
#include "SkylineStorage.h"
#include "SkylineMatrixBase.h"
namespace Eigen {
/** \ingroup Skyline_Module
*
* \class SkylineMatrix
*
* \brief The main skyline matrix class
*
* This class implements a skyline matrix using the very uncommon storage
* scheme.
*
* \param _Scalar the scalar type, i.e. the type of the coefficients
* \param _Options Union of bit flags controlling the storage scheme. Currently the only possibility
* is RowMajor. The default is 0 which means column-major.
*
*
*/
namespace internal {
template<typename _Scalar, int _Options>
struct traits<SkylineMatrix<_Scalar, _Options> > {
typedef _Scalar Scalar;
typedef Sparse StorageKind;
enum {
RowsAtCompileTime = Dynamic,
ColsAtCompileTime = Dynamic,
MaxRowsAtCompileTime = Dynamic,
MaxColsAtCompileTime = Dynamic,
Flags = SkylineBit | _Options,
CoeffReadCost = NumTraits<Scalar>::ReadCost,
};
};
}
template<typename _Scalar, int _Options>
class SkylineMatrix
: public SkylineMatrixBase<SkylineMatrix<_Scalar, _Options> > {
public:
EIGEN_SKYLINE_GENERIC_PUBLIC_INTERFACE(SkylineMatrix)
EIGEN_SKYLINE_INHERIT_ASSIGNMENT_OPERATOR(SkylineMatrix, +=)
EIGEN_SKYLINE_INHERIT_ASSIGNMENT_OPERATOR(SkylineMatrix, -=)
using Base::IsRowMajor;
protected:
typedef SkylineMatrix<Scalar, (Flags&~RowMajorBit) | (IsRowMajor ? RowMajorBit : 0) > TransposedSkylineMatrix;
Index m_outerSize;
Index m_innerSize;
public:
Index* m_colStartIndex;
Index* m_rowStartIndex;
SkylineStorage<Scalar> m_data;
public:
inline Index rows() const {
return IsRowMajor ? m_outerSize : m_innerSize;
}
inline Index cols() const {
return IsRowMajor ? m_innerSize : m_outerSize;
}
inline Index innerSize() const {
return m_innerSize;
}
inline Index outerSize() const {
return m_outerSize;
}
inline Index upperNonZeros() const {
return m_data.upperSize();
}
inline Index lowerNonZeros() const {
return m_data.lowerSize();
}
inline Index upperNonZeros(Index j) const {
return m_colStartIndex[j + 1] - m_colStartIndex[j];
}
inline Index lowerNonZeros(Index j) const {
return m_rowStartIndex[j + 1] - m_rowStartIndex[j];
}
inline const Scalar* _diagPtr() const {
return &m_data.diag(0);
}
inline Scalar* _diagPtr() {
return &m_data.diag(0);
}
inline const Scalar* _upperPtr() const {
return &m_data.upper(0);
}
inline Scalar* _upperPtr() {
return &m_data.upper(0);
}
inline const Scalar* _lowerPtr() const {
return &m_data.lower(0);
}
inline Scalar* _lowerPtr() {
return &m_data.lower(0);
}
inline const Index* _upperProfilePtr() const {
return &m_data.upperProfile(0);
}
inline Index* _upperProfilePtr() {
return &m_data.upperProfile(0);
}
inline const Index* _lowerProfilePtr() const {
return &m_data.lowerProfile(0);
}
inline Index* _lowerProfilePtr() {
return &m_data.lowerProfile(0);
}
inline Scalar coeff(Index row, Index col) const {
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
eigen_assert(outer < outerSize());
eigen_assert(inner < innerSize());
if (outer == inner)
return this->m_data.diag(outer);
if (IsRowMajor) {
if (inner > outer) //upper matrix
{
const Index minOuterIndex = inner - m_data.upperProfile(inner);
if (outer >= minOuterIndex)
return this->m_data.upper(m_colStartIndex[inner] + outer - (inner - m_data.upperProfile(inner)));
else
return Scalar(0);
}
if (inner < outer) //lower matrix
{
const Index minInnerIndex = outer - m_data.lowerProfile(outer);
if (inner >= minInnerIndex)
return this->m_data.lower(m_rowStartIndex[outer] + inner - (outer - m_data.lowerProfile(outer)));
else
return Scalar(0);
}
return m_data.upper(m_colStartIndex[inner] + outer - inner);
} else {
if (outer > inner) //upper matrix
{
const Index maxOuterIndex = inner + m_data.upperProfile(inner);
if (outer <= maxOuterIndex)
return this->m_data.upper(m_colStartIndex[inner] + (outer - inner));
else
return Scalar(0);
}
if (outer < inner) //lower matrix
{
const Index maxInnerIndex = outer + m_data.lowerProfile(outer);
if (inner <= maxInnerIndex)
return this->m_data.lower(m_rowStartIndex[outer] + (inner - outer));
else
return Scalar(0);
}
}
}
inline Scalar& coeffRef(Index row, Index col) {
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
eigen_assert(outer < outerSize());
eigen_assert(inner < innerSize());
if (outer == inner)
return this->m_data.diag(outer);
if (IsRowMajor) {
if (col > row) //upper matrix
{
const Index minOuterIndex = inner - m_data.upperProfile(inner);
eigen_assert(outer >= minOuterIndex && "you try to acces a coeff that do not exist in the storage");
return this->m_data.upper(m_colStartIndex[inner] + outer - (inner - m_data.upperProfile(inner)));
}
if (col < row) //lower matrix
{
const Index minInnerIndex = outer - m_data.lowerProfile(outer);
eigen_assert(inner >= minInnerIndex && "you try to acces a coeff that do not exist in the storage");
return this->m_data.lower(m_rowStartIndex[outer] + inner - (outer - m_data.lowerProfile(outer)));
}
} else {
if (outer > inner) //upper matrix
{
const Index maxOuterIndex = inner + m_data.upperProfile(inner);
eigen_assert(outer <= maxOuterIndex && "you try to acces a coeff that do not exist in the storage");
return this->m_data.upper(m_colStartIndex[inner] + (outer - inner));
}
if (outer < inner) //lower matrix
{
const Index maxInnerIndex = outer + m_data.lowerProfile(outer);
eigen_assert(inner <= maxInnerIndex && "you try to acces a coeff that do not exist in the storage");
return this->m_data.lower(m_rowStartIndex[outer] + (inner - outer));
}
}
}
inline Scalar coeffDiag(Index idx) const {
eigen_assert(idx < outerSize());
eigen_assert(idx < innerSize());
return this->m_data.diag(idx);
}
inline Scalar coeffLower(Index row, Index col) const {
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
eigen_assert(outer < outerSize());
eigen_assert(inner < innerSize());
eigen_assert(inner != outer);
if (IsRowMajor) {
const Index minInnerIndex = outer - m_data.lowerProfile(outer);
if (inner >= minInnerIndex)
return this->m_data.lower(m_rowStartIndex[outer] + inner - (outer - m_data.lowerProfile(outer)));
else
return Scalar(0);
} else {
const Index maxInnerIndex = outer + m_data.lowerProfile(outer);
if (inner <= maxInnerIndex)
return this->m_data.lower(m_rowStartIndex[outer] + (inner - outer));
else
return Scalar(0);
}
}
inline Scalar coeffUpper(Index row, Index col) const {
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
eigen_assert(outer < outerSize());
eigen_assert(inner < innerSize());
eigen_assert(inner != outer);
if (IsRowMajor) {
const Index minOuterIndex = inner - m_data.upperProfile(inner);
if (outer >= minOuterIndex)
return this->m_data.upper(m_colStartIndex[inner] + outer - (inner - m_data.upperProfile(inner)));
else
return Scalar(0);
} else {
const Index maxOuterIndex = inner + m_data.upperProfile(inner);
if (outer <= maxOuterIndex)
return this->m_data.upper(m_colStartIndex[inner] + (outer - inner));
else
return Scalar(0);
}
}
inline Scalar& coeffRefDiag(Index idx) {
eigen_assert(idx < outerSize());
eigen_assert(idx < innerSize());
return this->m_data.diag(idx);
}
inline Scalar& coeffRefLower(Index row, Index col) {
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
eigen_assert(outer < outerSize());
eigen_assert(inner < innerSize());
eigen_assert(inner != outer);
if (IsRowMajor) {
const Index minInnerIndex = outer - m_data.lowerProfile(outer);
eigen_assert(inner >= minInnerIndex && "you try to acces a coeff that do not exist in the storage");
return this->m_data.lower(m_rowStartIndex[outer] + inner - (outer - m_data.lowerProfile(outer)));
} else {
const Index maxInnerIndex = outer + m_data.lowerProfile(outer);
eigen_assert(inner <= maxInnerIndex && "you try to acces a coeff that do not exist in the storage");
return this->m_data.lower(m_rowStartIndex[outer] + (inner - outer));
}
}
inline bool coeffExistLower(Index row, Index col) {
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
eigen_assert(outer < outerSize());
eigen_assert(inner < innerSize());
eigen_assert(inner != outer);
if (IsRowMajor) {
const Index minInnerIndex = outer - m_data.lowerProfile(outer);
return inner >= minInnerIndex;
} else {
const Index maxInnerIndex = outer + m_data.lowerProfile(outer);
return inner <= maxInnerIndex;
}
}
inline Scalar& coeffRefUpper(Index row, Index col) {
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
eigen_assert(outer < outerSize());
eigen_assert(inner < innerSize());
eigen_assert(inner != outer);
if (IsRowMajor) {
const Index minOuterIndex = inner - m_data.upperProfile(inner);
eigen_assert(outer >= minOuterIndex && "you try to acces a coeff that do not exist in the storage");
return this->m_data.upper(m_colStartIndex[inner] + outer - (inner - m_data.upperProfile(inner)));
} else {
const Index maxOuterIndex = inner + m_data.upperProfile(inner);
eigen_assert(outer <= maxOuterIndex && "you try to acces a coeff that do not exist in the storage");
return this->m_data.upper(m_colStartIndex[inner] + (outer - inner));
}
}
inline bool coeffExistUpper(Index row, Index col) {
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
eigen_assert(outer < outerSize());
eigen_assert(inner < innerSize());
eigen_assert(inner != outer);
if (IsRowMajor) {
const Index minOuterIndex = inner - m_data.upperProfile(inner);
return outer >= minOuterIndex;
} else {
const Index maxOuterIndex = inner + m_data.upperProfile(inner);
return outer <= maxOuterIndex;
}
}
protected:
public:
class InnerUpperIterator;
class InnerLowerIterator;
class OuterUpperIterator;
class OuterLowerIterator;
/** Removes all non zeros */
inline void setZero() {
m_data.clear();
memset(m_colStartIndex, 0, (m_outerSize + 1) * sizeof (Index));
memset(m_rowStartIndex, 0, (m_outerSize + 1) * sizeof (Index));
}
/** \returns the number of non zero coefficients */
inline Index nonZeros() const {
return m_data.diagSize() + m_data.upperSize() + m_data.lowerSize();
}
/** Preallocates \a reserveSize non zeros */
inline void reserve(Index reserveSize, Index reserveUpperSize, Index reserveLowerSize) {
m_data.reserve(reserveSize, reserveUpperSize, reserveLowerSize);
}
/** \returns a reference to a novel non zero coefficient with coordinates \a row x \a col.
*
* \warning This function can be extremely slow if the non zero coefficients
* are not inserted in a coherent order.
*
* After an insertion session, you should call the finalize() function.
*/
EIGEN_DONT_INLINE Scalar & insert(Index row, Index col) {
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
eigen_assert(outer < outerSize());
eigen_assert(inner < innerSize());
if (outer == inner)
return m_data.diag(col);
if (IsRowMajor) {
if (outer < inner) //upper matrix
{
Index minOuterIndex = 0;
minOuterIndex = inner - m_data.upperProfile(inner);
if (outer < minOuterIndex) //The value does not yet exist
{
const Index previousProfile = m_data.upperProfile(inner);
m_data.upperProfile(inner) = inner - outer;
const Index bandIncrement = m_data.upperProfile(inner) - previousProfile;
//shift data stored after this new one
const Index stop = m_colStartIndex[cols()];
const Index start = m_colStartIndex[inner];
for (Index innerIdx = stop; innerIdx >= start; innerIdx--) {
m_data.upper(innerIdx + bandIncrement) = m_data.upper(innerIdx);
}
for (Index innerIdx = cols(); innerIdx > inner; innerIdx--) {
m_colStartIndex[innerIdx] += bandIncrement;
}
//zeros new data
memset(this->_upperPtr() + start, 0, (bandIncrement - 1) * sizeof (Scalar));
return m_data.upper(m_colStartIndex[inner]);
} else {
return m_data.upper(m_colStartIndex[inner] + outer - (inner - m_data.upperProfile(inner)));
}
}
if (outer > inner) //lower matrix
{
const Index minInnerIndex = outer - m_data.lowerProfile(outer);
if (inner < minInnerIndex) //The value does not yet exist
{
const Index previousProfile = m_data.lowerProfile(outer);
m_data.lowerProfile(outer) = outer - inner;
const Index bandIncrement = m_data.lowerProfile(outer) - previousProfile;
//shift data stored after this new one
const Index stop = m_rowStartIndex[rows()];
const Index start = m_rowStartIndex[outer];
for (Index innerIdx = stop; innerIdx >= start; innerIdx--) {
m_data.lower(innerIdx + bandIncrement) = m_data.lower(innerIdx);
}
for (Index innerIdx = rows(); innerIdx > outer; innerIdx--) {
m_rowStartIndex[innerIdx] += bandIncrement;
}
//zeros new data
memset(this->_lowerPtr() + start, 0, (bandIncrement - 1) * sizeof (Scalar));
return m_data.lower(m_rowStartIndex[outer]);
} else {
return m_data.lower(m_rowStartIndex[outer] + inner - (outer - m_data.lowerProfile(outer)));
}
}
} else {
if (outer > inner) //upper matrix
{
const Index maxOuterIndex = inner + m_data.upperProfile(inner);
if (outer > maxOuterIndex) //The value does not yet exist
{
const Index previousProfile = m_data.upperProfile(inner);
m_data.upperProfile(inner) = outer - inner;
const Index bandIncrement = m_data.upperProfile(inner) - previousProfile;
//shift data stored after this new one
const Index stop = m_rowStartIndex[rows()];
const Index start = m_rowStartIndex[inner + 1];
for (Index innerIdx = stop; innerIdx >= start; innerIdx--) {
m_data.upper(innerIdx + bandIncrement) = m_data.upper(innerIdx);
}
for (Index innerIdx = inner + 1; innerIdx < outerSize() + 1; innerIdx++) {
m_rowStartIndex[innerIdx] += bandIncrement;
}
memset(this->_upperPtr() + m_rowStartIndex[inner] + previousProfile + 1, 0, (bandIncrement - 1) * sizeof (Scalar));
return m_data.upper(m_rowStartIndex[inner] + m_data.upperProfile(inner));
} else {
return m_data.upper(m_rowStartIndex[inner] + (outer - inner));
}
}
if (outer < inner) //lower matrix
{
const Index maxInnerIndex = outer + m_data.lowerProfile(outer);
if (inner > maxInnerIndex) //The value does not yet exist
{
const Index previousProfile = m_data.lowerProfile(outer);
m_data.lowerProfile(outer) = inner - outer;
const Index bandIncrement = m_data.lowerProfile(outer) - previousProfile;
//shift data stored after this new one
const Index stop = m_colStartIndex[cols()];
const Index start = m_colStartIndex[outer + 1];
for (Index innerIdx = stop; innerIdx >= start; innerIdx--) {
m_data.lower(innerIdx + bandIncrement) = m_data.lower(innerIdx);
}
for (Index innerIdx = outer + 1; innerIdx < outerSize() + 1; innerIdx++) {
m_colStartIndex[innerIdx] += bandIncrement;
}
memset(this->_lowerPtr() + m_colStartIndex[outer] + previousProfile + 1, 0, (bandIncrement - 1) * sizeof (Scalar));
return m_data.lower(m_colStartIndex[outer] + m_data.lowerProfile(outer));
} else {
return m_data.lower(m_colStartIndex[outer] + (inner - outer));
}
}
}
}
/** Must be called after inserting a set of non zero entries.
*/
inline void finalize() {
if (IsRowMajor) {
if (rows() > cols())
m_data.resize(cols(), cols(), rows(), m_colStartIndex[cols()] + 1, m_rowStartIndex[rows()] + 1);
else
m_data.resize(rows(), cols(), rows(), m_colStartIndex[cols()] + 1, m_rowStartIndex[rows()] + 1);
// eigen_assert(rows() == cols() && "memory reorganisatrion only works with suare matrix");
//
// Scalar* newArray = new Scalar[m_colStartIndex[cols()] + 1 + m_rowStartIndex[rows()] + 1];
// Index dataIdx = 0;
// for (Index row = 0; row < rows(); row++) {
//
// const Index nbLowerElts = m_rowStartIndex[row + 1] - m_rowStartIndex[row];
// // std::cout << "nbLowerElts" << nbLowerElts << std::endl;
// memcpy(newArray + dataIdx, m_data.m_lower + m_rowStartIndex[row], nbLowerElts * sizeof (Scalar));
// m_rowStartIndex[row] = dataIdx;
// dataIdx += nbLowerElts;
//
// const Index nbUpperElts = m_colStartIndex[row + 1] - m_colStartIndex[row];
// memcpy(newArray + dataIdx, m_data.m_upper + m_colStartIndex[row], nbUpperElts * sizeof (Scalar));
// m_colStartIndex[row] = dataIdx;
// dataIdx += nbUpperElts;
//
//
// }
// //todo : don't access m_data profile directly : add an accessor from SkylineMatrix
// m_rowStartIndex[rows()] = m_rowStartIndex[rows()-1] + m_data.lowerProfile(rows()-1);
// m_colStartIndex[cols()] = m_colStartIndex[cols()-1] + m_data.upperProfile(cols()-1);
//
// delete[] m_data.m_lower;
// delete[] m_data.m_upper;
//
// m_data.m_lower = newArray;
// m_data.m_upper = newArray;
} else {
if (rows() > cols())
m_data.resize(cols(), rows(), cols(), m_rowStartIndex[cols()] + 1, m_colStartIndex[cols()] + 1);
else
m_data.resize(rows(), rows(), cols(), m_rowStartIndex[rows()] + 1, m_colStartIndex[rows()] + 1);
}
}
inline void squeeze() {
finalize();
m_data.squeeze();
}
void prune(Scalar reference, RealScalar epsilon = dummy_precision<RealScalar > ()) {
//TODO
}
/** Resizes the matrix to a \a rows x \a cols matrix and initializes it to zero
* \sa resizeNonZeros(Index), reserve(), setZero()
*/
void resize(size_t rows, size_t cols) {
const Index diagSize = rows > cols ? cols : rows;
m_innerSize = IsRowMajor ? cols : rows;
eigen_assert(rows == cols && "Skyline matrix must be square matrix");
if (diagSize % 2) { // diagSize is odd
const Index k = (diagSize - 1) / 2;
m_data.resize(diagSize, IsRowMajor ? cols : rows, IsRowMajor ? rows : cols,
2 * k * k + k + 1,
2 * k * k + k + 1);
} else // diagSize is even
{
const Index k = diagSize / 2;
m_data.resize(diagSize, IsRowMajor ? cols : rows, IsRowMajor ? rows : cols,
2 * k * k - k + 1,
2 * k * k - k + 1);
}
if (m_colStartIndex && m_rowStartIndex) {
delete[] m_colStartIndex;
delete[] m_rowStartIndex;
}
m_colStartIndex = new Index [cols + 1];
m_rowStartIndex = new Index [rows + 1];
m_outerSize = diagSize;
m_data.reset();
m_data.clear();
m_outerSize = diagSize;
memset(m_colStartIndex, 0, (cols + 1) * sizeof (Index));
memset(m_rowStartIndex, 0, (rows + 1) * sizeof (Index));
}
void resizeNonZeros(Index size) {
m_data.resize(size);
}
inline SkylineMatrix()
: m_outerSize(-1), m_innerSize(0), m_colStartIndex(0), m_rowStartIndex(0) {
resize(0, 0);
}
inline SkylineMatrix(size_t rows, size_t cols)
: m_outerSize(0), m_innerSize(0), m_colStartIndex(0), m_rowStartIndex(0) {
resize(rows, cols);
}
template<typename OtherDerived>
inline SkylineMatrix(const SkylineMatrixBase<OtherDerived>& other)
: m_outerSize(0), m_innerSize(0), m_colStartIndex(0), m_rowStartIndex(0) {
*this = other.derived();
}
inline SkylineMatrix(const SkylineMatrix & other)
: Base(), m_outerSize(0), m_innerSize(0), m_colStartIndex(0), m_rowStartIndex(0) {
*this = other.derived();
}
inline void swap(SkylineMatrix & other) {
//EIGEN_DBG_SKYLINE(std::cout << "SkylineMatrix:: swap\n");
std::swap(m_colStartIndex, other.m_colStartIndex);
std::swap(m_rowStartIndex, other.m_rowStartIndex);
std::swap(m_innerSize, other.m_innerSize);
std::swap(m_outerSize, other.m_outerSize);
m_data.swap(other.m_data);
}
inline SkylineMatrix & operator=(const SkylineMatrix & other) {
std::cout << "SkylineMatrix& operator=(const SkylineMatrix& other)\n";
if (other.isRValue()) {
swap(other.const_cast_derived());
} else {
resize(other.rows(), other.cols());
memcpy(m_colStartIndex, other.m_colStartIndex, (m_outerSize + 1) * sizeof (Index));
memcpy(m_rowStartIndex, other.m_rowStartIndex, (m_outerSize + 1) * sizeof (Index));
m_data = other.m_data;
}
return *this;
}
template<typename OtherDerived>
inline SkylineMatrix & operator=(const SkylineMatrixBase<OtherDerived>& other) {
const bool needToTranspose = (Flags & RowMajorBit) != (OtherDerived::Flags & RowMajorBit);
if (needToTranspose) {
// TODO
// return *this;
} else {
// there is no special optimization
return SkylineMatrixBase<SkylineMatrix>::operator=(other.derived());
}
}
friend std::ostream & operator <<(std::ostream & s, const SkylineMatrix & m) {
EIGEN_DBG_SKYLINE(
std::cout << "upper elements : " << std::endl;
for (Index i = 0; i < m.m_data.upperSize(); i++)
std::cout << m.m_data.upper(i) << "\t";
std::cout << std::endl;
std::cout << "upper profile : " << std::endl;
for (Index i = 0; i < m.m_data.upperProfileSize(); i++)
std::cout << m.m_data.upperProfile(i) << "\t";
std::cout << std::endl;
std::cout << "lower startIdx : " << std::endl;
for (Index i = 0; i < m.m_data.upperProfileSize(); i++)
std::cout << (IsRowMajor ? m.m_colStartIndex[i] : m.m_rowStartIndex[i]) << "\t";
std::cout << std::endl;
std::cout << "lower elements : " << std::endl;
for (Index i = 0; i < m.m_data.lowerSize(); i++)
std::cout << m.m_data.lower(i) << "\t";
std::cout << std::endl;
std::cout << "lower profile : " << std::endl;
for (Index i = 0; i < m.m_data.lowerProfileSize(); i++)
std::cout << m.m_data.lowerProfile(i) << "\t";
std::cout << std::endl;
std::cout << "lower startIdx : " << std::endl;
for (Index i = 0; i < m.m_data.lowerProfileSize(); i++)
std::cout << (IsRowMajor ? m.m_rowStartIndex[i] : m.m_colStartIndex[i]) << "\t";
std::cout << std::endl;
);
for (Index rowIdx = 0; rowIdx < m.rows(); rowIdx++) {
for (Index colIdx = 0; colIdx < m.cols(); colIdx++) {
s << m.coeff(rowIdx, colIdx) << "\t";
}
s << std::endl;
}
return s;
}
/** Destructor */
inline ~SkylineMatrix() {
delete[] m_colStartIndex;
delete[] m_rowStartIndex;
}
/** Overloaded for performance */
Scalar sum() const;
};
template<typename Scalar, int _Options>
class SkylineMatrix<Scalar, _Options>::InnerUpperIterator {
public:
InnerUpperIterator(const SkylineMatrix& mat, Index outer)
: m_matrix(mat), m_outer(outer),
m_id(_Options == RowMajor ? mat.m_colStartIndex[outer] : mat.m_rowStartIndex[outer] + 1),
m_start(m_id),
m_end(_Options == RowMajor ? mat.m_colStartIndex[outer + 1] : mat.m_rowStartIndex[outer + 1] + 1) {
}
inline InnerUpperIterator & operator++() {
m_id++;
return *this;
}
inline InnerUpperIterator & operator+=(Index shift) {
m_id += shift;
return *this;
}
inline Scalar value() const {
return m_matrix.m_data.upper(m_id);
}
inline Scalar* valuePtr() {
return const_cast<Scalar*> (&(m_matrix.m_data.upper(m_id)));
}
inline Scalar& valueRef() {
return const_cast<Scalar&> (m_matrix.m_data.upper(m_id));
}
inline Index index() const {
return IsRowMajor ? m_outer - m_matrix.m_data.upperProfile(m_outer) + (m_id - m_start) :
m_outer + (m_id - m_start) + 1;
}
inline Index row() const {
return IsRowMajor ? index() : m_outer;
}
inline Index col() const {
return IsRowMajor ? m_outer : index();
}
inline size_t size() const {
return m_matrix.m_data.upperProfile(m_outer);
}
inline operator bool() const {
return (m_id < m_end) && (m_id >= m_start);
}
protected:
const SkylineMatrix& m_matrix;
const Index m_outer;
Index m_id;
const Index m_start;
const Index m_end;
};
template<typename Scalar, int _Options>
class SkylineMatrix<Scalar, _Options>::InnerLowerIterator {
public:
InnerLowerIterator(const SkylineMatrix& mat, Index outer)
: m_matrix(mat),
m_outer(outer),
m_id(_Options == RowMajor ? mat.m_rowStartIndex[outer] : mat.m_colStartIndex[outer] + 1),
m_start(m_id),
m_end(_Options == RowMajor ? mat.m_rowStartIndex[outer + 1] : mat.m_colStartIndex[outer + 1] + 1) {
}
inline InnerLowerIterator & operator++() {
m_id++;
return *this;
}
inline InnerLowerIterator & operator+=(Index shift) {
m_id += shift;
return *this;
}
inline Scalar value() const {
return m_matrix.m_data.lower(m_id);
}
inline Scalar* valuePtr() {
return const_cast<Scalar*> (&(m_matrix.m_data.lower(m_id)));
}
inline Scalar& valueRef() {
return const_cast<Scalar&> (m_matrix.m_data.lower(m_id));
}
inline Index index() const {
return IsRowMajor ? m_outer - m_matrix.m_data.lowerProfile(m_outer) + (m_id - m_start) :
m_outer + (m_id - m_start) + 1;
;
}
inline Index row() const {
return IsRowMajor ? m_outer : index();
}
inline Index col() const {
return IsRowMajor ? index() : m_outer;
}
inline size_t size() const {
return m_matrix.m_data.lowerProfile(m_outer);
}
inline operator bool() const {
return (m_id < m_end) && (m_id >= m_start);
}
protected:
const SkylineMatrix& m_matrix;
const Index m_outer;
Index m_id;
const Index m_start;
const Index m_end;
};
} // end namespace Eigen
#endif // EIGEN_SkylineMatrix_H

View File

@@ -0,0 +1,212 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Guillaume Saupin <guillaume.saupin@cea.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SKYLINEMATRIXBASE_H
#define EIGEN_SKYLINEMATRIXBASE_H
#include "SkylineUtil.h"
namespace Eigen {
/** \ingroup Skyline_Module
*
* \class SkylineMatrixBase
*
* \brief Base class of any skyline matrices or skyline expressions
*
* \param Derived
*
*/
template<typename Derived> class SkylineMatrixBase : public EigenBase<Derived> {
public:
typedef typename internal::traits<Derived>::Scalar Scalar;
typedef typename internal::traits<Derived>::StorageKind StorageKind;
typedef typename internal::index<StorageKind>::type Index;
enum {
RowsAtCompileTime = internal::traits<Derived>::RowsAtCompileTime,
/**< The number of rows at compile-time. This is just a copy of the value provided
* by the \a Derived type. If a value is not known at compile-time,
* it is set to the \a Dynamic constant.
* \sa MatrixBase::rows(), MatrixBase::cols(), ColsAtCompileTime, SizeAtCompileTime */
ColsAtCompileTime = internal::traits<Derived>::ColsAtCompileTime,
/**< The number of columns at compile-time. This is just a copy of the value provided
* by the \a Derived type. If a value is not known at compile-time,
* it is set to the \a Dynamic constant.
* \sa MatrixBase::rows(), MatrixBase::cols(), RowsAtCompileTime, SizeAtCompileTime */
SizeAtCompileTime = (internal::size_at_compile_time<internal::traits<Derived>::RowsAtCompileTime,
internal::traits<Derived>::ColsAtCompileTime>::ret),
/**< This is equal to the number of coefficients, i.e. the number of
* rows times the number of columns, or to \a Dynamic if this is not
* known at compile-time. \sa RowsAtCompileTime, ColsAtCompileTime */
MaxRowsAtCompileTime = RowsAtCompileTime,
MaxColsAtCompileTime = ColsAtCompileTime,
MaxSizeAtCompileTime = (internal::size_at_compile_time<MaxRowsAtCompileTime,
MaxColsAtCompileTime>::ret),
IsVectorAtCompileTime = RowsAtCompileTime == 1 || ColsAtCompileTime == 1,
/**< This is set to true if either the number of rows or the number of
* columns is known at compile-time to be equal to 1. Indeed, in that case,
* we are dealing with a column-vector (if there is only one column) or with
* a row-vector (if there is only one row). */
Flags = internal::traits<Derived>::Flags,
/**< This stores expression \ref flags flags which may or may not be inherited by new expressions
* constructed from this one. See the \ref flags "list of flags".
*/
CoeffReadCost = internal::traits<Derived>::CoeffReadCost,
/**< This is a rough measure of how expensive it is to read one coefficient from
* this expression.
*/
IsRowMajor = Flags & RowMajorBit ? 1 : 0
};
#ifndef EIGEN_PARSED_BY_DOXYGEN
/** This is the "real scalar" type; if the \a Scalar type is already real numbers
* (e.g. int, float or double) then \a RealScalar is just the same as \a Scalar. If
* \a Scalar is \a std::complex<T> then RealScalar is \a T.
*
* \sa class NumTraits
*/
typedef typename NumTraits<Scalar>::Real RealScalar;
/** type of the equivalent square matrix */
typedef Matrix<Scalar, EIGEN_SIZE_MAX(RowsAtCompileTime, ColsAtCompileTime),
EIGEN_SIZE_MAX(RowsAtCompileTime, ColsAtCompileTime) > SquareMatrixType;
inline const Derived& derived() const {
return *static_cast<const Derived*> (this);
}
inline Derived& derived() {
return *static_cast<Derived*> (this);
}
inline Derived& const_cast_derived() const {
return *static_cast<Derived*> (const_cast<SkylineMatrixBase*> (this));
}
#endif // not EIGEN_PARSED_BY_DOXYGEN
/** \returns the number of rows. \sa cols(), RowsAtCompileTime */
inline Index rows() const {
return derived().rows();
}
/** \returns the number of columns. \sa rows(), ColsAtCompileTime*/
inline Index cols() const {
return derived().cols();
}
/** \returns the number of coefficients, which is \a rows()*cols().
* \sa rows(), cols(), SizeAtCompileTime. */
inline Index size() const {
return rows() * cols();
}
/** \returns the number of nonzero coefficients which is in practice the number
* of stored coefficients. */
inline Index nonZeros() const {
return derived().nonZeros();
}
/** \returns the size of the storage major dimension,
* i.e., the number of columns for a columns major matrix, and the number of rows otherwise */
Index outerSize() const {
return (int(Flags) & RowMajorBit) ? this->rows() : this->cols();
}
/** \returns the size of the inner dimension according to the storage order,
* i.e., the number of rows for a columns major matrix, and the number of cols otherwise */
Index innerSize() const {
return (int(Flags) & RowMajorBit) ? this->cols() : this->rows();
}
bool isRValue() const {
return m_isRValue;
}
Derived& markAsRValue() {
m_isRValue = true;
return derived();
}
SkylineMatrixBase() : m_isRValue(false) {
/* TODO check flags */
}
inline Derived & operator=(const Derived& other) {
this->operator=<Derived > (other);
return derived();
}
template<typename OtherDerived>
inline void assignGeneric(const OtherDerived& other) {
derived().resize(other.rows(), other.cols());
for (Index row = 0; row < rows(); row++)
for (Index col = 0; col < cols(); col++) {
if (other.coeff(row, col) != Scalar(0))
derived().insert(row, col) = other.coeff(row, col);
}
derived().finalize();
}
template<typename OtherDerived>
inline Derived & operator=(const SkylineMatrixBase<OtherDerived>& other) {
//TODO
}
template<typename Lhs, typename Rhs>
inline Derived & operator=(const SkylineProduct<Lhs, Rhs, SkylineTimeSkylineProduct>& product);
friend std::ostream & operator <<(std::ostream & s, const SkylineMatrixBase& m) {
s << m.derived();
return s;
}
template<typename OtherDerived>
const typename SkylineProductReturnType<Derived, OtherDerived>::Type
operator*(const MatrixBase<OtherDerived> &other) const;
/** \internal use operator= */
template<typename DenseDerived>
void evalTo(MatrixBase<DenseDerived>& dst) const {
dst.setZero();
for (Index i = 0; i < rows(); i++)
for (Index j = 0; j < rows(); j++)
dst(i, j) = derived().coeff(i, j);
}
Matrix<Scalar, RowsAtCompileTime, ColsAtCompileTime> toDense() const {
return derived();
}
/** \returns the matrix or vector obtained by evaluating this expression.
*
* Notice that in the case of a plain matrix or vector (not an expression) this function just returns
* a const reference, in order to avoid a useless copy.
*/
EIGEN_STRONG_INLINE const typename internal::eval<Derived, IsSkyline>::type eval() const {
return typename internal::eval<Derived>::type(derived());
}
protected:
bool m_isRValue;
};
} // end namespace Eigen
#endif // EIGEN_SkylineMatrixBase_H

View File

@@ -0,0 +1,295 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Guillaume Saupin <guillaume.saupin@cea.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SKYLINEPRODUCT_H
#define EIGEN_SKYLINEPRODUCT_H
namespace Eigen {
template<typename Lhs, typename Rhs, int ProductMode>
struct SkylineProductReturnType {
typedef const typename internal::nested<Lhs, Rhs::RowsAtCompileTime>::type LhsNested;
typedef const typename internal::nested<Rhs, Lhs::RowsAtCompileTime>::type RhsNested;
typedef SkylineProduct<LhsNested, RhsNested, ProductMode> Type;
};
template<typename LhsNested, typename RhsNested, int ProductMode>
struct internal::traits<SkylineProduct<LhsNested, RhsNested, ProductMode> > {
// clean the nested types:
typedef typename internal::remove_all<LhsNested>::type _LhsNested;
typedef typename internal::remove_all<RhsNested>::type _RhsNested;
typedef typename _LhsNested::Scalar Scalar;
enum {
LhsCoeffReadCost = _LhsNested::CoeffReadCost,
RhsCoeffReadCost = _RhsNested::CoeffReadCost,
LhsFlags = _LhsNested::Flags,
RhsFlags = _RhsNested::Flags,
RowsAtCompileTime = _LhsNested::RowsAtCompileTime,
ColsAtCompileTime = _RhsNested::ColsAtCompileTime,
InnerSize = EIGEN_SIZE_MIN_PREFER_FIXED(_LhsNested::ColsAtCompileTime, _RhsNested::RowsAtCompileTime),
MaxRowsAtCompileTime = _LhsNested::MaxRowsAtCompileTime,
MaxColsAtCompileTime = _RhsNested::MaxColsAtCompileTime,
EvalToRowMajor = (RhsFlags & LhsFlags & RowMajorBit),
ResultIsSkyline = ProductMode == SkylineTimeSkylineProduct,
RemovedBits = ~((EvalToRowMajor ? 0 : RowMajorBit) | (ResultIsSkyline ? 0 : SkylineBit)),
Flags = (int(LhsFlags | RhsFlags) & HereditaryBits & RemovedBits)
| EvalBeforeAssigningBit
| EvalBeforeNestingBit,
CoeffReadCost = Dynamic
};
typedef typename internal::conditional<ResultIsSkyline,
SkylineMatrixBase<SkylineProduct<LhsNested, RhsNested, ProductMode> >,
MatrixBase<SkylineProduct<LhsNested, RhsNested, ProductMode> > >::type Base;
};
namespace internal {
template<typename LhsNested, typename RhsNested, int ProductMode>
class SkylineProduct : no_assignment_operator,
public traits<SkylineProduct<LhsNested, RhsNested, ProductMode> >::Base {
public:
EIGEN_GENERIC_PUBLIC_INTERFACE(SkylineProduct)
private:
typedef typename traits<SkylineProduct>::_LhsNested _LhsNested;
typedef typename traits<SkylineProduct>::_RhsNested _RhsNested;
public:
template<typename Lhs, typename Rhs>
EIGEN_STRONG_INLINE SkylineProduct(const Lhs& lhs, const Rhs& rhs)
: m_lhs(lhs), m_rhs(rhs) {
eigen_assert(lhs.cols() == rhs.rows());
enum {
ProductIsValid = _LhsNested::ColsAtCompileTime == Dynamic
|| _RhsNested::RowsAtCompileTime == Dynamic
|| int(_LhsNested::ColsAtCompileTime) == int(_RhsNested::RowsAtCompileTime),
AreVectors = _LhsNested::IsVectorAtCompileTime && _RhsNested::IsVectorAtCompileTime,
SameSizes = EIGEN_PREDICATE_SAME_MATRIX_SIZE(_LhsNested, _RhsNested)
};
// note to the lost user:
// * for a dot product use: v1.dot(v2)
// * for a coeff-wise product use: v1.cwise()*v2
EIGEN_STATIC_ASSERT(ProductIsValid || !(AreVectors && SameSizes),
INVALID_VECTOR_VECTOR_PRODUCT__IF_YOU_WANTED_A_DOT_OR_COEFF_WISE_PRODUCT_YOU_MUST_USE_THE_EXPLICIT_FUNCTIONS)
EIGEN_STATIC_ASSERT(ProductIsValid || !(SameSizes && !AreVectors),
INVALID_MATRIX_PRODUCT__IF_YOU_WANTED_A_COEFF_WISE_PRODUCT_YOU_MUST_USE_THE_EXPLICIT_FUNCTION)
EIGEN_STATIC_ASSERT(ProductIsValid || SameSizes, INVALID_MATRIX_PRODUCT)
}
EIGEN_STRONG_INLINE Index rows() const {
return m_lhs.rows();
}
EIGEN_STRONG_INLINE Index cols() const {
return m_rhs.cols();
}
EIGEN_STRONG_INLINE const _LhsNested& lhs() const {
return m_lhs;
}
EIGEN_STRONG_INLINE const _RhsNested& rhs() const {
return m_rhs;
}
protected:
LhsNested m_lhs;
RhsNested m_rhs;
};
// dense = skyline * dense
// Note that here we force no inlining and separate the setZero() because GCC messes up otherwise
template<typename Lhs, typename Rhs, typename Dest>
EIGEN_DONT_INLINE void skyline_row_major_time_dense_product(const Lhs& lhs, const Rhs& rhs, Dest& dst) {
typedef typename remove_all<Lhs>::type _Lhs;
typedef typename remove_all<Rhs>::type _Rhs;
typedef typename traits<Lhs>::Scalar Scalar;
enum {
LhsIsRowMajor = (_Lhs::Flags & RowMajorBit) == RowMajorBit,
LhsIsSelfAdjoint = (_Lhs::Flags & SelfAdjointBit) == SelfAdjointBit,
ProcessFirstHalf = LhsIsSelfAdjoint
&& (((_Lhs::Flags & (UpperTriangularBit | LowerTriangularBit)) == 0)
|| ((_Lhs::Flags & UpperTriangularBit) && !LhsIsRowMajor)
|| ((_Lhs::Flags & LowerTriangularBit) && LhsIsRowMajor)),
ProcessSecondHalf = LhsIsSelfAdjoint && (!ProcessFirstHalf)
};
//Use matrix diagonal part <- Improvement : use inner iterator on dense matrix.
for (Index col = 0; col < rhs.cols(); col++) {
for (Index row = 0; row < lhs.rows(); row++) {
dst(row, col) = lhs.coeffDiag(row) * rhs(row, col);
}
}
//Use matrix lower triangular part
for (Index row = 0; row < lhs.rows(); row++) {
typename _Lhs::InnerLowerIterator lIt(lhs, row);
const Index stop = lIt.col() + lIt.size();
for (Index col = 0; col < rhs.cols(); col++) {
Index k = lIt.col();
Scalar tmp = 0;
while (k < stop) {
tmp +=
lIt.value() *
rhs(k++, col);
++lIt;
}
dst(row, col) += tmp;
lIt += -lIt.size();
}
}
//Use matrix upper triangular part
for (Index lhscol = 0; lhscol < lhs.cols(); lhscol++) {
typename _Lhs::InnerUpperIterator uIt(lhs, lhscol);
const Index stop = uIt.size() + uIt.row();
for (Index rhscol = 0; rhscol < rhs.cols(); rhscol++) {
const Scalar rhsCoeff = rhs.coeff(lhscol, rhscol);
Index k = uIt.row();
while (k < stop) {
dst(k++, rhscol) +=
uIt.value() *
rhsCoeff;
++uIt;
}
uIt += -uIt.size();
}
}
}
template<typename Lhs, typename Rhs, typename Dest>
EIGEN_DONT_INLINE void skyline_col_major_time_dense_product(const Lhs& lhs, const Rhs& rhs, Dest& dst) {
typedef typename remove_all<Lhs>::type _Lhs;
typedef typename remove_all<Rhs>::type _Rhs;
typedef typename traits<Lhs>::Scalar Scalar;
enum {
LhsIsRowMajor = (_Lhs::Flags & RowMajorBit) == RowMajorBit,
LhsIsSelfAdjoint = (_Lhs::Flags & SelfAdjointBit) == SelfAdjointBit,
ProcessFirstHalf = LhsIsSelfAdjoint
&& (((_Lhs::Flags & (UpperTriangularBit | LowerTriangularBit)) == 0)
|| ((_Lhs::Flags & UpperTriangularBit) && !LhsIsRowMajor)
|| ((_Lhs::Flags & LowerTriangularBit) && LhsIsRowMajor)),
ProcessSecondHalf = LhsIsSelfAdjoint && (!ProcessFirstHalf)
};
//Use matrix diagonal part <- Improvement : use inner iterator on dense matrix.
for (Index col = 0; col < rhs.cols(); col++) {
for (Index row = 0; row < lhs.rows(); row++) {
dst(row, col) = lhs.coeffDiag(row) * rhs(row, col);
}
}
//Use matrix upper triangular part
for (Index row = 0; row < lhs.rows(); row++) {
typename _Lhs::InnerUpperIterator uIt(lhs, row);
const Index stop = uIt.col() + uIt.size();
for (Index col = 0; col < rhs.cols(); col++) {
Index k = uIt.col();
Scalar tmp = 0;
while (k < stop) {
tmp +=
uIt.value() *
rhs(k++, col);
++uIt;
}
dst(row, col) += tmp;
uIt += -uIt.size();
}
}
//Use matrix lower triangular part
for (Index lhscol = 0; lhscol < lhs.cols(); lhscol++) {
typename _Lhs::InnerLowerIterator lIt(lhs, lhscol);
const Index stop = lIt.size() + lIt.row();
for (Index rhscol = 0; rhscol < rhs.cols(); rhscol++) {
const Scalar rhsCoeff = rhs.coeff(lhscol, rhscol);
Index k = lIt.row();
while (k < stop) {
dst(k++, rhscol) +=
lIt.value() *
rhsCoeff;
++lIt;
}
lIt += -lIt.size();
}
}
}
template<typename Lhs, typename Rhs, typename ResultType,
int LhsStorageOrder = traits<Lhs>::Flags&RowMajorBit>
struct skyline_product_selector;
template<typename Lhs, typename Rhs, typename ResultType>
struct skyline_product_selector<Lhs, Rhs, ResultType, RowMajor> {
typedef typename traits<typename remove_all<Lhs>::type>::Scalar Scalar;
static void run(const Lhs& lhs, const Rhs& rhs, ResultType & res) {
skyline_row_major_time_dense_product<Lhs, Rhs, ResultType > (lhs, rhs, res);
}
};
template<typename Lhs, typename Rhs, typename ResultType>
struct skyline_product_selector<Lhs, Rhs, ResultType, ColMajor> {
typedef typename traits<typename remove_all<Lhs>::type>::Scalar Scalar;
static void run(const Lhs& lhs, const Rhs& rhs, ResultType & res) {
skyline_col_major_time_dense_product<Lhs, Rhs, ResultType > (lhs, rhs, res);
}
};
} // end namespace internal
// template<typename Derived>
// template<typename Lhs, typename Rhs >
// Derived & MatrixBase<Derived>::lazyAssign(const SkylineProduct<Lhs, Rhs, SkylineTimeDenseProduct>& product) {
// typedef typename internal::remove_all<Lhs>::type _Lhs;
// internal::skyline_product_selector<typename internal::remove_all<Lhs>::type,
// typename internal::remove_all<Rhs>::type,
// Derived>::run(product.lhs(), product.rhs(), derived());
//
// return derived();
// }
// skyline * dense
template<typename Derived>
template<typename OtherDerived >
EIGEN_STRONG_INLINE const typename SkylineProductReturnType<Derived, OtherDerived>::Type
SkylineMatrixBase<Derived>::operator*(const MatrixBase<OtherDerived> &other) const {
return typename SkylineProductReturnType<Derived, OtherDerived>::Type(derived(), other.derived());
}
} // end namespace Eigen
#endif // EIGEN_SKYLINEPRODUCT_H

View File

@@ -0,0 +1,259 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Guillaume Saupin <guillaume.saupin@cea.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SKYLINE_STORAGE_H
#define EIGEN_SKYLINE_STORAGE_H
namespace Eigen {
/** Stores a skyline set of values in three structures :
* The diagonal elements
* The upper elements
* The lower elements
*
*/
template<typename Scalar>
class SkylineStorage {
typedef typename NumTraits<Scalar>::Real RealScalar;
typedef SparseIndex Index;
public:
SkylineStorage()
: m_diag(0),
m_lower(0),
m_upper(0),
m_lowerProfile(0),
m_upperProfile(0),
m_diagSize(0),
m_upperSize(0),
m_lowerSize(0),
m_upperProfileSize(0),
m_lowerProfileSize(0),
m_allocatedSize(0) {
}
SkylineStorage(const SkylineStorage& other)
: m_diag(0),
m_lower(0),
m_upper(0),
m_lowerProfile(0),
m_upperProfile(0),
m_diagSize(0),
m_upperSize(0),
m_lowerSize(0),
m_upperProfileSize(0),
m_lowerProfileSize(0),
m_allocatedSize(0) {
*this = other;
}
SkylineStorage & operator=(const SkylineStorage& other) {
resize(other.diagSize(), other.m_upperProfileSize, other.m_lowerProfileSize, other.upperSize(), other.lowerSize());
memcpy(m_diag, other.m_diag, m_diagSize * sizeof (Scalar));
memcpy(m_upper, other.m_upper, other.upperSize() * sizeof (Scalar));
memcpy(m_lower, other.m_lower, other.lowerSize() * sizeof (Scalar));
memcpy(m_upperProfile, other.m_upperProfile, m_upperProfileSize * sizeof (Index));
memcpy(m_lowerProfile, other.m_lowerProfile, m_lowerProfileSize * sizeof (Index));
return *this;
}
void swap(SkylineStorage& other) {
std::swap(m_diag, other.m_diag);
std::swap(m_upper, other.m_upper);
std::swap(m_lower, other.m_lower);
std::swap(m_upperProfile, other.m_upperProfile);
std::swap(m_lowerProfile, other.m_lowerProfile);
std::swap(m_diagSize, other.m_diagSize);
std::swap(m_upperSize, other.m_upperSize);
std::swap(m_lowerSize, other.m_lowerSize);
std::swap(m_allocatedSize, other.m_allocatedSize);
}
~SkylineStorage() {
delete[] m_diag;
delete[] m_upper;
if (m_upper != m_lower)
delete[] m_lower;
delete[] m_upperProfile;
delete[] m_lowerProfile;
}
void reserve(Index size, Index upperProfileSize, Index lowerProfileSize, Index upperSize, Index lowerSize) {
Index newAllocatedSize = size + upperSize + lowerSize;
if (newAllocatedSize > m_allocatedSize)
reallocate(size, upperProfileSize, lowerProfileSize, upperSize, lowerSize);
}
void squeeze() {
if (m_allocatedSize > m_diagSize + m_upperSize + m_lowerSize)
reallocate(m_diagSize, m_upperProfileSize, m_lowerProfileSize, m_upperSize, m_lowerSize);
}
void resize(Index diagSize, Index upperProfileSize, Index lowerProfileSize, Index upperSize, Index lowerSize, float reserveSizeFactor = 0) {
if (m_allocatedSize < diagSize + upperSize + lowerSize)
reallocate(diagSize, upperProfileSize, lowerProfileSize, upperSize + Index(reserveSizeFactor * upperSize), lowerSize + Index(reserveSizeFactor * lowerSize));
m_diagSize = diagSize;
m_upperSize = upperSize;
m_lowerSize = lowerSize;
m_upperProfileSize = upperProfileSize;
m_lowerProfileSize = lowerProfileSize;
}
inline Index diagSize() const {
return m_diagSize;
}
inline Index upperSize() const {
return m_upperSize;
}
inline Index lowerSize() const {
return m_lowerSize;
}
inline Index upperProfileSize() const {
return m_upperProfileSize;
}
inline Index lowerProfileSize() const {
return m_lowerProfileSize;
}
inline Index allocatedSize() const {
return m_allocatedSize;
}
inline void clear() {
m_diagSize = 0;
}
inline Scalar& diag(Index i) {
return m_diag[i];
}
inline const Scalar& diag(Index i) const {
return m_diag[i];
}
inline Scalar& upper(Index i) {
return m_upper[i];
}
inline const Scalar& upper(Index i) const {
return m_upper[i];
}
inline Scalar& lower(Index i) {
return m_lower[i];
}
inline const Scalar& lower(Index i) const {
return m_lower[i];
}
inline Index& upperProfile(Index i) {
return m_upperProfile[i];
}
inline const Index& upperProfile(Index i) const {
return m_upperProfile[i];
}
inline Index& lowerProfile(Index i) {
return m_lowerProfile[i];
}
inline const Index& lowerProfile(Index i) const {
return m_lowerProfile[i];
}
static SkylineStorage Map(Index* upperProfile, Index* lowerProfile, Scalar* diag, Scalar* upper, Scalar* lower, Index size, Index upperSize, Index lowerSize) {
SkylineStorage res;
res.m_upperProfile = upperProfile;
res.m_lowerProfile = lowerProfile;
res.m_diag = diag;
res.m_upper = upper;
res.m_lower = lower;
res.m_allocatedSize = res.m_diagSize = size;
res.m_upperSize = upperSize;
res.m_lowerSize = lowerSize;
return res;
}
inline void reset() {
memset(m_diag, 0, m_diagSize * sizeof (Scalar));
memset(m_upper, 0, m_upperSize * sizeof (Scalar));
memset(m_lower, 0, m_lowerSize * sizeof (Scalar));
memset(m_upperProfile, 0, m_diagSize * sizeof (Index));
memset(m_lowerProfile, 0, m_diagSize * sizeof (Index));
}
void prune(Scalar reference, RealScalar epsilon = dummy_precision<RealScalar>()) {
//TODO
}
protected:
inline void reallocate(Index diagSize, Index upperProfileSize, Index lowerProfileSize, Index upperSize, Index lowerSize) {
Scalar* diag = new Scalar[diagSize];
Scalar* upper = new Scalar[upperSize];
Scalar* lower = new Scalar[lowerSize];
Index* upperProfile = new Index[upperProfileSize];
Index* lowerProfile = new Index[lowerProfileSize];
Index copyDiagSize = (std::min)(diagSize, m_diagSize);
Index copyUpperSize = (std::min)(upperSize, m_upperSize);
Index copyLowerSize = (std::min)(lowerSize, m_lowerSize);
Index copyUpperProfileSize = (std::min)(upperProfileSize, m_upperProfileSize);
Index copyLowerProfileSize = (std::min)(lowerProfileSize, m_lowerProfileSize);
// copy
memcpy(diag, m_diag, copyDiagSize * sizeof (Scalar));
memcpy(upper, m_upper, copyUpperSize * sizeof (Scalar));
memcpy(lower, m_lower, copyLowerSize * sizeof (Scalar));
memcpy(upperProfile, m_upperProfile, copyUpperProfileSize * sizeof (Index));
memcpy(lowerProfile, m_lowerProfile, copyLowerProfileSize * sizeof (Index));
// delete old stuff
delete[] m_diag;
delete[] m_upper;
delete[] m_lower;
delete[] m_upperProfile;
delete[] m_lowerProfile;
m_diag = diag;
m_upper = upper;
m_lower = lower;
m_upperProfile = upperProfile;
m_lowerProfile = lowerProfile;
m_allocatedSize = diagSize + upperSize + lowerSize;
m_upperSize = upperSize;
m_lowerSize = lowerSize;
}
public:
Scalar* m_diag;
Scalar* m_upper;
Scalar* m_lower;
Index* m_upperProfile;
Index* m_lowerProfile;
Index m_diagSize;
Index m_upperSize;
Index m_lowerSize;
Index m_upperProfileSize;
Index m_lowerProfileSize;
Index m_allocatedSize;
};
} // end namespace Eigen
#endif // EIGEN_COMPRESSED_STORAGE_H

View File

@@ -0,0 +1,89 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Guillaume Saupin <guillaume.saupin@cea.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SKYLINEUTIL_H
#define EIGEN_SKYLINEUTIL_H
namespace Eigen {
#ifdef NDEBUG
#define EIGEN_DBG_SKYLINE(X)
#else
#define EIGEN_DBG_SKYLINE(X) X
#endif
const unsigned int SkylineBit = 0x1200;
template<typename Lhs, typename Rhs, int ProductMode> class SkylineProduct;
enum AdditionalProductEvaluationMode {SkylineTimeDenseProduct, SkylineTimeSkylineProduct, DenseTimeSkylineProduct};
enum {IsSkyline = SkylineBit};
#define EIGEN_SKYLINE_INHERIT_ASSIGNMENT_OPERATOR(Derived, Op) \
template<typename OtherDerived> \
EIGEN_STRONG_INLINE Derived& operator Op(const Eigen::SkylineMatrixBase<OtherDerived>& other) \
{ \
return Base::operator Op(other.derived()); \
} \
EIGEN_STRONG_INLINE Derived& operator Op(const Derived& other) \
{ \
return Base::operator Op(other); \
}
#define EIGEN_SKYLINE_INHERIT_SCALAR_ASSIGNMENT_OPERATOR(Derived, Op) \
template<typename Other> \
EIGEN_STRONG_INLINE Derived& operator Op(const Other& scalar) \
{ \
return Base::operator Op(scalar); \
}
#define EIGEN_SKYLINE_INHERIT_ASSIGNMENT_OPERATORS(Derived) \
EIGEN_SKYLINE_INHERIT_ASSIGNMENT_OPERATOR(Derived, =) \
EIGEN_SKYLINE_INHERIT_ASSIGNMENT_OPERATOR(Derived, +=) \
EIGEN_SKYLINE_INHERIT_ASSIGNMENT_OPERATOR(Derived, -=) \
EIGEN_SKYLINE_INHERIT_SCALAR_ASSIGNMENT_OPERATOR(Derived, *=) \
EIGEN_SKYLINE_INHERIT_SCALAR_ASSIGNMENT_OPERATOR(Derived, /=)
#define _EIGEN_SKYLINE_GENERIC_PUBLIC_INTERFACE(Derived, BaseClass) \
typedef BaseClass Base; \
typedef typename Eigen::internal::traits<Derived>::Scalar Scalar; \
typedef typename Eigen::NumTraits<Scalar>::Real RealScalar; \
typedef typename Eigen::internal::traits<Derived>::StorageKind StorageKind; \
typedef typename Eigen::internal::index<StorageKind>::type Index; \
enum { Flags = Eigen::internal::traits<Derived>::Flags, };
#define EIGEN_SKYLINE_GENERIC_PUBLIC_INTERFACE(Derived) \
_EIGEN_SKYLINE_GENERIC_PUBLIC_INTERFACE(Derived, Eigen::SkylineMatrixBase<Derived>)
template<typename Derived> class SkylineMatrixBase;
template<typename _Scalar, int _Flags = 0> class SkylineMatrix;
template<typename _Scalar, int _Flags = 0> class DynamicSkylineMatrix;
template<typename _Scalar, int _Flags = 0> class SkylineVector;
template<typename _Scalar, int _Flags = 0> class MappedSkylineMatrix;
namespace internal {
template<typename Lhs, typename Rhs> struct skyline_product_mode;
template<typename Lhs, typename Rhs, int ProductMode = skyline_product_mode<Lhs,Rhs>::value> struct SkylineProductReturnType;
template<typename T> class eval<T,IsSkyline>
{
typedef typename traits<T>::Scalar _Scalar;
enum {
_Flags = traits<T>::Flags
};
public:
typedef SkylineMatrix<_Scalar, _Flags> type;
};
} // end namespace internal
} // end namespace Eigen
#endif // EIGEN_SKYLINEUTIL_H

View File

@@ -0,0 +1,122 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H
#define EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H
namespace Eigen {
#if 0
// NOTE Have to be reimplemented as a specialization of BlockImpl< DynamicSparseMatrix<_Scalar, _Options, _Index>, ... >
// See SparseBlock.h for an example
/***************************************************************************
* specialisation for DynamicSparseMatrix
***************************************************************************/
template<typename _Scalar, int _Options, typename _Index, int Size>
class SparseInnerVectorSet<DynamicSparseMatrix<_Scalar, _Options, _Index>, Size>
: public SparseMatrixBase<SparseInnerVectorSet<DynamicSparseMatrix<_Scalar, _Options, _Index>, Size> >
{
typedef DynamicSparseMatrix<_Scalar, _Options, _Index> MatrixType;
public:
enum { IsRowMajor = internal::traits<SparseInnerVectorSet>::IsRowMajor };
EIGEN_SPARSE_PUBLIC_INTERFACE(SparseInnerVectorSet)
class InnerIterator: public MatrixType::InnerIterator
{
public:
inline InnerIterator(const SparseInnerVectorSet& xpr, Index outer)
: MatrixType::InnerIterator(xpr.m_matrix, xpr.m_outerStart + outer), m_outer(outer)
{}
inline Index row() const { return IsRowMajor ? m_outer : this->index(); }
inline Index col() const { return IsRowMajor ? this->index() : m_outer; }
protected:
Index m_outer;
};
inline SparseInnerVectorSet(const MatrixType& matrix, Index outerStart, Index outerSize)
: m_matrix(matrix), m_outerStart(outerStart), m_outerSize(outerSize)
{
eigen_assert( (outerStart>=0) && ((outerStart+outerSize)<=matrix.outerSize()) );
}
inline SparseInnerVectorSet(const MatrixType& matrix, Index outer)
: m_matrix(matrix), m_outerStart(outer), m_outerSize(Size)
{
eigen_assert(Size!=Dynamic);
eigen_assert( (outer>=0) && (outer<matrix.outerSize()) );
}
template<typename OtherDerived>
inline SparseInnerVectorSet& operator=(const SparseMatrixBase<OtherDerived>& other)
{
if (IsRowMajor != ((OtherDerived::Flags&RowMajorBit)==RowMajorBit))
{
// need to transpose => perform a block evaluation followed by a big swap
DynamicSparseMatrix<Scalar,IsRowMajor?RowMajorBit:0> aux(other);
*this = aux.markAsRValue();
}
else
{
// evaluate/copy vector per vector
for (Index j=0; j<m_outerSize.value(); ++j)
{
SparseVector<Scalar,IsRowMajor ? RowMajorBit : 0> aux(other.innerVector(j));
m_matrix.const_cast_derived()._data()[m_outerStart+j].swap(aux._data());
}
}
return *this;
}
inline SparseInnerVectorSet& operator=(const SparseInnerVectorSet& other)
{
return operator=<SparseInnerVectorSet>(other);
}
Index nonZeros() const
{
Index count = 0;
for (Index j=0; j<m_outerSize.value(); ++j)
count += m_matrix._data()[m_outerStart+j].size();
return count;
}
const Scalar& lastCoeff() const
{
EIGEN_STATIC_ASSERT_VECTOR_ONLY(SparseInnerVectorSet);
eigen_assert(m_matrix.data()[m_outerStart].size()>0);
return m_matrix.data()[m_outerStart].vale(m_matrix.data()[m_outerStart].size()-1);
}
// template<typename Sparse>
// inline SparseInnerVectorSet& operator=(const SparseMatrixBase<OtherDerived>& other)
// {
// return *this;
// }
EIGEN_STRONG_INLINE Index rows() const { return IsRowMajor ? m_outerSize.value() : m_matrix.rows(); }
EIGEN_STRONG_INLINE Index cols() const { return IsRowMajor ? m_matrix.cols() : m_outerSize.value(); }
protected:
const typename MatrixType::Nested m_matrix;
Index m_outerStart;
const internal::variable_if_dynamic<Index, Size> m_outerSize;
};
#endif
} // end namespace Eigen
#endif // EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_SparseExtra_SRCS "*.h")
INSTALL(FILES
${Eigen_SparseExtra_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/SparseExtra COMPONENT Devel
)

View File

@@ -0,0 +1,357 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_DYNAMIC_SPARSEMATRIX_H
#define EIGEN_DYNAMIC_SPARSEMATRIX_H
namespace Eigen {
/** \deprecated use a SparseMatrix in an uncompressed mode
*
* \class DynamicSparseMatrix
*
* \brief A sparse matrix class designed for matrix assembly purpose
*
* \param _Scalar the scalar type, i.e. the type of the coefficients
*
* Unlike SparseMatrix, this class provides a much higher degree of flexibility. In particular, it allows
* random read/write accesses in log(rho*outer_size) where \c rho is the probability that a coefficient is
* nonzero and outer_size is the number of columns if the matrix is column-major and the number of rows
* otherwise.
*
* Internally, the data are stored as a std::vector of compressed vector. The performances of random writes might
* decrease as the number of nonzeros per inner-vector increase. In practice, we observed very good performance
* till about 100 nonzeros/vector, and the performance remains relatively good till 500 nonzeros/vectors.
*
* \see SparseMatrix
*/
namespace internal {
template<typename _Scalar, int _Options, typename _Index>
struct traits<DynamicSparseMatrix<_Scalar, _Options, _Index> >
{
typedef _Scalar Scalar;
typedef _Index Index;
typedef Sparse StorageKind;
typedef MatrixXpr XprKind;
enum {
RowsAtCompileTime = Dynamic,
ColsAtCompileTime = Dynamic,
MaxRowsAtCompileTime = Dynamic,
MaxColsAtCompileTime = Dynamic,
Flags = _Options | NestByRefBit | LvalueBit,
CoeffReadCost = NumTraits<Scalar>::ReadCost,
SupportedAccessPatterns = OuterRandomAccessPattern
};
};
}
template<typename _Scalar, int _Options, typename _Index>
class DynamicSparseMatrix
: public SparseMatrixBase<DynamicSparseMatrix<_Scalar, _Options, _Index> >
{
public:
EIGEN_SPARSE_PUBLIC_INTERFACE(DynamicSparseMatrix)
// FIXME: why are these operator already alvailable ???
// EIGEN_SPARSE_INHERIT_ASSIGNMENT_OPERATOR(DynamicSparseMatrix, +=)
// EIGEN_SPARSE_INHERIT_ASSIGNMENT_OPERATOR(DynamicSparseMatrix, -=)
typedef MappedSparseMatrix<Scalar,Flags> Map;
using Base::IsRowMajor;
using Base::operator=;
enum {
Options = _Options
};
protected:
typedef DynamicSparseMatrix<Scalar,(Flags&~RowMajorBit)|(IsRowMajor?RowMajorBit:0)> TransposedSparseMatrix;
Index m_innerSize;
std::vector<internal::CompressedStorage<Scalar,Index> > m_data;
public:
inline Index rows() const { return IsRowMajor ? outerSize() : m_innerSize; }
inline Index cols() const { return IsRowMajor ? m_innerSize : outerSize(); }
inline Index innerSize() const { return m_innerSize; }
inline Index outerSize() const { return static_cast<Index>(m_data.size()); }
inline Index innerNonZeros(Index j) const { return m_data[j].size(); }
std::vector<internal::CompressedStorage<Scalar,Index> >& _data() { return m_data; }
const std::vector<internal::CompressedStorage<Scalar,Index> >& _data() const { return m_data; }
/** \returns the coefficient value at given position \a row, \a col
* This operation involes a log(rho*outer_size) binary search.
*/
inline Scalar coeff(Index row, Index col) const
{
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
return m_data[outer].at(inner);
}
/** \returns a reference to the coefficient value at given position \a row, \a col
* This operation involes a log(rho*outer_size) binary search. If the coefficient does not
* exist yet, then a sorted insertion into a sequential buffer is performed.
*/
inline Scalar& coeffRef(Index row, Index col)
{
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
return m_data[outer].atWithInsertion(inner);
}
class InnerIterator;
class ReverseInnerIterator;
void setZero()
{
for (Index j=0; j<outerSize(); ++j)
m_data[j].clear();
}
/** \returns the number of non zero coefficients */
Index nonZeros() const
{
Index res = 0;
for (Index j=0; j<outerSize(); ++j)
res += static_cast<Index>(m_data[j].size());
return res;
}
void reserve(Index reserveSize = 1000)
{
if (outerSize()>0)
{
Index reserveSizePerVector = (std::max)(reserveSize/outerSize(),Index(4));
for (Index j=0; j<outerSize(); ++j)
{
m_data[j].reserve(reserveSizePerVector);
}
}
}
/** Does nothing: provided for compatibility with SparseMatrix */
inline void startVec(Index /*outer*/) {}
/** \returns a reference to the non zero coefficient at position \a row, \a col assuming that:
* - the nonzero does not already exist
* - the new coefficient is the last one of the given inner vector.
*
* \sa insert, insertBackByOuterInner */
inline Scalar& insertBack(Index row, Index col)
{
return insertBackByOuterInner(IsRowMajor?row:col, IsRowMajor?col:row);
}
/** \sa insertBack */
inline Scalar& insertBackByOuterInner(Index outer, Index inner)
{
eigen_assert(outer<Index(m_data.size()) && inner<m_innerSize && "out of range");
eigen_assert(((m_data[outer].size()==0) || (m_data[outer].index(m_data[outer].size()-1)<inner))
&& "wrong sorted insertion");
m_data[outer].append(0, inner);
return m_data[outer].value(m_data[outer].size()-1);
}
inline Scalar& insert(Index row, Index col)
{
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
Index startId = 0;
Index id = static_cast<Index>(m_data[outer].size()) - 1;
m_data[outer].resize(id+2,1);
while ( (id >= startId) && (m_data[outer].index(id) > inner) )
{
m_data[outer].index(id+1) = m_data[outer].index(id);
m_data[outer].value(id+1) = m_data[outer].value(id);
--id;
}
m_data[outer].index(id+1) = inner;
m_data[outer].value(id+1) = 0;
return m_data[outer].value(id+1);
}
/** Does nothing: provided for compatibility with SparseMatrix */
inline void finalize() {}
/** Suppress all nonzeros which are smaller than \a reference under the tolerence \a epsilon */
void prune(Scalar reference, RealScalar epsilon = NumTraits<RealScalar>::dummy_precision())
{
for (Index j=0; j<outerSize(); ++j)
m_data[j].prune(reference,epsilon);
}
/** Resize the matrix without preserving the data (the matrix is set to zero)
*/
void resize(Index rows, Index cols)
{
const Index outerSize = IsRowMajor ? rows : cols;
m_innerSize = IsRowMajor ? cols : rows;
setZero();
if (Index(m_data.size()) != outerSize)
{
m_data.resize(outerSize);
}
}
void resizeAndKeepData(Index rows, Index cols)
{
const Index outerSize = IsRowMajor ? rows : cols;
const Index innerSize = IsRowMajor ? cols : rows;
if (m_innerSize>innerSize)
{
// remove all coefficients with innerCoord>=innerSize
// TODO
//std::cerr << "not implemented yet\n";
exit(2);
}
if (m_data.size() != outerSize)
{
m_data.resize(outerSize);
}
}
/** The class DynamicSparseMatrix is deprectaed */
EIGEN_DEPRECATED inline DynamicSparseMatrix()
: m_innerSize(0), m_data(0)
{
eigen_assert(innerSize()==0 && outerSize()==0);
}
/** The class DynamicSparseMatrix is deprectaed */
EIGEN_DEPRECATED inline DynamicSparseMatrix(Index rows, Index cols)
: m_innerSize(0)
{
resize(rows, cols);
}
/** The class DynamicSparseMatrix is deprectaed */
template<typename OtherDerived>
EIGEN_DEPRECATED explicit inline DynamicSparseMatrix(const SparseMatrixBase<OtherDerived>& other)
: m_innerSize(0)
{
Base::operator=(other.derived());
}
inline DynamicSparseMatrix(const DynamicSparseMatrix& other)
: Base(), m_innerSize(0)
{
*this = other.derived();
}
inline void swap(DynamicSparseMatrix& other)
{
//EIGEN_DBG_SPARSE(std::cout << "SparseMatrix:: swap\n");
std::swap(m_innerSize, other.m_innerSize);
//std::swap(m_outerSize, other.m_outerSize);
m_data.swap(other.m_data);
}
inline DynamicSparseMatrix& operator=(const DynamicSparseMatrix& other)
{
if (other.isRValue())
{
swap(other.const_cast_derived());
}
else
{
resize(other.rows(), other.cols());
m_data = other.m_data;
}
return *this;
}
/** Destructor */
inline ~DynamicSparseMatrix() {}
public:
/** \deprecated
* Set the matrix to zero and reserve the memory for \a reserveSize nonzero coefficients. */
EIGEN_DEPRECATED void startFill(Index reserveSize = 1000)
{
setZero();
reserve(reserveSize);
}
/** \deprecated use insert()
* inserts a nonzero coefficient at given coordinates \a row, \a col and returns its reference assuming that:
* 1 - the coefficient does not exist yet
* 2 - this the coefficient with greater inner coordinate for the given outer coordinate.
* In other words, assuming \c *this is column-major, then there must not exists any nonzero coefficient of coordinates
* \c i \c x \a col such that \c i >= \a row. Otherwise the matrix is invalid.
*
* \see fillrand(), coeffRef()
*/
EIGEN_DEPRECATED Scalar& fill(Index row, Index col)
{
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
return insertBack(outer,inner);
}
/** \deprecated use insert()
* Like fill() but with random inner coordinates.
* Compared to the generic coeffRef(), the unique limitation is that we assume
* the coefficient does not exist yet.
*/
EIGEN_DEPRECATED Scalar& fillrand(Index row, Index col)
{
return insert(row,col);
}
/** \deprecated use finalize()
* Does nothing. Provided for compatibility with SparseMatrix. */
EIGEN_DEPRECATED void endFill() {}
# ifdef EIGEN_DYNAMICSPARSEMATRIX_PLUGIN
# include EIGEN_DYNAMICSPARSEMATRIX_PLUGIN
# endif
};
template<typename Scalar, int _Options, typename _Index>
class DynamicSparseMatrix<Scalar,_Options,_Index>::InnerIterator : public SparseVector<Scalar,_Options,_Index>::InnerIterator
{
typedef typename SparseVector<Scalar,_Options,_Index>::InnerIterator Base;
public:
InnerIterator(const DynamicSparseMatrix& mat, Index outer)
: Base(mat.m_data[outer]), m_outer(outer)
{}
inline Index row() const { return IsRowMajor ? m_outer : Base::index(); }
inline Index col() const { return IsRowMajor ? Base::index() : m_outer; }
protected:
const Index m_outer;
};
template<typename Scalar, int _Options, typename _Index>
class DynamicSparseMatrix<Scalar,_Options,_Index>::ReverseInnerIterator : public SparseVector<Scalar,_Options,_Index>::ReverseInnerIterator
{
typedef typename SparseVector<Scalar,_Options,_Index>::ReverseInnerIterator Base;
public:
ReverseInnerIterator(const DynamicSparseMatrix& mat, Index outer)
: Base(mat.m_data[outer]), m_outer(outer)
{}
inline Index row() const { return IsRowMajor ? m_outer : Base::index(); }
inline Index col() const { return IsRowMajor ? Base::index() : m_outer; }
protected:
const Index m_outer;
};
} // end namespace Eigen
#endif // EIGEN_DYNAMIC_SPARSEMATRIX_H

View File

@@ -0,0 +1,273 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2011 Gael Guennebaud <gael.guennebaud@inria.fr>
// Copyright (C) 2012 Desire NUENTSA WAKAM <desire.nuentsa_wakam@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_SPARSE_MARKET_IO_H
#define EIGEN_SPARSE_MARKET_IO_H
#include <iostream>
namespace Eigen {
namespace internal
{
template <typename Scalar>
inline bool GetMarketLine (std::stringstream& line, int& M, int& N, int& i, int& j, Scalar& value)
{
line >> i >> j >> value;
i--;
j--;
if(i>=0 && j>=0 && i<M && j<N)
{
return true;
}
else
return false;
}
template <typename Scalar>
inline bool GetMarketLine (std::stringstream& line, int& M, int& N, int& i, int& j, std::complex<Scalar>& value)
{
Scalar valR, valI;
line >> i >> j >> valR >> valI;
i--;
j--;
if(i>=0 && j>=0 && i<M && j<N)
{
value = std::complex<Scalar>(valR, valI);
return true;
}
else
return false;
}
template <typename RealScalar>
inline void GetVectorElt (const std::string& line, RealScalar& val)
{
std::istringstream newline(line);
newline >> val;
}
template <typename RealScalar>
inline void GetVectorElt (const std::string& line, std::complex<RealScalar>& val)
{
RealScalar valR, valI;
std::istringstream newline(line);
newline >> valR >> valI;
val = std::complex<RealScalar>(valR, valI);
}
template<typename Scalar>
inline void putMarketHeader(std::string& header,int sym)
{
header= "%%MatrixMarket matrix coordinate ";
if(internal::is_same<Scalar, std::complex<float> >::value || internal::is_same<Scalar, std::complex<double> >::value)
{
header += " complex";
if(sym == Symmetric) header += " symmetric";
else if (sym == SelfAdjoint) header += " Hermitian";
else header += " general";
}
else
{
header += " real";
if(sym == Symmetric) header += " symmetric";
else header += " general";
}
}
template<typename Scalar>
inline void PutMatrixElt(Scalar value, int row, int col, std::ofstream& out)
{
out << row << " "<< col << " " << value << "\n";
}
template<typename Scalar>
inline void PutMatrixElt(std::complex<Scalar> value, int row, int col, std::ofstream& out)
{
out << row << " " << col << " " << value.real() << " " << value.imag() << "\n";
}
template<typename Scalar>
inline void putVectorElt(Scalar value, std::ofstream& out)
{
out << value << "\n";
}
template<typename Scalar>
inline void putVectorElt(std::complex<Scalar> value, std::ofstream& out)
{
out << value.real << " " << value.imag()<< "\n";
}
} // end namepsace internal
inline bool getMarketHeader(const std::string& filename, int& sym, bool& iscomplex, bool& isvector)
{
sym = 0;
isvector = false;
std::ifstream in(filename.c_str(),std::ios::in);
if(!in)
return false;
std::string line;
// The matrix header is always the first line in the file
std::getline(in, line); eigen_assert(in.good());
std::stringstream fmtline(line);
std::string substr[5];
fmtline>> substr[0] >> substr[1] >> substr[2] >> substr[3] >> substr[4];
if(substr[2].compare("array") == 0) isvector = true;
if(substr[3].compare("complex") == 0) iscomplex = true;
if(substr[4].compare("symmetric") == 0) sym = Symmetric;
else if (substr[4].compare("Hermitian") == 0) sym = SelfAdjoint;
return true;
}
template<typename SparseMatrixType>
bool loadMarket(SparseMatrixType& mat, const std::string& filename)
{
typedef typename SparseMatrixType::Scalar Scalar;
std::ifstream input(filename.c_str(),std::ios::in);
if(!input)
return false;
const int maxBuffersize = 2048;
char buffer[maxBuffersize];
bool readsizes = false;
typedef Triplet<Scalar,int> T;
std::vector<T> elements;
int M(-1), N(-1), NNZ(-1);
int count = 0;
while(input.getline(buffer, maxBuffersize))
{
// skip comments
//NOTE An appropriate test should be done on the header to get the symmetry
if(buffer[0]=='%')
continue;
std::stringstream line(buffer);
if(!readsizes)
{
line >> M >> N >> NNZ;
if(M > 0 && N > 0 && NNZ > 0)
{
readsizes = true;
std::cout << "sizes: " << M << "," << N << "," << NNZ << "\n";
mat.resize(M,N);
mat.reserve(NNZ);
}
}
else
{
int i(-1), j(-1);
Scalar value;
if( internal::GetMarketLine(line, M, N, i, j, value) )
{
++ count;
elements.push_back(T(i,j,value));
}
else
std::cerr << "Invalid read: " << i << "," << j << "\n";
}
}
mat.setFromTriplets(elements.begin(), elements.end());
if(count!=NNZ)
std::cerr << count << "!=" << NNZ << "\n";
input.close();
return true;
}
template<typename VectorType>
bool loadMarketVector(VectorType& vec, const std::string& filename)
{
typedef typename VectorType::Scalar Scalar;
std::ifstream in(filename.c_str(), std::ios::in);
if(!in)
return false;
std::string line;
int n(0), col(0);
do
{ // Skip comments
std::getline(in, line); eigen_assert(in.good());
} while (line[0] == '%');
std::istringstream newline(line);
newline >> n >> col;
eigen_assert(n>0 && col>0);
vec.resize(n);
int i = 0;
Scalar value;
while ( std::getline(in, line) && (i < n) ){
internal::GetVectorElt(line, value);
vec(i++) = value;
}
in.close();
if (i!=n){
std::cerr<< "Unable to read all elements from file " << filename << "\n";
return false;
}
return true;
}
template<typename SparseMatrixType>
bool saveMarket(const SparseMatrixType& mat, const std::string& filename, int sym = 0)
{
typedef typename SparseMatrixType::Scalar Scalar;
std::ofstream out(filename.c_str(),std::ios::out);
if(!out)
return false;
out.flags(std::ios_base::scientific);
out.precision(64);
std::string header;
internal::putMarketHeader<Scalar>(header, sym);
out << header << std::endl;
out << mat.rows() << " " << mat.cols() << " " << mat.nonZeros() << "\n";
int count = 0;
for(int j=0; j<mat.outerSize(); ++j)
for(typename SparseMatrixType::InnerIterator it(mat,j); it; ++it)
{
++ count;
internal::PutMatrixElt(it.value(), it.row()+1, it.col()+1, out);
// out << it.row()+1 << " " << it.col()+1 << " " << it.value() << "\n";
}
out.close();
return true;
}
template<typename VectorType>
bool saveMarketVector (const VectorType& vec, const std::string& filename)
{
typedef typename VectorType::Scalar Scalar;
std::ofstream out(filename.c_str(),std::ios::out);
if(!out)
return false;
out.flags(std::ios_base::scientific);
out.precision(64);
if(internal::is_same<Scalar, std::complex<float> >::value || internal::is_same<Scalar, std::complex<double> >::value)
out << "%%MatrixMarket matrix array complex general\n";
else
out << "%%MatrixMarket matrix array real general\n";
out << vec.size() << " "<< 1 << "\n";
for (int i=0; i < vec.size(); i++){
internal::putVectorElt(vec(i), out);
}
out.close();
return true;
}
} // end namespace Eigen
#endif // EIGEN_SPARSE_MARKET_IO_H

View File

@@ -0,0 +1,232 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2012 Desire NUENTSA WAKAM <desire.nuentsa_wakam@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_BROWSE_MATRICES_H
#define EIGEN_BROWSE_MATRICES_H
namespace Eigen {
enum {
SPD = 0x100,
NonSymmetric = 0x0
};
/**
* @brief Iterator to browse matrices from a specified folder
*
* This is used to load all the matrices from a folder.
* The matrices should be in Matrix Market format
* It is assumed that the matrices are named as matname.mtx
* and matname_SPD.mtx if the matrix is Symmetric and positive definite (or Hermitian)
* The right hand side vectors are loaded as well, if they exist.
* They should be named as matname_b.mtx.
* Note that the right hand side for a SPD matrix is named as matname_SPD_b.mtx
*
* Sometimes a reference solution is available. In this case, it should be named as matname_x.mtx
*
* Sample code
* \code
*
* \endcode
*
* \tparam Scalar The scalar type
*/
template <typename Scalar>
class MatrixMarketIterator
{
public:
typedef Matrix<Scalar,Dynamic,1> VectorType;
typedef SparseMatrix<Scalar,ColMajor> MatrixType;
public:
MatrixMarketIterator(const std::string folder):m_sym(0),m_isvalid(false),m_matIsLoaded(false),m_hasRhs(false),m_hasrefX(false),m_folder(folder)
{
m_folder_id = opendir(folder.c_str());
if (!m_folder_id){
m_isvalid = false;
std::cerr << "The provided Matrix folder could not be opened \n\n";
abort();
}
Getnextvalidmatrix();
}
~MatrixMarketIterator()
{
if (m_folder_id) closedir(m_folder_id);
}
inline MatrixMarketIterator& operator++()
{
m_matIsLoaded = false;
m_hasrefX = false;
m_hasRhs = false;
Getnextvalidmatrix();
return *this;
}
inline operator bool() const { return m_isvalid;}
/** Return the sparse matrix corresponding to the current file */
inline MatrixType& matrix()
{
// Read the matrix
if (m_matIsLoaded) return m_mat;
std::string matrix_file = m_folder + "/" + m_matname + ".mtx";
if ( !loadMarket(m_mat, matrix_file))
{
m_matIsLoaded = false;
return m_mat;
}
m_matIsLoaded = true;
if (m_sym != NonSymmetric)
{ // Store the upper part of the matrix. It is needed by the solvers dealing with nonsymmetric matrices ??
MatrixType B;
B = m_mat;
m_mat = B.template selfadjointView<Lower>();
}
return m_mat;
}
/** Return the right hand side corresponding to the current matrix.
* If the rhs file is not provided, a random rhs is generated
*/
inline VectorType& rhs()
{
// Get the right hand side
if (m_hasRhs) return m_rhs;
std::string rhs_file;
rhs_file = m_folder + "/" + m_matname + "_b.mtx"; // The pattern is matname_b.mtx
m_hasRhs = Fileexists(rhs_file);
if (m_hasRhs)
{
m_rhs.resize(m_mat.cols());
m_hasRhs = loadMarketVector(m_rhs, rhs_file);
}
if (!m_hasRhs)
{
// Generate a random right hand side
if (!m_matIsLoaded) this->matrix();
m_refX.resize(m_mat.cols());
m_refX.setRandom();
m_rhs = m_mat * m_refX;
m_hasrefX = true;
m_hasRhs = true;
}
return m_rhs;
}
/** Return a reference solution
* If it is not provided and if the right hand side is not available
* then refX is randomly generated such that A*refX = b
* where A and b are the matrix and the rhs.
* Note that when a rhs is provided, refX is not available
*/
inline VectorType& refX()
{
// Check if a reference solution is provided
if (m_hasrefX) return m_refX;
std::string lhs_file;
lhs_file = m_folder + "/" + m_matname + "_x.mtx";
m_hasrefX = Fileexists(lhs_file);
if (m_hasrefX)
{
m_refX.resize(m_mat.cols());
m_hasrefX = loadMarketVector(m_refX, lhs_file);
}
return m_refX;
}
inline std::string& matname() { return m_matname; }
inline int sym() { return m_sym; }
inline bool hasRhs() {return m_hasRhs; }
inline bool hasrefX() {return m_hasrefX; }
protected:
inline bool Fileexists(std::string file)
{
std::ifstream file_id(file.c_str());
if (!file_id.good() )
{
return false;
}
else
{
file_id.close();
return true;
}
}
void Getnextvalidmatrix( )
{
m_isvalid = false;
// Here, we return with the next valid matrix in the folder
while ( (m_curs_id = readdir(m_folder_id)) != NULL) {
m_isvalid = false;
std::string curfile;
curfile = m_folder + "/" + m_curs_id->d_name;
// Discard if it is a folder
if (m_curs_id->d_type == DT_DIR) continue; //FIXME This may not be available on non BSD systems
// struct stat st_buf;
// stat (curfile.c_str(), &st_buf);
// if (S_ISDIR(st_buf.st_mode)) continue;
// Determine from the header if it is a matrix or a right hand side
bool isvector,iscomplex=false;
if(!getMarketHeader(curfile,m_sym,iscomplex,isvector)) continue;
if(isvector) continue;
if (!iscomplex)
{
if(internal::is_same<Scalar, std::complex<float> >::value || internal::is_same<Scalar, std::complex<double> >::value)
continue;
}
if (iscomplex)
{
if(internal::is_same<Scalar, float>::value || internal::is_same<Scalar, double>::value)
continue;
}
// Get the matrix name
std::string filename = m_curs_id->d_name;
m_matname = filename.substr(0, filename.length()-4);
// Find if the matrix is SPD
size_t found = m_matname.find("SPD");
if( (found!=std::string::npos) && (m_sym != NonSymmetric) )
m_sym = SPD;
m_isvalid = true;
break;
}
}
int m_sym; // Symmetry of the matrix
MatrixType m_mat; // Current matrix
VectorType m_rhs; // Current vector
VectorType m_refX; // The reference solution, if exists
std::string m_matname; // Matrix Name
bool m_isvalid;
bool m_matIsLoaded; // Determine if the matrix has already been loaded from the file
bool m_hasRhs; // The right hand side exists
bool m_hasrefX; // A reference solution is provided
std::string m_folder;
DIR * m_folder_id;
struct dirent *m_curs_id;
};
} // end namespace Eigen
#endif

View File

@@ -0,0 +1,327 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_RANDOMSETTER_H
#define EIGEN_RANDOMSETTER_H
namespace Eigen {
/** Represents a std::map
*
* \see RandomSetter
*/
template<typename Scalar> struct StdMapTraits
{
typedef int KeyType;
typedef std::map<KeyType,Scalar> Type;
enum {
IsSorted = 1
};
static void setInvalidKey(Type&, const KeyType&) {}
};
#ifdef EIGEN_UNORDERED_MAP_SUPPORT
/** Represents a std::unordered_map
*
* To use it you need to both define EIGEN_UNORDERED_MAP_SUPPORT and include the unordered_map header file
* yourself making sure that unordered_map is defined in the std namespace.
*
* For instance, with current version of gcc you can either enable C++0x standard (-std=c++0x) or do:
* \code
* #include <tr1/unordered_map>
* #define EIGEN_UNORDERED_MAP_SUPPORT
* namespace std {
* using std::tr1::unordered_map;
* }
* \endcode
*
* \see RandomSetter
*/
template<typename Scalar> struct StdUnorderedMapTraits
{
typedef int KeyType;
typedef std::unordered_map<KeyType,Scalar> Type;
enum {
IsSorted = 0
};
static void setInvalidKey(Type&, const KeyType&) {}
};
#endif // EIGEN_UNORDERED_MAP_SUPPORT
#ifdef _DENSE_HASH_MAP_H_
/** Represents a google::dense_hash_map
*
* \see RandomSetter
*/
template<typename Scalar> struct GoogleDenseHashMapTraits
{
typedef int KeyType;
typedef google::dense_hash_map<KeyType,Scalar> Type;
enum {
IsSorted = 0
};
static void setInvalidKey(Type& map, const KeyType& k)
{ map.set_empty_key(k); }
};
#endif
#ifdef _SPARSE_HASH_MAP_H_
/** Represents a google::sparse_hash_map
*
* \see RandomSetter
*/
template<typename Scalar> struct GoogleSparseHashMapTraits
{
typedef int KeyType;
typedef google::sparse_hash_map<KeyType,Scalar> Type;
enum {
IsSorted = 0
};
static void setInvalidKey(Type&, const KeyType&) {}
};
#endif
/** \class RandomSetter
*
* \brief The RandomSetter is a wrapper object allowing to set/update a sparse matrix with random access
*
* \param SparseMatrixType the type of the sparse matrix we are updating
* \param MapTraits a traits class representing the map implementation used for the temporary sparse storage.
* Its default value depends on the system.
* \param OuterPacketBits defines the number of rows (or columns) manage by a single map object
* as a power of two exponent.
*
* This class temporarily represents a sparse matrix object using a generic map implementation allowing for
* efficient random access. The conversion from the compressed representation to a hash_map object is performed
* in the RandomSetter constructor, while the sparse matrix is updated back at destruction time. This strategy
* suggest the use of nested blocks as in this example:
*
* \code
* SparseMatrix<double> m(rows,cols);
* {
* RandomSetter<SparseMatrix<double> > w(m);
* // don't use m but w instead with read/write random access to the coefficients:
* for(;;)
* w(rand(),rand()) = rand;
* }
* // when w is deleted, the data are copied back to m
* // and m is ready to use.
* \endcode
*
* Since hash_map objects are not fully sorted, representing a full matrix as a single hash_map would
* involve a big and costly sort to update the compressed matrix back. To overcome this issue, a RandomSetter
* use multiple hash_map, each representing 2^OuterPacketBits columns or rows according to the storage order.
* To reach optimal performance, this value should be adjusted according to the average number of nonzeros
* per rows/columns.
*
* The possible values for the template parameter MapTraits are:
* - \b StdMapTraits: corresponds to std::map. (does not perform very well)
* - \b GnuHashMapTraits: corresponds to __gnu_cxx::hash_map (available only with GCC)
* - \b GoogleDenseHashMapTraits: corresponds to google::dense_hash_map (best efficiency, reasonable memory consumption)
* - \b GoogleSparseHashMapTraits: corresponds to google::sparse_hash_map (best memory consumption, relatively good performance)
*
* The default map implementation depends on the availability, and the preferred order is:
* GoogleSparseHashMapTraits, GnuHashMapTraits, and finally StdMapTraits.
*
* For performance and memory consumption reasons it is highly recommended to use one of
* the Google's hash_map implementation. To enable the support for them, you have two options:
* - \#include <google/dense_hash_map> yourself \b before Eigen/Sparse header
* - define EIGEN_GOOGLEHASH_SUPPORT
* In the later case the inclusion of <google/dense_hash_map> is made for you.
*
* \see http://code.google.com/p/google-sparsehash/
*/
template<typename SparseMatrixType,
template <typename T> class MapTraits =
#if defined _DENSE_HASH_MAP_H_
GoogleDenseHashMapTraits
#elif defined _HASH_MAP
GnuHashMapTraits
#else
StdMapTraits
#endif
,int OuterPacketBits = 6>
class RandomSetter
{
typedef typename SparseMatrixType::Scalar Scalar;
typedef typename SparseMatrixType::Index Index;
struct ScalarWrapper
{
ScalarWrapper() : value(0) {}
Scalar value;
};
typedef typename MapTraits<ScalarWrapper>::KeyType KeyType;
typedef typename MapTraits<ScalarWrapper>::Type HashMapType;
static const int OuterPacketMask = (1 << OuterPacketBits) - 1;
enum {
SwapStorage = 1 - MapTraits<ScalarWrapper>::IsSorted,
TargetRowMajor = (SparseMatrixType::Flags & RowMajorBit) ? 1 : 0,
SetterRowMajor = SwapStorage ? 1-TargetRowMajor : TargetRowMajor
};
public:
/** Constructs a random setter object from the sparse matrix \a target
*
* Note that the initial value of \a target are imported. If you want to re-set
* a sparse matrix from scratch, then you must set it to zero first using the
* setZero() function.
*/
inline RandomSetter(SparseMatrixType& target)
: mp_target(&target)
{
const Index outerSize = SwapStorage ? target.innerSize() : target.outerSize();
const Index innerSize = SwapStorage ? target.outerSize() : target.innerSize();
m_outerPackets = outerSize >> OuterPacketBits;
if (outerSize&OuterPacketMask)
m_outerPackets += 1;
m_hashmaps = new HashMapType[m_outerPackets];
// compute number of bits needed to store inner indices
Index aux = innerSize - 1;
m_keyBitsOffset = 0;
while (aux)
{
++m_keyBitsOffset;
aux = aux >> 1;
}
KeyType ik = (1<<(OuterPacketBits+m_keyBitsOffset));
for (Index k=0; k<m_outerPackets; ++k)
MapTraits<ScalarWrapper>::setInvalidKey(m_hashmaps[k],ik);
// insert current coeffs
for (Index j=0; j<mp_target->outerSize(); ++j)
for (typename SparseMatrixType::InnerIterator it(*mp_target,j); it; ++it)
(*this)(TargetRowMajor?j:it.index(), TargetRowMajor?it.index():j) = it.value();
}
/** Destructor updating back the sparse matrix target */
~RandomSetter()
{
KeyType keyBitsMask = (1<<m_keyBitsOffset)-1;
if (!SwapStorage) // also means the map is sorted
{
mp_target->setZero();
mp_target->makeCompressed();
mp_target->reserve(nonZeros());
Index prevOuter = -1;
for (Index k=0; k<m_outerPackets; ++k)
{
const Index outerOffset = (1<<OuterPacketBits) * k;
typename HashMapType::iterator end = m_hashmaps[k].end();
for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it)
{
const Index outer = (it->first >> m_keyBitsOffset) + outerOffset;
const Index inner = it->first & keyBitsMask;
if (prevOuter!=outer)
{
for (Index j=prevOuter+1;j<=outer;++j)
mp_target->startVec(j);
prevOuter = outer;
}
mp_target->insertBackByOuterInner(outer, inner) = it->second.value;
}
}
mp_target->finalize();
}
else
{
VectorXi positions(mp_target->outerSize());
positions.setZero();
// pass 1
for (Index k=0; k<m_outerPackets; ++k)
{
typename HashMapType::iterator end = m_hashmaps[k].end();
for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it)
{
const Index outer = it->first & keyBitsMask;
++positions[outer];
}
}
// prefix sum
Index count = 0;
for (Index j=0; j<mp_target->outerSize(); ++j)
{
Index tmp = positions[j];
mp_target->outerIndexPtr()[j] = count;
positions[j] = count;
count += tmp;
}
mp_target->makeCompressed();
mp_target->outerIndexPtr()[mp_target->outerSize()] = count;
mp_target->resizeNonZeros(count);
// pass 2
for (Index k=0; k<m_outerPackets; ++k)
{
const Index outerOffset = (1<<OuterPacketBits) * k;
typename HashMapType::iterator end = m_hashmaps[k].end();
for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it)
{
const Index inner = (it->first >> m_keyBitsOffset) + outerOffset;
const Index outer = it->first & keyBitsMask;
// sorted insertion
// Note that we have to deal with at most 2^OuterPacketBits unsorted coefficients,
// moreover those 2^OuterPacketBits coeffs are likely to be sparse, an so only a
// small fraction of them have to be sorted, whence the following simple procedure:
Index posStart = mp_target->outerIndexPtr()[outer];
Index i = (positions[outer]++) - 1;
while ( (i >= posStart) && (mp_target->innerIndexPtr()[i] > inner) )
{
mp_target->valuePtr()[i+1] = mp_target->valuePtr()[i];
mp_target->innerIndexPtr()[i+1] = mp_target->innerIndexPtr()[i];
--i;
}
mp_target->innerIndexPtr()[i+1] = inner;
mp_target->valuePtr()[i+1] = it->second.value;
}
}
}
delete[] m_hashmaps;
}
/** \returns a reference to the coefficient at given coordinates \a row, \a col */
Scalar& operator() (Index row, Index col)
{
const Index outer = SetterRowMajor ? row : col;
const Index inner = SetterRowMajor ? col : row;
const Index outerMajor = outer >> OuterPacketBits; // index of the packet/map
const Index outerMinor = outer & OuterPacketMask; // index of the inner vector in the packet
const KeyType key = (KeyType(outerMinor)<<m_keyBitsOffset) | inner;
return m_hashmaps[outerMajor][key].value;
}
/** \returns the number of non zero coefficients
*
* \note According to the underlying map/hash_map implementation,
* this function might be quite expensive.
*/
Index nonZeros() const
{
Index nz = 0;
for (Index k=0; k<m_outerPackets; ++k)
nz += static_cast<Index>(m_hashmaps[k].size());
return nz;
}
protected:
HashMapType* m_hashmaps;
SparseMatrixType* mp_target;
Index m_outerPackets;
unsigned char m_keyBitsOffset;
};
} // end namespace Eigen
#endif // EIGEN_RANDOMSETTER_H

View File

@@ -0,0 +1,6 @@
FILE(GLOB Eigen_Splines_SRCS "*.h")
INSTALL(FILES
${Eigen_Splines_SRCS}
DESTINATION ${INCLUDE_INSTALL_DIR}/unsupported/Eigen/src/Splines COMPONENT Devel
)

Some files were not shown because too many files have changed in this diff Show More