Skip to content

Commit

Permalink
Merge pull request #1111 from lambday/develop
Browse files Browse the repository at this point in the history
JacobiEllipticFunctions added
  • Loading branch information
karlnapf committed May 22, 2013
2 parents efbbbc4 + b005795 commit 8bb3fd5
Show file tree
Hide file tree
Showing 5 changed files with 505 additions and 0 deletions.
41 changes: 41 additions & 0 deletions src/configure
Expand Up @@ -103,6 +103,7 @@ _mosek=auto
_arpack=auto
_nlopt=auto
_eigen3=auto
_arprec=no
_gmock=auto
_gtest=auto
_bigstates=yes
Expand Down Expand Up @@ -1435,6 +1436,7 @@ Optional features:
--enable-superlu enable SuperLU [auto]
--enable-nlopt enable NLOPT [auto]
--enable-eigen3 enable Eigen3 [auto]
--enable-arprec enable Arprec [disabled]
--enable-cplex enable code using CPLEX [auto]
--enable-lpsolve enable code using lpsolve [auto]
--enable-glpk enable code using GLPK [auto]
Expand Down Expand Up @@ -1462,6 +1464,7 @@ Optional features:
--disable-superlu disable SuperLU [auto]
--disable-nlopt disable NLOPT [auto]
--disable-eigen3 disable Eigen3 [auto]
--disable-arprec disable Arprec [disabled]
--disable-gmock disable google mocking framework [auto]
--disable-gtest disable google testing framework [auto]
--disable-cplex disable code using CPLEX [auto]
Expand Down Expand Up @@ -1693,6 +1696,8 @@ EOF
--enable-nlopt) _nlopt=yes ;;
--disable-eigen3) _eigen3=no ;;
--enable-eigen3) _eigen3=yes ;;
--disable-arprec) _arprec=no ;;
--enable-arprec) _arprec=yes ;;
--disable-gtest) _gtest=no ;;
--enable-gtest) _gtest=yes ;;
--disable-gmock) _gmock=no ;;
Expand Down Expand Up @@ -3675,6 +3680,41 @@ EOF
fi
}

check_arprec()
{
if test "$_arprec" = yes || test "$_arprec" = auto
then
cat > $TMPCXX << EOF
#include <arprec/mp_real.h>
#include <arprec/mp_complex.h>
int main()
{
mp::mp_init(100,NULL,true);
mp_real pi=mp_real::_pi;
mp_complex sin_u=sin(mp_complex(20));
mp::mp_finalize();
return 0;
}
EOF
echocheck "ARPREC support"
if cxx_check -larprec
then
echores "yes"
HAVE_ARPREC='#define HAVE_ARPREC 1'
DEFINES="$DEFINES -DHAVE_ARPREC"
POSTLINKFLAGS="$POSTLINKFLAGS -larprec"
else
if test "$_arprec" = yes
then
die "ARPREC not found"
else
echores "no"
fi
fi
fi
}

check_mosek()
{
if test "$_mosek" = yes || test "$_mosek" = auto
Expand Down Expand Up @@ -5589,6 +5629,7 @@ check_mosek
check_arpack
check_nlopt
check_eigen3
check_arprec
check_cplex
check_glpk
check_lzo
Expand Down
134 changes: 134 additions & 0 deletions src/shogun/mathematics/JacobiEllipticFunctions.cpp
@@ -0,0 +1,134 @@
/*
* This program is free software; 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 3 of the License, or
* (at your option) any later version.
*
* Written (W) 2013 Soumyajit De
*
* KRYLSTAT Copyright 2011 by Erlend Aune <erlenda@math.ntnu.no> under GPL2+
* (few parts rewritten and adjusted for shogun)
*/

#include <shogun/mathematics/JacobiEllipticFunctions.h>

using namespace shogun;

void CJacobiEllipticFunctions::ellKKP(Real L, Real &K, Real &Kp)
{
REQUIRE(L>=0.0,
"CJacobiEllipticFunctions::ellKKP(): \
Parameter L should be non-negative\n");
#ifdef HAVE_ARPREC
const Real eps=Real(std::numeric_limits<float64_t>::epsilon());
const Real pi=mp_real::_pi;
#else
const Real eps=std::numeric_limits<Real>::epsilon();
const Real pi=M_PI;
#endif //HAVE_ARPREC
if (L>10.0)
{
K=pi*0.5;
Kp=pi*L+log(4.0);
}
else
{
Real m=exp(-2.0*pi*L);
Real mp=1.0-m;
if (m<eps)
{
K=compute_quarter_period(sqrt(mp));
Kp=Real(std::numeric_limits<float64_t>::max());
}
else if (mp<eps)
{
K=Real(std::numeric_limits<float64_t>::max());
Kp=compute_quarter_period(sqrt(m));
}
else
{
K=compute_quarter_period(sqrt(mp));
Kp=compute_quarter_period(sqrt(m));
}
}
}

void CJacobiEllipticFunctions
::ellPJC(Complex u, Real m, Complex &sn, Complex &cn, Complex &dn)
{
REQUIRE(m>=0.0 && m<=1.0,
"CJacobiEllipticFunctions::ellPJC(): \
Parameter m should be >=0 and <=1\n");

#ifdef HAVE_ARPREC
const Real eps=sqrt(mp_real::_eps);
#else
const Real eps=sqrt(std::numeric_limits<Real>::epsilon());
#endif //HAVE_ARPREC
if (m>=(1.0-eps))
{
#ifdef HAVE_ARPREC
std::complex<float64_t> _u(dble(u.real),dble(u.imag));
std::complex<float64_t> t=tanh(_u);
std::complex<float64_t> b=cosh(_u);
std::complex<float64_t> twon=b*sinh(_u);
std::complex<float64_t> ai=0.25*(1.0-dble(m));
std::complex<float64_t> _sn=t+ai*(twon-_u)/(b*b);
std::complex<float64_t> phi=1.0/b;
std::complex<float64_t> _cn=phi-ai*(twon-_u);
std::complex<float64_t> _dn=phi+ai*(twon+_u);
sn=mp_complex(_sn.real(),_sn.imag());
cn=mp_complex(_cn.real(),_cn.imag());
dn=mp_complex(_dn.real(),_dn.imag());
#else
Complex t=tanh(u);
Complex b=cosh(u);
Complex ai=0.25*(1.0-m);
Complex twon=b*sinh(u);
sn=t+ai*(twon-u)/(b*b);
Complex phi=Real(1.0)/b;
ai*=t*phi;
cn=phi-ai*(twon-u);
dn=phi+ai*(twon+u);
#endif //HAVE_ARPREC
}
else
{
const Real prec=4.0*eps;
const index_t MAX_ITER=128;
index_t i=0;
Real kappa[MAX_ITER];

while (i<MAX_ITER && m>prec)
{
Real k;
if (m>0.001)
{
Real mp=sqrt(1.0-m);
k=(1.0-mp)/(1.0+mp);
}
else
k=poly_six(m/4.0);
u/=(1.0+k);
m=k*k;
kappa[i++]=k;
}
Complex sin_u=sin(u);
Complex cos_u=cos(u);
Complex t=Real(0.25*m)*(u-sin_u*cos_u);
sn=sin_u-t*cos_u;
cn=cos_u+t*sin_u;
dn=Real(1.0)+Real(0.5*m)*(cos_u*cos_u);

i--;
while (i>=0)
{
Real k=kappa[i--];
Complex ksn2=k*(sn*sn);
Complex d=Real(1.0)+ksn2;
sn*=(1.0+k)/d;
cn*=dn/d;
dn=(Real(1.0)-ksn2)/d;
}
}
}
164 changes: 164 additions & 0 deletions src/shogun/mathematics/JacobiEllipticFunctions.h
@@ -0,0 +1,164 @@
/*
* This program is free software; 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 3 of the License, or
* (at your option) any later version.
*
* Written (W) 2013 Soumyajit De
*
* KRYLSTAT Copyright 2011 by Erlend Aune <erlenda@math.ntnu.no> under GPL2+
* (few parts rewritten and adjusted for shogun)
*
* NOTE: For higher precision, the methods in this class rely on an external
* library, ARPREC (http://crd-legacy.lbl.gov/~dhbailey/mpdist/), in absense of
* which they fallback to shogun datatypes. To use it with shogun, configure
* ARPREC with "./configure 'CXX c++ -fPIC'" in order to link.
*/

#ifndef __JACOBI_ELLIPTIC_FUNCTIONS_H_
#define __JACOBI_ELLIPTIC_FUNCTIONS_H_

#include <shogun/lib/config.h>
#include <shogun/base/SGObject.h>
#include <complex>
#include <limits>
#include <math.h>

#ifdef HAVE_ARPREC
#include <arprec/mp_real.h>
#include <arprec/mp_complex.h>
#endif //HAVE_ARPREC

namespace shogun
{

/** @brief Class that contains methods for computing Jacobi elliptic functions
* related to complex analysis. These functions are inverse of the elliptic
* integral of first kind, i.e.
* \f[
* u(k,m)=\int_{0}^{k}\frac{dt}{\sqrt{(1-t^{2})(1-m^{2}t^{2})}}
* =\int_{0}^{\varphi}\frac{d\theta}{\sqrt{(1-m^{2}sin^{2}\theta)}}
* \f]
* where \f$k=sin\varphi\f$, \f$t=sin\theta\f$ and parameter \f$m, 0\le m
* \le 1\f$ is called modulus. There main Jacobi elliptic functions are defined
* as \f$sn(u,m)=k=sin\theta\f$, \f$cn(u,m)=cos\theta=\sqrt{1-sn(u,m)^{2}}\f$
* and \f$dn(u,m)=\sqrt{1-m^{2}sn(u,m)^{2}}\f$.
* For \f$k=1\f$, i.e. \f$\varphi=\frac{\pi}{2}\f$, \f$u(1,m)=K(m)\f$ is known
* as the complete elliptic integral of first kind. Similarly, \f$u(1,m))=
* K'(m')\f$, \f$m'=\sqrt{1-m^{2}}\f$ is called the complementary complete
* elliptic integral of first kind. Jacobi functions are double periodic with
* quardratic periods \f$K\f$ and \f$K'\f$.
*
* This class provides two sets of methods for computing \f$K,K'\f$, and
* \f$sn,cn,dn\f$. Useful for computing rational approximation of matrix
* functions given by Cauchy's integral formula, etc.
*/
class CJacobiEllipticFunctions: public CSGObject
{
#ifdef HAVE_ARPREC
typedef mp_real Real;
typedef mp_complex Complex;
#else
typedef float64_t Real;
typedef std::complex<Real> Complex;
#endif //HAVE_ARPREC
private:
static inline Real compute_quarter_period(Real b)
{
#ifdef HAVE_ARPREC
const Real eps=mp_real::_eps;
const Real pi=mp_real::_pi;
#else
const Real eps=std::numeric_limits<Real>::epsilon();
const Real pi=M_PI;
#endif //HAVE_ARPREC
Real a=1.0;
Real mm=1.0;

int64_t p=2;
do
{
Real a_new=(a+b)*0.5;
Real b_new=sqrt(a*b);
Real c=(a-b)*0.5;
mm=Real(p)*c*c;
p<<=1;
a=a_new;
b=b_new;
} while (mm>eps);
return pi*0.5/a;
}

static inline Real poly_six(Real x)
{
return (132*pow(x,6)+42*pow(x,5)+14*pow(x,4)+5*pow(x,3)+2*pow(x,2)+x);
}

public:
/** Computes the quarter periods (K and K') of Jacobian elliptic functions
* (see class description).
* @param L
* @param K the quarter period (to be computed) on the Real axis
* @param Kp the quarter period (to be computed) on the Imaginary axis
* computed
*/
static void ellKKP(Real L, Real &K, Real &Kp);

/** Computes three main Jacobi elliptic functions, \f$sn(u,m)\f$,
* \f$cn(u,m)\f$ and \f$dn(u,m)\f$ (see class description).
* @param u the elliptic integral of the first kind \f$u(k,m)\f$
* @param m the modulus parameter, \f$0\le m \le 1\f$
* @param sn Jacobi elliptic function sn(u,m)
* @param cn Jacobi elliptic function cn(u,m)
* @param dn Jacobi elliptic function dn(u,m)
*/
static void ellPJC(Complex u, Real m, Complex &sn, Complex &cn, Complex &dn);

#ifdef HAVE_ARPREC
/** Wrapper method for ellKKP if ARPREC is present (for high precision)
* @param L
* @param K the quarter period (to be computed) on the Real axis
* @param Kp the quarter period (to be computed) on the Imaginary axis
* computed
*/
static void ellKKP(float64_t L, float64_t &K, float64_t &Kp)
{
mp::mp_init(100, NULL, true);
mp_real _K, _Kp;
ellKKP(mp_real(L), _K, _Kp);
K=dble(_K);
Kp=dble(_Kp);
mp::mp_finalize();
}

/** Wrapper method for ellPJC if ARPREC is present (for high precision)
* @param u the elliptic integral of the first kind \f$u(k,m)\f$
* @param m the modulus parameter, \f$0\le m \le 1\f$
* @param sn Jacobi elliptic function sn(u,m)
* @param cn Jacobi elliptic function cn(u,m)
* @param dn Jacobi elliptic function dn(u,m)
*/
static void ellPJC(std::complex<float64_t> u, float64_t m,
std::complex<float64_t> &sn, std::complex<float64_t> &cn,
std::complex<float64_t> &dn)
{
mp::mp_init(100, NULL, true);
mp_complex _sn, _cn, _dn;
ellPJC(mp_complex(u.real(),u.imag()), mp_real(m), _sn, _cn, _dn);
sn=std::complex<float64_t>(dble(_sn.real),dble(_sn.imag));
cn=std::complex<float64_t>(dble(_cn.real),dble(_cn.imag));
dn=std::complex<float64_t>(dble(_dn.real),dble(_dn.imag));
mp::mp_finalize();
}
#endif //HAVE_ARPREC

/** @return object name */
virtual const char* get_name() const
{
return "JacobiEllipticFunctions";
}
};

}

#endif /* __JACOBI_ELLIPTIC_FUNCTIONS_H_ */

0 comments on commit 8bb3fd5

Please sign in to comment.