From 71df39e4900c80397611f03ba479fd5dbaccc9fd Mon Sep 17 00:00:00 2001
From: axexlck
Date: Sat, 10 Aug 2024 12:06:45 +0200
Subject: [PATCH] WIP #1034 `NSum` for complex numbers
- added *Complexx.java variants
---
symja_android_library/doc/functions/NSum.md | 3 +-
.../core/numerics/series/dp/Aitken.java | 174 +++---
.../core/numerics/series/dp/Cohen.java | 87 ++-
.../core/numerics/series/dp/Direct.java | 28 +-
.../core/numerics/series/dp/Ensemble.java | 470 ++++++++-------
.../numerics/series/dp/IteratedTheta.java | 92 +--
.../core/numerics/series/dp/Richardson.java | 562 +++++++++---------
.../numerics/series/dp/SeriesAlgorithm.java | 88 +++
.../series/dp/complex/AitkenComplex.java | 117 ++++
.../dp/complex/BrezinskiThetaComplex.java | 95 +++
.../series/dp/complex/CohenComplex.java | 68 +++
.../series/dp/complex/DirectComplex.java | 26 +
.../series/dp/complex/EnsembleComplex.java | 279 +++++++++
.../dp/complex/IteratedThetaComplex.java | 65 ++
.../series/dp/complex/LevinComplex.java | 164 +++++
.../series/dp/complex/RichardsonComplex.java | 315 ++++++++++
.../dp/complex/SeriesAlgorithmComplex.java | 256 ++++++++
.../series/dp/complex/WynnEpsilonComplex.java | 288 +++++++++
.../series/dp/complex/WynnRhoComplex.java | 109 ++++
.../core/reflection/system/NSum.java | 150 +++--
.../src/main/resources/doc/functions/NSum.md | 25 +
.../org/matheclipse/core/system/SumTest.java | 12 +-
22 files changed, 2695 insertions(+), 778 deletions(-)
create mode 100644 symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/AitkenComplex.java
create mode 100644 symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/BrezinskiThetaComplex.java
create mode 100644 symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/CohenComplex.java
create mode 100644 symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/DirectComplex.java
create mode 100644 symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/EnsembleComplex.java
create mode 100644 symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/IteratedThetaComplex.java
create mode 100644 symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/LevinComplex.java
create mode 100644 symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/RichardsonComplex.java
create mode 100644 symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/SeriesAlgorithmComplex.java
create mode 100644 symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/WynnEpsilonComplex.java
create mode 100644 symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/WynnRhoComplex.java
create mode 100644 symja_android_library/matheclipse-core/src/main/resources/doc/functions/NSum.md
diff --git a/symja_android_library/doc/functions/NSum.md b/symja_android_library/doc/functions/NSum.md
index b540a5a876..c0223c8d89 100644
--- a/symja_android_library/doc/functions/NSum.md
+++ b/symja_android_library/doc/functions/NSum.md
@@ -13,12 +13,13 @@ NSum(expr, {i, imin, imax, di})
> `i` ranges from `imin` to `imax` in steps `di`
See
+* [Wikipedia - Series (mathematics)](https://en.wikipedia.org/wiki/Series_(mathematics))
* [Wikipedia - Summation](https://en.wikipedia.org/wiki/Summation)
* [Wikipedia - Convergence_tests](https://en.wikipedia.org/wiki/Convergence_tests)
### Examples
```
->> Num(k, {k, 1, 10})
+>> NSum(k, {k, 1, 10})
55
```
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Aitken.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Aitken.java
index 3864399527..ab58d1c93b 100644
--- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Aitken.java
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Aitken.java
@@ -1,106 +1,106 @@
package org.matheclipse.core.numerics.series.dp;
/**
- * Implements a modified adaptive Aitken delta^2 process for estimating infinite
- * series, based on the method in [1].
+ * Implements a modified adaptive Aitken delta^2 process for estimating infinite series, based on
+ * the method in [1].
*
*
* References:
*
- * - [1] Osada, Naoki. Acceleration methods for slowly convergent sequences
- * and their applications. Diss. PhD thesis, Nagoya University, 1993.
+ * - [1] Osada, Naoki. Acceleration methods for slowly convergent sequences and their
+ * applications. Diss. PhD thesis, Nagoya University, 1993.
*
*
*/
public final class Aitken extends SeriesAlgorithm {
- private int status;
- private double xx, dx, dd, xp, alpha;
- private final double[] x;
- private final double[][] s, ds, ts, dts;
+ private int status;
+ private double xx, dx, dd, xp, alpha;
+ private final double[] x;
+ private final double[][] s, ds, ts, dts;
- public Aitken(final double tolerance, final int maxIters, final int patience) {
- super(tolerance, maxIters, patience);
- x = new double[maxIters];
- s = new double[2][maxIters + 1];
- ds = new double[2][maxIters + 1];
- ts = new double[2][maxIters + 1];
- dts = new double[2][maxIters + 1];
- }
+ public Aitken(final double tolerance, final int maxIters, final int patience) {
+ super(tolerance, maxIters, patience);
+ x = new double[maxIters];
+ s = new double[2][maxIters + 1];
+ ds = new double[2][maxIters + 1];
+ ts = new double[2][maxIters + 1];
+ dts = new double[2][maxIters + 1];
+ }
- @Override
- public final double next(final double e, final double term) {
- if (myIndex == 0) {
- xx = dx = dd = 0.0;
- }
- final double xxp = xx;
- x[myIndex] = xx = term;
- if (myIndex == 0) {
- dx = xx;
- } else if (myIndex == 1) {
- final double dxp = dx;
- dx = xx - xxp;
- dd = dx / (dx - dxp);
- } else {
- final double dxp = dx;
- final double ddp = dd;
- dx = xx - xxp;
- dd = dx / (dx - dxp);
- alpha = 1.0 / (dd - ddp) + 1.0;
- alpha = aitken(alpha, ts, dts, myIndex - 1, -2.0);
- }
- xp = xx;
- if (myIndex >= 2) {
- for (int m = 1; m <= myIndex + 1; ++m) {
- xp = aitken(x[m - 1], s, ds, m, alpha);
- }
- }
- ++myIndex;
- if (status == 1) {
- return Double.NaN;
- } else {
- return xp;
- }
+ @Override
+ public final double next(final double e, final double term) {
+ if (myIndex == 0) {
+ xx = dx = dd = 0.0;
}
-
- private final double aitken(final double xx, final double[][] s, final double[][] ds, final int n,
- final double theta) {
- status = 0;
- if (n != 1) {
- final int kend = (n - 1) >> 1;
- System.arraycopy(s[1], 0, s[0], 0, kend + 1);
- if ((n & 1) == 0) {
- System.arraycopy(ds[1], 0, ds[0], 0, kend);
- } else {
- System.arraycopy(ds[1], 0, ds[0], 0, kend + 1);
- }
- }
- s[1][0] = xx;
- if (n == 1) {
- ds[1][0] = xx;
- return xx;
- }
- ds[1][0] = xx - s[0][0];
- for (int k = 1; k <= (n >> 1); ++k) {
- final double w1 = ds[0][k - 1] * ds[1][k - 1];
- final double w2 = ds[1][k - 1] - ds[0][k - 1];
- if (Math.abs(w2) < TINY) {
- status = 1;
- return xx;
- }
- final int twok = k << 1;
- final double coef = ((twok - 1) - theta) / (twok - 2 - theta);
- s[1][k] = s[0][k - 1] - coef * w1 / w2;
- if (n != twok - 1) {
- ds[1][k] = s[1][k] - s[0][k];
- }
- }
- final int kopt = n >> 1;
- return s[1][kopt];
+ final double xxp = xx;
+ x[myIndex] = xx = term;
+ if (myIndex == 0) {
+ dx = xx;
+ } else if (myIndex == 1) {
+ final double dxp = dx;
+ dx = xx - xxp;
+ dd = dx / (dx - dxp);
+ } else {
+ final double dxp = dx;
+ final double ddp = dd;
+ dx = xx - xxp;
+ dd = dx / (dx - dxp);
+ alpha = 1.0 / (dd - ddp) + 1.0;
+ alpha = aitken(alpha, ts, dts, myIndex - 1, -2.0);
+ }
+ xp = xx;
+ if (myIndex >= 2) {
+ for (int m = 1; m <= myIndex + 1; ++m) {
+ xp = aitken(x[m - 1], s, ds, m, alpha);
+ }
+ }
+ ++myIndex;
+ if (status == 1) {
+ return Double.NaN;
+ } else {
+ return xp;
}
+ }
- @Override
- public final String getName() {
- return "Modified Aitken";
+ private final double aitken(final double xx, final double[][] s, final double[][] ds, final int n,
+ final double theta) {
+ status = 0;
+ if (n != 1) {
+ final int kend = (n - 1) >> 1;
+ System.arraycopy(s[1], 0, s[0], 0, kend + 1);
+ if ((n & 1) == 0) {
+ System.arraycopy(ds[1], 0, ds[0], 0, kend);
+ } else {
+ System.arraycopy(ds[1], 0, ds[0], 0, kend + 1);
+ }
}
+ s[1][0] = xx;
+ if (n == 1) {
+ ds[1][0] = xx;
+ return xx;
+ }
+ ds[1][0] = xx - s[0][0];
+ for (int k = 1; k <= (n >> 1); ++k) {
+ final double w1 = ds[0][k - 1] * ds[1][k - 1];
+ final double w2 = ds[1][k - 1] - ds[0][k - 1];
+ if (Math.abs(w2) < TINY) {
+ status = 1;
+ return xx;
+ }
+ final int twok = k << 1;
+ final double coef = ((twok - 1) - theta) / (twok - 2 - theta);
+ s[1][k] = s[0][k - 1] - coef * w1 / w2;
+ if (n != twok - 1) {
+ ds[1][k] = s[1][k] - s[0][k];
+ }
+ }
+ final int kopt = n >> 1;
+ return s[1][kopt];
+ }
+
+ @Override
+ public final String getName() {
+ return "Modified Aitken";
+ }
}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Cohen.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Cohen.java
index 0ba1c1aca7..de2573ce30 100644
--- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Cohen.java
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Cohen.java
@@ -4,65 +4,64 @@
import org.matheclipse.core.numerics.utils.SimpleMath;
/**
- * This is an implementation of Algorithm 1 in [1] for evaluating infinite
- * series whose terms have alternating signs.
+ * This is an implementation of Algorithm 1 in [1] for evaluating infinite series whose terms have
+ * alternating signs.
*
*
* References:
*
- * - [1] Cohen, Henri, Fernando Rodriguez Villegas, and Don Zagier.
- * "Convergence acceleration of alternating series." Experimental mathematics
- * 9.1 (2000): 3-12.
+ * - [1] Cohen, Henri, Fernando Rodriguez Villegas, and Don Zagier. "Convergence acceleration of
+ * alternating series." Experimental mathematics 9.1 (2000): 3-12.
*
*
*/
public final class Cohen extends SeriesAlgorithm {
- private final double[] myTab;
- private double mySign0;
+ private final double[] myTab;
+ private double mySign0;
- public Cohen(final double tolerance, final int maxIters, final int patience) {
- super(tolerance, maxIters, patience);
- myTab = new double[maxIters];
- }
+ public Cohen(final double tolerance, final int maxIters, final int patience) {
+ super(tolerance, maxIters, patience);
+ myTab = new double[maxIters];
+ }
- @Override
- public final double next(final double e, final double term) {
+ @Override
+ public final double next(final double e, final double term) {
- // add next element
- final int n = myIndex + 1;
- myTab[myIndex] = Math.abs(e);
+ // add next element
+ final int n = myIndex + 1;
+ myTab[myIndex] = Math.abs(e);
- // record the sign of the first term, since this method
- // requires the first term of the sequence to be positive
- if (myIndex == 0) {
- mySign0 = Math.signum(e);
- if (mySign0 == 0.0) {
- mySign0 = 1.0;
- }
- }
+ // record the sign of the first term, since this method
+ // requires the first term of the sequence to be positive
+ if (myIndex == 0) {
+ mySign0 = Math.signum(e);
+ if (mySign0 == 0.0) {
+ mySign0 = 1.0;
+ }
+ }
- // initialize d value
- double d = SimpleMath.pow(3.0 + 2.0 * Constants.SQRT2, n);
- d = 0.5 * (d + 1.0 / d);
+ // initialize d value
+ double d = SimpleMath.pow(3.0 + 2.0 * Constants.SQRT2, n);
+ d = 0.5 * (d + 1.0 / d);
- // apply the Chebychef polynomial recursively (Algorithm 1)
- double b = -1.0;
- double c = -d;
- double s = 0.0;
- for (int k = 0; k < n; ++k) {
- c = b - c;
- s += c * myTab[k];
- final double numer = (k + n) * (k - n);
- final double denom = (k + 0.5) * (k + 1);
- b *= numer / denom;
- }
- ++myIndex;
- return mySign0 * s / d;
+ // apply the Chebychef polynomial recursively (Algorithm 1)
+ double b = -1.0;
+ double c = -d;
+ double s = 0.0;
+ for (int k = 0; k < n; ++k) {
+ c = b - c;
+ s += c * myTab[k];
+ final double numer = (k + n) * (k - n);
+ final double denom = (k + 0.5) * (k + 1);
+ b *= numer / denom;
}
+ ++myIndex;
+ return mySign0 * s / d;
+ }
- @Override
- public final String getName() {
- return "Cohen";
- }
+ @Override
+ public final String getName() {
+ return "Cohen";
+ }
}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Direct.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Direct.java
index 41543bd671..0415851cfc 100644
--- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Direct.java
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Direct.java
@@ -1,23 +1,23 @@
package org.matheclipse.core.numerics.series.dp;
/**
- * Naively computes the limit of a sequence or series by inspecting elements one
- * a time. No transform of the original sequence is performed.
+ * Naively computes the limit of a sequence or series by inspecting elements one a time. No
+ * transform of the original sequence is performed.
*/
public final class Direct extends SeriesAlgorithm {
- public Direct(final double tolerance, final int maxIters, final int patience) {
- super(tolerance, maxIters, patience);
- }
+ public Direct(final double tolerance, final int maxIters, final int patience) {
+ super(tolerance, maxIters, patience);
+ }
- @Override
- public final double next(final double e, final double term) {
- ++myIndex;
- return term;
- }
+ @Override
+ public final double next(final double e, final double term) {
+ ++myIndex;
+ return term;
+ }
- @Override
- public final String getName() {
- return "Direct Sum";
- }
+ @Override
+ public final String getName() {
+ return "Direct Sum";
+ }
}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Ensemble.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Ensemble.java
index ba0faf47aa..e546c18408 100644
--- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Ensemble.java
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Ensemble.java
@@ -3,278 +3,276 @@
import java.util.ArrayList;
import java.util.List;
import org.matheclipse.core.numerics.series.dp.Levin.RemainderSequence;
+import org.matheclipse.core.numerics.series.dp.SeriesAlgorithm.SeriesSolution;
import org.matheclipse.core.numerics.series.dp.WynnEpsilon.ShanksMethod;
/**
- * An ensemble algorithm for evaluating the limits of sequences and series of
- * real values. This algorithms works by initializing a number of convergence
- * acceleration algorithms with different properties, and running them in
- * parallel. The estimates of the sequence's or corresponding series limit are
- * extracted from the instance that has converged first to a stable limit to the
- * desired tolerance.
+ * An ensemble algorithm for evaluating the limits of sequences and series of real values. This
+ * algorithms works by initializing a number of convergence acceleration algorithms with different
+ * properties, and running them in parallel. The estimates of the sequence's or corresponding series
+ * limit are extracted from the instance that has converged first to a stable limit to the desired
+ * tolerance.
*/
public final class Ensemble extends SeriesAlgorithm {
- private static final int PRINT_DIGITS = 18;
+ private static final int PRINT_DIGITS = 18;
- private final int myPrint;
- private final List myMethods;
- private final List myForAlternating;
- private final List myForSequence;
+ private final int myPrint;
+ private final List myMethods;
+ private final List myForAlternating;
+ private final List myForSequence;
- private int myMethodCount, myBestMethod;
- private boolean myIsAlternating, myCheckForSequenceOnly;
- private double myOldSignum;
- private boolean[] myExcept;
- private double[] myPrevEst, myEst;
+ private int myMethodCount, myBestMethod;
+ private boolean myIsAlternating, myCheckForSequenceOnly;
+ private double myOldSignum;
+ private boolean[] myExcept;
+ private double[] myPrevEst, myEst;
- /**
- * Creates a new instance of an ensemble convergence acceleration algorithm.
- *
- * @param tolerance the smallest acceptable change in series evaluation in
- * consecutive iterations that indicates the algorithm has
- * converged
- * @param maxIters the maximum number of sequence terms to evaluate before
- * giving up
- * @param patience the number of iterations used to assess whether or not a
- * particular instance of convergence acceleration
- * algorithm used by this algorithm has converged
- * @param printProgress how often to print the progress of the current
- * algorithm, e.g. the estimate of a sequence's or series's
- * limit at each iteration according to each algorithm
- * instance
- */
- public Ensemble(final double tolerance, final int maxIters, final int patience, final int printProgress) {
- super(tolerance, maxIters, patience);
- myPrint = printProgress;
- myMethods = new ArrayList<>();
- myForAlternating = new ArrayList<>();
- myForSequence = new ArrayList<>();
+ /**
+ * Creates a new instance of an ensemble convergence acceleration algorithm.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of iterations used to assess whether or not a particular instance of
+ * convergence acceleration algorithm used by this algorithm has converged
+ * @param printProgress how often to print the progress of the current algorithm, e.g. the
+ * estimate of a sequence's or series's limit at each iteration according to each algorithm
+ * instance
+ */
+ public Ensemble(final double tolerance, final int maxIters, final int patience,
+ final int printProgress) {
+ super(tolerance, maxIters, patience);
+ myPrint = printProgress;
+ myMethods = new ArrayList<>();
+ myForAlternating = new ArrayList<>();
+ myForSequence = new ArrayList<>();
- // only for alternating series
- myMethods.add(new Cohen(myTol, myMaxIters, 1));
- myForAlternating.add(true);
- myForSequence.add(false);
- myMethods.add(new Levin(myTol, myMaxIters, 1, RemainderSequence.D));
- myForAlternating.add(true);
- myForSequence.add(false);
+ // only for alternating series
+ myMethods.add(new Cohen(myTol, myMaxIters, 1));
+ myForAlternating.add(true);
+ myForSequence.add(false);
+ myMethods.add(new Levin(myTol, myMaxIters, 1, RemainderSequence.D));
+ myForAlternating.add(true);
+ myForSequence.add(false);
- // for alternating and linear series
- myMethods.add(new Aitken(myTol, myMaxIters, 1));
- myForAlternating.add(false);
- myForSequence.add(true);
- myMethods.add(new WynnEpsilon(myTol, myMaxIters, 1, ShanksMethod.WYNN));
- myForAlternating.add(false);
- myForSequence.add(true);
- myMethods.add(new Levin(myTol, myMaxIters, 1, RemainderSequence.T));
- myForAlternating.add(false);
- myForSequence.add(false);
+ // for alternating and linear series
+ myMethods.add(new Aitken(myTol, myMaxIters, 1));
+ myForAlternating.add(false);
+ myForSequence.add(true);
+ myMethods.add(new WynnEpsilon(myTol, myMaxIters, 1, ShanksMethod.WYNN));
+ myForAlternating.add(false);
+ myForSequence.add(true);
+ myMethods.add(new Levin(myTol, myMaxIters, 1, RemainderSequence.T));
+ myForAlternating.add(false);
+ myForSequence.add(false);
- // for logarithmic series
- myMethods.add(new Levin(myTol, myMaxIters, 1, RemainderSequence.U));
- myForAlternating.add(false);
- myForSequence.add(false);
- myMethods.add(new Levin(myTol, myMaxIters, 1, RemainderSequence.V));
- myForAlternating.add(false);
- myForSequence.add(false);
- myMethods.add(new Richardson(myTol, myMaxIters, 1));
- myForAlternating.add(false);
- myForSequence.add(false);
- myMethods.add(new BrezinskiTheta(myTol, myMaxIters, 1));
- myForAlternating.add(false);
- myForSequence.add(true);
- myMethods.add(new IteratedTheta(myTol, myMaxIters, 1));
- myForAlternating.add(false);
- myForSequence.add(true);
- myMethods.add(new WynnRho(myTol, myMaxIters, 1));
- myForAlternating.add(false);
- myForSequence.add(true);
- myMethods.add(new WynnEpsilon(myTol, myMaxIters, 1, ShanksMethod.ALTERNATING));
- myForAlternating.add(false);
- myForSequence.add(true);
- }
-
- public Ensemble(final double tolerance, final int maxIters, final int patience) {
- this(tolerance, maxIters, patience, 0);
- }
+ // for logarithmic series
+ myMethods.add(new Levin(myTol, myMaxIters, 1, RemainderSequence.U));
+ myForAlternating.add(false);
+ myForSequence.add(false);
+ myMethods.add(new Levin(myTol, myMaxIters, 1, RemainderSequence.V));
+ myForAlternating.add(false);
+ myForSequence.add(false);
+ myMethods.add(new Richardson(myTol, myMaxIters, 1));
+ myForAlternating.add(false);
+ myForSequence.add(false);
+ myMethods.add(new BrezinskiTheta(myTol, myMaxIters, 1));
+ myForAlternating.add(false);
+ myForSequence.add(true);
+ myMethods.add(new IteratedTheta(myTol, myMaxIters, 1));
+ myForAlternating.add(false);
+ myForSequence.add(true);
+ myMethods.add(new WynnRho(myTol, myMaxIters, 1));
+ myForAlternating.add(false);
+ myForSequence.add(true);
+ myMethods.add(new WynnEpsilon(myTol, myMaxIters, 1, ShanksMethod.ALTERNATING));
+ myForAlternating.add(false);
+ myForSequence.add(true);
+ }
- @Override
- public final double next(final double e, final double term) {
+ public Ensemble(final double tolerance, final int maxIters, final int patience) {
+ this(tolerance, maxIters, patience, 0);
+ }
- // ********************************************************************
- // INITIALIZATION
- // ********************************************************************
- if (myIndex == 0) {
+ @Override
+ public final double next(final double e, final double term) {
- // initialize variables
- myIsAlternating = true;
- myMethodCount = myMethods.size();
- myOldSignum = Math.signum(e);
- myPrevEst = new double[myMethodCount];
- myEst = new double[myMethodCount];
- myExcept = new boolean[myMethodCount];
+ // ********************************************************************
+ // INITIALIZATION
+ // ********************************************************************
+ if (myIndex == 0) {
- // disable methods for series only
- if (myCheckForSequenceOnly) {
- for (int m = 0; m < myMethodCount; ++m) {
- if (!myForSequence.get(m)) {
- myExcept[m] = true;
- if (myPrint > 0) {
- System.out.println("Disabling method " + myMethods.get(m).getName()
- + " that is invalid for sequences");
- }
- }
- }
- myCheckForSequenceOnly = false;
- }
+ // initialize variables
+ myIsAlternating = true;
+ myMethodCount = myMethods.size();
+ myOldSignum = Math.signum(e);
+ myPrevEst = new double[myMethodCount];
+ myEst = new double[myMethodCount];
+ myExcept = new boolean[myMethodCount];
- // initialize each method
- for (int m = 0; m < myMethodCount; ++m) {
- if (!myExcept[m]) {
- final SeriesAlgorithm method = myMethods.get(m);
- method.myIndex = 0;
- myEst[m] = method.next(e, term);
- }
- }
+ // disable methods for series only
+ if (myCheckForSequenceOnly) {
+ for (int m = 0; m < myMethodCount; ++m) {
+ if (!myForSequence.get(m)) {
+ myExcept[m] = true;
+ if (myPrint > 0) {
+ System.out.println("Disabling method " + myMethods.get(m).getName()
+ + " that is invalid for sequences");
+ }
+ }
+ }
+ myCheckForSequenceOnly = false;
+ }
- // print header
- if (myPrint > 0) {
- String line = "";
- line += pad("Index", PRINT_DIGITS + 5);
- for (final SeriesAlgorithm method : myMethods) {
- line += "\t" + pad(method.getName(), PRINT_DIGITS + 5);
- }
- System.out.println(line);
- }
+ // initialize each method
+ for (int m = 0; m < myMethodCount; ++m) {
+ if (!myExcept[m]) {
+ final SeriesAlgorithm method = myMethods.get(m);
+ method.myIndex = 0;
+ myEst[m] = method.next(e, term);
+ }
+ }
- // print values
- if (myPrint > 0) {
- printRow();
- }
+ // print header
+ if (myPrint > 0) {
+ String line = "";
+ line += pad("Index", PRINT_DIGITS + 5);
+ for (final SeriesAlgorithm method : myMethods) {
+ line += "\t" + pad(method.getName(), PRINT_DIGITS + 5);
+ }
+ System.out.println(line);
+ }
- ++myIndex;
- return term;
- }
+ // print values
+ if (myPrint > 0) {
+ printRow();
+ }
- // ********************************************************************
- // SEQUENCE INSPECTION
- // ********************************************************************
- // test for invalid value of series or sequence
- if (!Double.isFinite(term) || !Double.isFinite(e)) {
- if (myPrint > 0) {
- System.out.println("Aborting due to NaN at iteration " + myIndex);
- }
- ++myIndex;
- return Double.NaN;
- }
+ ++myIndex;
+ return term;
+ }
- // track the sign of the sequence
- final double signum = Math.signum(e);
- if (myIndex > 0 && myIsAlternating && signum * myOldSignum >= 0) {
- myIsAlternating = false;
- }
- myOldSignum = signum;
+ // ********************************************************************
+ // SEQUENCE INSPECTION
+ // ********************************************************************
+ // test for invalid value of series or sequence
+ if (!Double.isFinite(term) || !Double.isFinite(e)) {
+ if (myPrint > 0) {
+ System.out.println("Aborting due to NaN at iteration " + myIndex);
+ }
+ ++myIndex;
+ return Double.NaN;
+ }
- // exclude alternating series methods if the series does not alternate
- if (!myIsAlternating) {
- for (int m = 0; m < myMethodCount; ++m) {
- if (!myExcept[m] && myForAlternating.get(m)) {
- myExcept[m] = true;
- if (myPrint > 0) {
- System.out.println("Disabling method " + myMethods.get(m).getName()
- + " because series is not alternating at iteration " + myIndex);
- }
- }
- }
- }
+ // track the sign of the sequence
+ final double signum = Math.signum(e);
+ if (myIndex > 0 && myIsAlternating && signum * myOldSignum >= 0) {
+ myIsAlternating = false;
+ }
+ myOldSignum = signum;
- // ********************************************************************
- // MAIN UPDATE
- // ********************************************************************
- myBestMethod = -1;
- double bestError = Double.POSITIVE_INFINITY;
- for (int m = 0; m < myMethodCount; ++m) {
- if (myExcept[m]) {
- continue;
- }
+ // exclude alternating series methods if the series does not alternate
+ if (!myIsAlternating) {
+ for (int m = 0; m < myMethodCount; ++m) {
+ if (!myExcept[m] && myForAlternating.get(m)) {
+ myExcept[m] = true;
+ if (myPrint > 0) {
+ System.out.println("Disabling method " + myMethods.get(m).getName()
+ + " because series is not alternating at iteration " + myIndex);
+ }
+ }
+ }
+ }
- // estimate the next terms
- final SeriesAlgorithm method = myMethods.get(m);
- myPrevEst[m] = myEst[m];
- myEst[m] = method.next(e, term);
+ // ********************************************************************
+ // MAIN UPDATE
+ // ********************************************************************
+ myBestMethod = -1;
+ double bestError = Double.POSITIVE_INFINITY;
+ for (int m = 0; m < myMethodCount; ++m) {
+ if (myExcept[m]) {
+ continue;
+ }
- // if a method produces an invalid estimate it is excluded
- if (!Double.isFinite(myEst[m])) {
- if (myPrint > 0) {
- System.out.println(
- "Disabling method " + method.getName() + " due to instability at iteration " + myIndex);
- }
- myExcept[m] = true;
- continue;
- }
+ // estimate the next terms
+ final SeriesAlgorithm method = myMethods.get(m);
+ myPrevEst[m] = myEst[m];
+ myEst[m] = method.next(e, term);
- // estimate error and best method
- final double error = Math.abs(myPrevEst[m] - myEst[m]);
- if (error < bestError && myEst[m] != HUGE) {
- myBestMethod = m;
- bestError = error;
- }
- }
+ // if a method produces an invalid estimate it is excluded
+ if (!Double.isFinite(myEst[m])) {
+ if (myPrint > 0) {
+ System.out.println("Disabling method " + method.getName()
+ + " due to instability at iteration " + myIndex);
+ }
+ myExcept[m] = true;
+ continue;
+ }
- // ********************************************************************
- // FINALIZE THIS ITERATION
- // ********************************************************************
- if (myPrint > 0 && myIndex % myPrint == 0) {
- printRow();
- }
+ // estimate error and best method
+ final double error = Math.abs(myPrevEst[m] - myEst[m]);
+ if (error < bestError && myEst[m] != HUGE) {
+ myBestMethod = m;
+ bestError = error;
+ }
+ }
- // return the method with the best convergence so far
- ++myIndex;
- if (myBestMethod >= 0) {
- return myEst[myBestMethod];
- } else {
- return Double.NaN;
- }
+ // ********************************************************************
+ // FINALIZE THIS ITERATION
+ // ********************************************************************
+ if (myPrint > 0 && myIndex % myPrint == 0) {
+ printRow();
}
- @Override
- public final SeriesSolution limit(final Iterable seq, final boolean series, final int extrapolateStart) {
- myCheckForSequenceOnly = !series;
- final SeriesSolution result = super.limit(seq, series, extrapolateStart);
- if (myPrint > 0 && myBestMethod >= 0 && result.converged) {
- System.out.println(
- "Converged at iteration " + myIndex + " with method " + myMethods.get(myBestMethod).getName());
- }
- return result;
+ // return the method with the best convergence so far
+ ++myIndex;
+ if (myBestMethod >= 0) {
+ return myEst[myBestMethod];
+ } else {
+ return Double.NaN;
}
+ }
- @Override
- public final String getName() {
- return "Ensemble";
+ @Override
+ public final SeriesSolution limit(final Iterable seq, final boolean series,
+ final int extrapolateStart) {
+ myCheckForSequenceOnly = !series;
+ final SeriesSolution result = super.limit(seq, series, extrapolateStart);
+ if (myPrint > 0 && myBestMethod >= 0 && result.converged) {
+ System.out.println("Converged at iteration " + myIndex + " with method "
+ + myMethods.get(myBestMethod).getName());
}
+ return result;
+ }
- private final void printRow() {
- String line = pad(myIndex + "", PRINT_DIGITS);
- for (int m = 0; m < myMethodCount; ++m) {
- final String str;
- if (myExcept[m]) {
- str = "-";
- } else {
- str = Double.toString(myEst[m]);
- }
- line += "\t" + pad(str, PRINT_DIGITS);
- }
- System.out.println(line);
+ @Override
+ public final String getName() {
+ return "Ensemble";
+ }
+
+ private final void printRow() {
+ String line = pad(myIndex + "", PRINT_DIGITS);
+ for (int m = 0; m < myMethodCount; ++m) {
+ final String str;
+ if (myExcept[m]) {
+ str = "-";
+ } else {
+ str = Double.toString(myEst[m]);
+ }
+ line += "\t" + pad(str, PRINT_DIGITS);
}
+ System.out.println(line);
+ }
- private static final String pad(final String str, final int len) {
- if (str.length() >= len) {
- return str;
- }
- String result = str;
- for (int i = str.length() + 1; i <= len; ++i) {
- result += " ";
- }
- return result;
+ private static final String pad(final String str, final int len) {
+ if (str.length() >= len) {
+ return str;
+ }
+ String result = str;
+ for (int i = str.length() + 1; i <= len; ++i) {
+ result += " ";
}
+ return result;
+ }
}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/IteratedTheta.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/IteratedTheta.java
index b1ca4f4f60..fb31c77670 100644
--- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/IteratedTheta.java
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/IteratedTheta.java
@@ -1,65 +1,67 @@
package org.matheclipse.core.numerics.series.dp;
+import org.hipparchus.complex.Complex;
+
/**
- * Implements the Iterated Theta algorithm for convergence of infinite series as
- * described in [1].
+ * Implements the Iterated Theta algorithm for convergence of infinite series as described in [1].
*
*
* References:
*
- * - [1] Weniger, Ernst Joachim. "Nonlinear sequence transformations for the
- * acceleration of convergence and the summation of divergent series." arXiv
- * preprint math/0306302 (2003).
+ * - [1] Weniger, Ernst Joachim. "Nonlinear sequence transformations for the acceleration of
+ * convergence and the summation of divergent series." arXiv preprint math/0306302 (2003).
*
*
*/
public final class IteratedTheta extends SeriesAlgorithm {
- private final double[] myTab;
-
- public IteratedTheta(final double tolerance, final int maxIters, final int patience) {
- super(tolerance, maxIters, patience);
- myTab = new double[maxIters];
- }
+ private final double[] myTab;
+ private final Complex[] myComplexTab;
- @Override
- public final double next(final double e, final double term) {
+ public IteratedTheta(final double tolerance, final int maxIters, final int patience) {
+ super(tolerance, maxIters, patience);
+ myTab = new double[maxIters];
+ myComplexTab = new Complex[maxIters];
+ }
- // initial value for J
- myTab[myIndex] = term;
- if (myIndex < 3) {
- ++myIndex;
- return term;
- }
+ @Override
+ public final double next(final double e, final double term) {
- // higher-order J array
- final int lmax = myIndex / 3;
- int m = myIndex;
- for (int l = 1; l <= lmax; ++l) {
- m -= 3;
- final double diff0 = myTab[m + 1] - myTab[m];
- final double diff1 = myTab[m + 2] - myTab[m + 1];
- final double diff2 = myTab[m + 3] - myTab[m + 2];
- final double dfsq1 = diff1 - diff0;
- final double dfsq2 = diff2 - diff1;
- final double denom = diff2 * dfsq1 - diff0 * dfsq2;
- final double ratio = dfsq2 / denom;
+ // initial value for J
+ myTab[myIndex] = term;
+ if (myIndex < 3) {
+ ++myIndex;
+ return term;
+ }
- // safeguarded against underflow
- if (Math.abs(denom) < TINY) {
- myTab[m] = HUGE;
- } else {
- myTab[m] = myTab[m + 1] - diff0 * diff1 * ratio;
- }
- }
- ++myIndex;
+ // higher-order J array
+ final int lmax = myIndex / 3;
+ int m = myIndex;
+ for (int l = 1; l <= lmax; ++l) {
+ m -= 3;
+ final double diff0 = myTab[m + 1] - myTab[m];
+ final double diff1 = myTab[m + 2] - myTab[m + 1];
+ final double diff2 = myTab[m + 3] - myTab[m + 2];
+ final double dfsq1 = diff1 - diff0;
+ final double dfsq2 = diff2 - diff1;
+ final double denom = diff2 * dfsq1 - diff0 * dfsq2;
+ final double ratio = dfsq2 / denom;
- // return result
- return myTab[myIndex % 3];
+ // safeguarded against underflow
+ if (Math.abs(denom) < TINY) {
+ myTab[m] = HUGE;
+ } else {
+ myTab[m] = myTab[m + 1] - diff0 * diff1 * ratio;
+ }
}
+ ++myIndex;
- @Override
- public final String getName() {
- return "Iterated Theta";
- }
+ // return result
+ return myTab[myIndex % 3];
+ }
+
+ @Override
+ public final String getName() {
+ return "Iterated Theta";
+ }
}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Richardson.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Richardson.java
index e76ce8c3b5..d77b9de512 100644
--- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Richardson.java
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/Richardson.java
@@ -3,323 +3,309 @@
import java.util.Iterator;
/**
- * Implements a generalized form of the Richardson extrapolation for evaluating
- * infinite series. This is implemented using a d-transform of the original
- * series and is called the W(m) algorithm [1].
+ * Implements a generalized form of the Richardson extrapolation for evaluating infinite series.
+ * This is implemented using a d-transform of the original series and is called the W(m) algorithm
+ * [1].
*
*
- * The method in [1] defines an auxiliary increasing sequence of integers, R(l),
- * defined by the recursion R(l + 1) = (int)(sigma * R(l)) + s, where sigma and
- * s are two tuning parameters. The series are evaluated at each R(l) of the
- * auxiliary sequence and forms a subsequence of the original partial sums that
- * converges to the same limit. The d-transform is then applied to accelerate
- * this sequence to infinity.
+ * The method in [1] defines an auxiliary increasing sequence of integers, R(l), defined by the
+ * recursion R(l + 1) = (int)(sigma * R(l)) + s, where sigma and s are two tuning parameters. The
+ * series are evaluated at each R(l) of the auxiliary sequence and forms a subsequence of the
+ * original partial sums that converges to the same limit. The d-transform is then applied to
+ * accelerate this sequence to infinity.
*
*
*
* References:
*
- * - [1] Ford, William & Sidi, Avram. (1987). An Algorithm for a
- * Generalization of the Richardson Extrapolation Process. SIAM Journal on
- * Numerical Analysis. 24. 10.1137/0724080.
+ * - [1] Ford, William & Sidi, Avram. (1987). An Algorithm for a Generalization of the Richardson
+ * Extrapolation Process. SIAM Journal on Numerical Analysis. 24. 10.1137/0724080.
*
*
*/
public final class Richardson extends SeriesAlgorithm {
- private final double mySigma;
- private final int myIncr;
-
- private int myPrevIndex;
- private final double[] myElements;
-
- private final int myM;
- private final double[] myG;
- private final double[][][] myPsiAI, myPsiQ, myPsiG;
- private int evals;
-
- /**
- * Creates an instance of the d-transform for a given auxiliary sequence.
- *
- * @param tolerance the smallest acceptable change in series evaluation in
- * consecutive iterations that indicates the algorithm has
- * converged
- * @param maxIters the maximum number of sequence terms to evaluate before
- * giving up
- * @param patience the number of consecutive iterations required for the
- * tolerance criterion to be satisfied to stop the algorithm
- * @param m the parameter in the paper; must be >= 1
- * @param sigma the parameter sigma defining the auxiliary sequence R(l);
- * must be > 1
- * @param s the parameter s defining the auxiliary sequence R(l); must
- * be >= 1
- */
- public Richardson(final double tolerance, final int maxIters, final int patience, final int m, final double sigma,
- final int s) {
- super(tolerance, maxIters, patience);
- myM = m;
- mySigma = sigma;
- myIncr = s;
-
- myElements = new double[maxIters + 1];
- myG = new double[myM];
- myPsiAI = new double[maxIters + 1][2][2];
- myPsiQ = new double[maxIters + 1][myM][2];
- myPsiG = new double[maxIters + 1][myM][2];
+ private final double mySigma;
+ private final int myIncr;
+
+ private int myPrevIndex;
+ private final double[] myElements;
+
+ private final int myM;
+ private final double[] myG;
+ private final double[][][] myPsiAI, myPsiQ, myPsiG;
+ private int evals;
+
+ /**
+ * Creates an instance of the d-transform for a given auxiliary sequence.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param m the parameter in the paper; must be >= 1
+ * @param sigma the parameter sigma defining the auxiliary sequence R(l); must be > 1
+ * @param s the parameter s defining the auxiliary sequence R(l); must be >= 1
+ */
+ public Richardson(final double tolerance, final int maxIters, final int patience, final int m,
+ final double sigma, final int s) {
+ super(tolerance, maxIters, patience);
+ myM = m;
+ mySigma = sigma;
+ myIncr = s;
+
+ myElements = new double[maxIters + 1];
+ myG = new double[myM];
+ myPsiAI = new double[maxIters + 1][2][2];
+ myPsiQ = new double[maxIters + 1][myM][2];
+ myPsiG = new double[maxIters + 1][myM][2];
+ }
+
+ /**
+ * Creates an instance of the d-transform for a given auxiliary sequence with sigma = 1. The case
+ * s = 1 is most suitable for alternating series or quickly-converging series, while s > 1 is
+ * suitable for power series, trigonometric series, and other series representing analytic
+ * functions, for arguments close to the functions' singular values. [1] notes that s = 1 is OK
+ * for arguments far away from the functions' singular values.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param m the parameter in the paper; must be >= 1
+ * @param s the parameter s defining the auxiliary sequence R(l); must be >= 1
+ */
+ public Richardson(final double tolerance, final int maxIters, final int patience, final int m,
+ final int s) {
+ this(tolerance, maxIters, patience, m, 1.0, s);
+ }
+
+ /**
+ * Creates an instance of the d-transform for a given auxiliary sequence with s = 1. Most suitable
+ * for monotonic series that converge slowly.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param m the parameter in the paper; must be >= 1
+ * @param sigma the parameter sigma defining the auxiliary sequence R(l); must be > 1
+ */
+ public Richardson(final double tolerance, final int maxIters, final int patience, final int m,
+ final double sigma) {
+ this(tolerance, maxIters, patience, m, sigma, 1);
+ }
+
+ /**
+ * Creates an instance of the d-transform for the simplest auxiliary sequence R(l) = l. Most
+ * suitable for alternating series.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param m the parameter in the paper; must be >= 1
+ */
+ public Richardson(final double tolerance, final int maxIters, final int patience, final int m) {
+ this(tolerance, maxIters, patience, m, 1.0, 1);
+ }
+
+ /**
+ * Creates an instance of the d-transform for the simplest auxiliary sequence R(l) = l. Most
+ * suitable for alternating series.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ */
+ public Richardson(final double tolerance, final int maxIters, final int patience) {
+ this(tolerance, maxIters, patience, 2);
+ }
+
+ @Override
+ public final double next(final double e, final double term) {
+
+ // first m elements do not induce an update of g or psi
+ myElements[myIndex] = e;
+ ++myIndex;
+ if (myIndex < myM) {
+ return term;
}
- /**
- * Creates an instance of the d-transform for a given auxiliary sequence with
- * sigma = 1. The case s = 1 is most suitable for alternating series or
- * quickly-converging series, while s > 1 is suitable for power series,
- * trigonometric series, and other series representing analytic functions, for
- * arguments close to the functions' singular values. [1] notes that s = 1 is OK
- * for arguments far away from the functions' singular values.
- *
- * @param tolerance the smallest acceptable change in series evaluation in
- * consecutive iterations that indicates the algorithm has
- * converged
- * @param maxIters the maximum number of sequence terms to evaluate before
- * giving up
- * @param patience the number of consecutive iterations required for the
- * tolerance criterion to be satisfied to stop the algorithm
- * @param m the parameter in the paper; must be >= 1
- * @param s the parameter s defining the auxiliary sequence R(l); must
- * be >= 1
- */
- public Richardson(final double tolerance, final int maxIters, final int patience, final int m, final int s) {
- this(tolerance, maxIters, patience, m, 1.0, s);
+ // main updates for psi: in the iterative mode we can only use R(l) = l
+ final int l = myIndex - myM;
+ final int Rl = l;
+ double al = 0.0;
+ for (int i = 0; i <= Rl; ++i) {
+ al += myElements[i];
}
-
- /**
- * Creates an instance of the d-transform for a given auxiliary sequence with s
- * = 1. Most suitable for monotonic series that converge slowly.
- *
- * @param tolerance the smallest acceptable change in series evaluation in
- * consecutive iterations that indicates the algorithm has
- * converged
- * @param maxIters the maximum number of sequence terms to evaluate before
- * giving up
- * @param patience the number of consecutive iterations required for the
- * tolerance criterion to be satisfied to stop the algorithm
- * @param m the parameter in the paper; must be >= 1
- * @param sigma the parameter sigma defining the auxiliary sequence R(l);
- * must be > 1
- */
- public Richardson(final double tolerance, final int maxIters, final int patience, final int m, final double sigma) {
- this(tolerance, maxIters, patience, m, sigma, 1);
+ updateG(Rl);
+ return updatePsi(al, Rl, l);
+ }
+
+ @Override
+ public final SeriesSolution limit(final Iterable seq, final boolean series,
+ final int extrapolateStart) {
+
+ // the sequence will be consumed one element at time
+ final Iterator it = seq.iterator();
+ myPrevIndex = -1;
+ myIndex = 0;
+ evals = 0;
+
+ // read the first elements in the partial sum
+ double partialSum = 0.0;
+ for (int i = 0; i < extrapolateStart; ++i) {
+ partialSum += it.next();
+ ++myIndex;
+ ++evals;
}
- /**
- * Creates an instance of the d-transform for the simplest auxiliary sequence
- * R(l) = l. Most suitable for alternating series.
- *
- * @param tolerance the smallest acceptable change in series evaluation in
- * consecutive iterations that indicates the algorithm has
- * converged
- * @param maxIters the maximum number of sequence terms to evaluate before
- * giving up
- * @param patience the number of consecutive iterations required for the
- * tolerance criterion to be satisfied to stop the algorithm
- * @param m the parameter in the paper; must be >= 1
- */
- public Richardson(final double tolerance, final int maxIters, final int patience, final int m) {
- this(tolerance, maxIters, patience, m, 1.0, 1);
+ // extract the first element of the transformed series
+ int Rl = computeR(0);
+ double al = computePartialSum(it, Rl);
+ if (Double.isNaN(al)) {
+ return new SeriesSolution(Double.NaN, Double.NaN, evals, false);
}
-
- /**
- * Creates an instance of the d-transform for the simplest auxiliary sequence
- * R(l) = l. Most suitable for alternating series.
- *
- * @param tolerance the smallest acceptable change in series evaluation in
- * consecutive iterations that indicates the algorithm has
- * converged
- * @param maxIters the maximum number of sequence terms to evaluate before
- * giving up
- * @param patience the number of consecutive iterations required for the
- * tolerance criterion to be satisfied to stop the algorithm
- */
- public Richardson(final double tolerance, final int maxIters, final int patience) {
- this(tolerance, maxIters, patience, 2);
+ updateG(Rl);
+ updatePsi(al, Rl, 0);
+
+ // main loop
+ double result = al;
+ double oldResult = result;
+ int convergeSteps = 0;
+ for (int l = 1; l <= myMaxIters; ++l) {
+
+ // extract the next element of the transformed series
+ Rl = computeR(l);
+ al = computePartialSum(it, Rl);
+ if (Double.isNaN(al)) {
+ break;
+ }
+ updateG(Rl);
+ result = updatePsi(al, Rl, l);
+
+ // check for convergence
+ if (l >= 2) {
+ final double error = Math.abs(result - oldResult);
+ if (error <= myTol) {
+ ++convergeSteps;
+ } else {
+ convergeSteps = 0;
+ }
+ if (convergeSteps >= myPatience) {
+ return new SeriesSolution(result + partialSum, error, evals, true);
+ }
+ }
+ oldResult = result;
}
- @Override
- public final double next(final double e, final double term) {
-
- // first m elements do not induce an update of g or psi
- myElements[myIndex] = e;
- ++myIndex;
- if (myIndex < myM) {
- return term;
- }
-
- // main updates for psi: in the iterative mode we can only use R(l) = l
- final int l = myIndex - myM;
- final int Rl = l;
- double al = 0.0;
- for (int i = 0; i <= Rl; ++i) {
- al += myElements[i];
- }
- updateG(Rl);
- return updatePsi(al, Rl, l);
- }
+ // could not converge
+ return new SeriesSolution(Double.NaN, Double.NaN, evals, false);
+ }
- @Override
- public final SeriesSolution limit(final Iterable seq, final boolean series, final int extrapolateStart) {
-
- // the sequence will be consumed one element at time
- final Iterator it = seq.iterator();
- myPrevIndex = -1;
- myIndex = 0;
- evals = 0;
-
- // read the first elements in the partial sum
- double partialSum = 0.0;
- for (int i = 0; i < extrapolateStart; ++i) {
- partialSum += it.next();
- ++myIndex;
- ++evals;
- }
-
- // extract the first element of the transformed series
- int Rl = computeR(0);
- double al = computePartialSum(it, Rl);
- if (Double.isNaN(al)) {
- return new SeriesSolution(Double.NaN, Double.NaN, evals, false);
- }
- updateG(Rl);
- updatePsi(al, Rl, 0);
-
- // main loop
- double result = al;
- double oldResult = result;
- int convergeSteps = 0;
- for (int l = 1; l <= myMaxIters; ++l) {
-
- // extract the next element of the transformed series
- Rl = computeR(l);
- al = computePartialSum(it, Rl);
- if (Double.isNaN(al)) {
- break;
- }
- updateG(Rl);
- result = updatePsi(al, Rl, l);
-
- // check for convergence
- if (l >= 2) {
- final double error = Math.abs(result - oldResult);
- if (error <= myTol) {
- ++convergeSteps;
- } else {
- convergeSteps = 0;
- }
- if (convergeSteps >= myPatience) {
- return new SeriesSolution(result + partialSum, error, evals, true);
- }
- }
- oldResult = result;
- }
-
- // could not converge
- return new SeriesSolution(Double.NaN, Double.NaN, evals, false);
- }
+ @Override
+ public final String getName() {
+ return "W(" + myM + ") Algorithm";
+ }
+
+ private final double computePartialSum(final Iterator it, final int R) {
- @Override
- public final String getName() {
- return "W(" + myM + ") Algorithm";
+ // cache the sequence elements so they only need to be consumed once
+ final int newIndex = R + myM - 1;
+ if (newIndex >= myMaxIters) {
+ return Double.NaN;
+ }
+ for (int i = 1 + myPrevIndex; i <= newIndex; ++i) {
+ myElements[i] = it.next();
+ ++myIndex;
+ ++evals;
}
+ myPrevIndex = newIndex;
- private final double computePartialSum(final Iterator it, final int R) {
-
- // cache the sequence elements so they only need to be consumed once
- final int newIndex = R + myM - 1;
- if (newIndex >= myMaxIters) {
- return Double.NaN;
- }
- for (int i = 1 + myPrevIndex; i <= newIndex; ++i) {
- myElements[i] = it.next();
- ++myIndex;
- ++evals;
- }
- myPrevIndex = newIndex;
-
- // compute the transformed series element a(l) (B.3)
- double al = 0.0;
- for (int i = 0; i <= R; ++i) {
- al += myElements[i];
- }
- return al;
+ // compute the transformed series element a(l) (B.3)
+ double al = 0.0;
+ for (int i = 0; i <= R; ++i) {
+ al += myElements[i];
+ }
+ return al;
+ }
+
+ private final double updatePsi(final double term, final int R, final int l) {
+ final int ic = (l + 1) & 1;
+ final int ip = 1 - ic;
+ myPsiAI[0][0][ip] = term / myG[0];
+ myPsiAI[0][1][ip] = 1.0 / myG[0];
+ myPsiQ[0][0][ip] = R + 1.0;
+ for (int k = 1; k < myM; ++k) {
+ myPsiG[0][k - 1][ip] = myG[k] / myG[0];
+ }
+ myPsiG[0][myM - 1][ip] = 1.0 / (R + 1.0);
+
+ double sign = -1.0;
+ for (int p = 1; p <= l; ++p) {
+ double d = 0.0;
+ if (p <= myM) {
+ d = myPsiG[p - 1][p - 1][ip] - myPsiG[p - 1][p - 1][ic];
+ for (int i = p + 2; i <= myM + 1; ++i) {
+ myPsiG[p][i - 2][ip] = (myPsiG[p - 1][i - 2][ip] - myPsiG[p - 1][i - 2][ic]) / d;
+ }
+ }
+ if (p < myM) {
+ myPsiQ[p][p][ip] = sign / myPsiG[p][myM - 1][ip];
+ sign = -sign;
+ }
+ for (int q = 1; q <= p - 1 && q <= myM - 1; ++q) {
+ final double ps = myPsiQ[p - 2][q - 1][ic];
+ final double dq = ps / myPsiQ[p - 1][q - 1][ic] - ps / myPsiQ[p - 1][q - 1][ip];
+ myPsiQ[p][q][ip] = (myPsiQ[p - 1][q][ip] - myPsiQ[p - 1][q][ic]) / dq;
+ }
+ if (p > myM) {
+ final double ps = myPsiQ[p - 2][myM - 1][ic];
+ d = ps / myPsiQ[p - 1][myM - 1][ic] - ps / myPsiQ[p - 1][myM - 1][ip];
+ }
+ myPsiQ[p][0][ip] = (myPsiQ[p - 1][0][ip] - myPsiQ[p - 1][0][ic]) / d;
+ myPsiAI[p][0][ip] = (myPsiAI[p - 1][0][ip] - myPsiAI[p - 1][0][ic]) / d;
+ myPsiAI[p][1][ip] = (myPsiAI[p - 1][1][ip] - myPsiAI[p - 1][1][ic]) / d;
}
- private final double updatePsi(final double term, final int R, final int l) {
- final int ic = (l + 1) & 1;
- final int ip = 1 - ic;
- myPsiAI[0][0][ip] = term / myG[0];
- myPsiAI[0][1][ip] = 1.0 / myG[0];
- myPsiQ[0][0][ip] = R + 1.0;
- for (int k = 1; k < myM; ++k) {
- myPsiG[0][k - 1][ip] = myG[k] / myG[0];
- }
- myPsiG[0][myM - 1][ip] = 1.0 / (R + 1.0);
-
- double sign = -1.0;
- for (int p = 1; p <= l; ++p) {
- double d = 0.0;
- if (p <= myM) {
- d = myPsiG[p - 1][p - 1][ip] - myPsiG[p - 1][p - 1][ic];
- for (int i = p + 2; i <= myM + 1; ++i) {
- myPsiG[p][i - 2][ip] = (myPsiG[p - 1][i - 2][ip] - myPsiG[p - 1][i - 2][ic]) / d;
- }
- }
- if (p < myM) {
- myPsiQ[p][p][ip] = sign / myPsiG[p][myM - 1][ip];
- sign = -sign;
- }
- for (int q = 1; q <= p - 1 && q <= myM - 1; ++q) {
- final double ps = myPsiQ[p - 2][q - 1][ic];
- final double dq = ps / myPsiQ[p - 1][q - 1][ic] - ps / myPsiQ[p - 1][q - 1][ip];
- myPsiQ[p][q][ip] = (myPsiQ[p - 1][q][ip] - myPsiQ[p - 1][q][ic]) / dq;
- }
- if (p > myM) {
- final double ps = myPsiQ[p - 2][myM - 1][ic];
- d = ps / myPsiQ[p - 1][myM - 1][ic] - ps / myPsiQ[p - 1][myM - 1][ip];
- }
- myPsiQ[p][0][ip] = (myPsiQ[p - 1][0][ip] - myPsiQ[p - 1][0][ic]) / d;
- myPsiAI[p][0][ip] = (myPsiAI[p - 1][0][ip] - myPsiAI[p - 1][0][ic]) / d;
- myPsiAI[p][1][ip] = (myPsiAI[p - 1][1][ip] - myPsiAI[p - 1][1][ic]) / d;
- }
-
- final double denom = myPsiAI[l][1][ip];
- if (Math.abs(denom) < TINY) {
- return HUGE;
- } else {
- return myPsiAI[l][0][ip] / denom;
- }
+ final double denom = myPsiAI[l][1][ip];
+ if (Math.abs(denom) < TINY) {
+ return HUGE;
+ } else {
+ return myPsiAI[l][0][ip] / denom;
}
+ }
+
+ private final void updateG(final int R) {
- private final void updateG(final int R) {
-
- // compute the elements of the modified differences g(l) (B.4)
- for (int k = 0; k < myM; ++k) {
- myG[k] = myElements[R + k];
- }
- for (int i = 2; i <= myM; ++i) {
- for (int j = myM; j >= i; --j) {
- myG[j - 1] -= myG[j - 2];
- }
- }
- double p = R + 1.0;
- for (int k = 0; k < myM; ++k) {
- myG[k] *= p;
- p *= (R + 1.0);
- }
+ // compute the elements of the modified differences g(l) (B.4)
+ for (int k = 0; k < myM; ++k) {
+ myG[k] = myElements[R + k];
+ }
+ for (int i = 2; i <= myM; ++i) {
+ for (int j = myM; j >= i; --j) {
+ myG[j - 1] -= myG[j - 2];
+ }
+ }
+ double p = R + 1.0;
+ for (int k = 0; k < myM; ++k) {
+ myG[k] *= p;
+ p *= (R + 1.0);
}
+ }
- private final int computeR(final int l) {
- int r = 0;
- for (int i = 0; i < l; ++i) {
- r = (int) (mySigma * r) + myIncr;
- }
- return r;
+ private final int computeR(final int l) {
+ int r = 0;
+ for (int i = 0; i < l; ++i) {
+ r = (int) (mySigma * r) + myIncr;
}
+ return r;
+ }
}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/SeriesAlgorithm.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/SeriesAlgorithm.java
index 7ba862d959..df61c97ab5 100644
--- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/SeriesAlgorithm.java
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/SeriesAlgorithm.java
@@ -2,6 +2,7 @@
import java.util.Iterator;
import java.util.function.Function;
+import org.hipparchus.complex.Complex;
/**
* The base class of all convergence acceleration algorithms. Provides functionality for evaluating
@@ -31,6 +32,28 @@ public String toString() {
}
}
+ public static final class SeriesSolutionComplex {
+
+ public final Complex limit;
+ public final double error;
+ public final int evaluations;
+ public final boolean converged;
+
+ public SeriesSolutionComplex(final Complex est, final double err, final int evals,
+ final boolean success) {
+ limit = est;
+ error = err;
+ evaluations = evals;
+ converged = success;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%.08f", limit) + " +- " + String.format("%.08f", error) + "\n"
+ + "evaluations: " + evaluations + "\n" + "converged: " + converged;
+ }
+ }
+
protected static final double HUGE = 1e60;
protected static final double TINY = 1e-60;
@@ -64,6 +87,10 @@ public SeriesAlgorithm(final double tolerance, final int maxIters, final int pat
*/
public abstract double next(double e, double term);
+ public Complex nextComplex(Complex e, Complex term) {
+ return null;
+ }
+
/**
* Returns a string representation of the current algorithm.
*
@@ -143,6 +170,67 @@ public SeriesSolution limit(final Iterable seq, final boolean series,
return new SeriesSolution(est + partial, Double.NaN, evals, false);
}
+ // public SeriesSolutionComplex limitComplex(final Iterable seq, final boolean series) {
+ // return limitComplex(seq, series, 0);
+ // }
+ //
+ // public SeriesSolutionComplex limitComplex(final Iterable seq, final boolean series,
+ // final int extrapolateStart) {
+ // myIndex = 0;
+ // int convergeSteps = 0;
+ // int indexBeforeExtrap = 0;
+ // int evals = 0;
+ // Complex partial = Complex.ZERO;
+ // Complex term = Complex.ZERO;
+ // Complex est = Complex.NaN;
+ //
+ // for (final Complex e : seq) {
+ // ++evals;
+ //
+ // // check if extrapolation starts
+ // if (indexBeforeExtrap < extrapolateStart) {
+ // if (series) {
+ // partial = partial.add(e);
+ // }
+ // ++indexBeforeExtrap;
+ // continue;
+ // }
+ //
+ // // get the next term of the sequence
+ // if (series) {
+ // term = term.add(e);
+ // } else {
+ // term = e;
+ // }
+ //
+ // // estimate the next term
+ // final Complex oldest = est;
+ // est = nextComplex(e, term);
+ // if (est.isNaN()) {
+ // break;
+ // }
+ //
+ // // monitor convergence
+ // if (myIndex >= 2) {
+ // final double error = oldest.subtract(est).norm();
+ // if (error <= myTol && est.getReal() != HUGE && est.getImaginary() != HUGE) {
+ // ++convergeSteps;
+ // } else {
+ // convergeSteps = 0;
+ // }
+ // if (convergeSteps >= myPatience) {
+ // return new SeriesSolutionComplex(est.add(partial), error, evals, true);
+ // }
+ // if (myIndex >= myMaxIters || !Double.isFinite(error)) {
+ // break;
+ // }
+ // }
+ // }
+ //
+ // // did not achieve the desired error
+ // return new SeriesSolutionComplex(est.add(partial), Double.NaN, evals, false);
+ // }
+
/**
* Given a sequence represented as an Iterable object, numerically evaluates the limit of the
* sequence or the corresponding series.
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/AitkenComplex.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/AitkenComplex.java
new file mode 100644
index 0000000000..ad78ac3878
--- /dev/null
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/AitkenComplex.java
@@ -0,0 +1,117 @@
+package org.matheclipse.core.numerics.series.dp.complex;
+
+import org.hipparchus.complex.Complex;
+
+/**
+ * Implements a modified adaptive Aitken delta^2 process for estimating infinite series, based on
+ * the method in [1].
+ *
+ *
+ * References:
+ *
+ * - [1] Osada, Naoki. Acceleration methods for slowly convergent sequences and their
+ * applications. Diss. PhD thesis, Nagoya University, 1993.
+ *
+ *
+ */
+public final class AitkenComplex extends SeriesAlgorithmComplex {
+
+ private int status;
+ private Complex xx, dx, dd, xp, alpha;
+ private final Complex[] x;
+ private final Complex[][] s, ds, ts, dts;
+
+ public AitkenComplex(final double tolerance, final int maxIters, final int patience) {
+ super(tolerance, maxIters, patience);
+ x = new Complex[maxIters];
+ s = new Complex[2][maxIters + 1];
+ ds = new Complex[2][maxIters + 1];
+ ts = new Complex[2][maxIters + 1];
+ dts = new Complex[2][maxIters + 1];
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < maxIters + 1; j++) {
+ s[i][j] = Complex.ZERO;
+ ds[i][j] = Complex.ZERO;
+ ts[i][j] = Complex.ZERO;
+ dts[i][j] = Complex.ZERO;
+ }
+ }
+ }
+
+ @Override
+ public final Complex next(final Complex e, final Complex term) {
+ if (myIndex == 0) {
+ xx = dx = dd = Complex.ZERO;
+ }
+ final Complex xxp = xx;
+ x[myIndex] = xx = term;
+ if (myIndex == 0) {
+ dx = xx;
+ } else if (myIndex == 1) {
+ final Complex dxp = dx;
+ dx = xx.subtract(xxp);
+ dd = dx.divide(dx.subtract(dxp));
+ } else {
+ final Complex dxp = dx;
+ final Complex ddp = dd;
+ dx = xx.subtract(xxp);
+ dd = dx.divide(dx.subtract(dxp));
+ alpha = dd.subtract(ddp).reciprocal().add(1.0);
+ alpha = aitken(alpha, ts, dts, myIndex - 1, new Complex(-2.0));
+ }
+ xp = xx;
+ if (myIndex >= 2) {
+ for (int m = 1; m <= myIndex + 1; ++m) {
+ xp = aitken(x[m - 1], s, ds, m, alpha);
+ }
+ }
+ ++myIndex;
+ if (status == 1) {
+ return Complex.NaN;
+ } else {
+ return xp;
+ }
+ }
+
+ private final Complex aitken(final Complex xx, final Complex[][] s, final Complex[][] ds,
+ final int n, final Complex theta) {
+ status = 0;
+ if (n != 1) {
+ final int kend = (n - 1) >> 1;
+ System.arraycopy(s[1], 0, s[0], 0, kend + 1);
+ if ((n & 1) == 0) {
+ System.arraycopy(ds[1], 0, ds[0], 0, kend);
+ } else {
+ System.arraycopy(ds[1], 0, ds[0], 0, kend + 1);
+ }
+ }
+ s[1][0] = xx;
+ if (n == 1) {
+ ds[1][0] = xx;
+ return xx;
+ }
+ ds[1][0] = xx.subtract(s[0][0]);
+ for (int k = 1; k <= (n >> 1); ++k) {
+ final Complex w1 = ds[0][k - 1].multiply(ds[1][k - 1]);
+ final Complex w2 = ds[1][k - 1].subtract(ds[0][k - 1]);
+ if (w2.norm() < TINY) {
+ status = 1;
+ return xx;
+ }
+ final int twok = k << 1;
+ final Complex coef =
+ (new Complex(twok - 1).subtract(theta)).divide(new Complex(twok - 2).subtract(theta));
+ s[1][k] = s[0][k - 1].subtract(coef.multiply(w1).divide(w2));
+ if (n != twok - 1) {
+ ds[1][k] = s[1][k].subtract(s[0][k]);
+ }
+ }
+ final int kopt = n >> 1;
+ return s[1][kopt];
+ }
+
+ @Override
+ public final String getName() {
+ return "Modified Aitken Complex";
+ }
+}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/BrezinskiThetaComplex.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/BrezinskiThetaComplex.java
new file mode 100644
index 0000000000..61c8dbddf6
--- /dev/null
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/BrezinskiThetaComplex.java
@@ -0,0 +1,95 @@
+package org.matheclipse.core.numerics.series.dp.complex;
+
+import org.hipparchus.complex.Complex;
+
+/**
+ * Implements Brezinski's Theta algorithm for convergence of infinite series based on [1] and as
+ * described in [2].
+ *
+ *
+ * References:
+ *
+ * - [1] Brezinski, Claude. Acc�l�ration de la convergence en analyse num�rique. Vol. 584.
+ * Springer, 2006.
+ * - [2] Weniger, Ernst Joachim. "Nonlinear sequence transformations for the acceleration of
+ * convergence and the summation of divergent series." arXiv preprint math/0306302 (2003).
+ *
+ *
+ */
+public final class BrezinskiThetaComplex extends SeriesAlgorithmComplex {
+
+ private final Complex[] myTabA, myTabB;
+
+ public BrezinskiThetaComplex(final double tolerance, final int maxIters, final int patience) {
+ super(tolerance, maxIters, patience);
+ myTabA = new Complex[maxIters];
+ myTabB = new Complex[maxIters];
+ }
+
+ @Override
+ public final Complex next(final Complex e, final Complex term) {
+
+ // first entry of table
+ if (myIndex == 0) {
+ myTabA[0] = term;
+ ++myIndex;
+ return term;
+ }
+
+ // swapping the A and B arrays
+ final Complex[] a, b;
+ if ((myIndex & 1) == 0) {
+ a = myTabA;
+ b = myTabB;
+ } else {
+ a = myTabB;
+ b = myTabA;
+ }
+
+ // the case n >= 1
+ final int jmax = ((myIndex << 1) + 1) / 3;
+ Complex aux1 = a[0];
+ Complex aux2 = Complex.ZERO;
+ a[0] = term;
+ for (int j = 1; j <= jmax; ++j) {
+ final Complex aux3 = aux2;
+ aux2 = aux1;
+ if (j < jmax) {
+ aux1 = a[j];
+ }
+ if ((j & 1) == 0) {
+ final Complex denom = a[j - 1].subtract(b[j - 1].add(b[j - 1])).add(aux2);
+
+ // correct for underflow
+ if (denom.norm() <= TINY) {
+ a[j] = new Complex(HUGE, HUGE);
+ } else {
+ a[j] = aux3
+ .add((b[j - 2].subtract(aux3)).multiply((a[j - 1].subtract(b[j - 1]))).divide(denom));
+ }
+ } else {
+ final Complex diff = a[j - 1].subtract(b[j - 1]);
+
+ // correct for underflow
+ if (diff.norm() <= TINY) {
+ a[j] = new Complex(HUGE, HUGE);
+ } else {
+ a[j] = aux3.add(diff.reciprocal());
+ }
+ }
+ }
+ ++myIndex;
+
+ // return result
+ if ((jmax & 1) == 0) {
+ return a[jmax];
+ } else {
+ return a[jmax - 1];
+ }
+ }
+
+ @Override
+ public final String getName() {
+ return "Brezinski Theta";
+ }
+}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/CohenComplex.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/CohenComplex.java
new file mode 100644
index 0000000000..85fab20d63
--- /dev/null
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/CohenComplex.java
@@ -0,0 +1,68 @@
+package org.matheclipse.core.numerics.series.dp.complex;
+
+import org.hipparchus.complex.Complex;
+import org.matheclipse.core.numerics.utils.Constants;
+import org.matheclipse.core.numerics.utils.SimpleMath;
+
+/**
+ * This is an implementation of Algorithm 1 in [1] for evaluating infinite series whose terms have
+ * alternating signs.
+ *
+ *
+ * References:
+ *
+ * - [1] Cohen, Henri, Fernando Rodriguez Villegas, and Don Zagier. "Convergence acceleration of
+ * alternating series." Experimental mathematics 9.1 (2000): 3-12.
+ *
+ *
+ */
+public final class CohenComplex extends SeriesAlgorithmComplex {
+
+ private final Complex[] myTab;
+ private Complex mySign0;
+
+ public CohenComplex(final double tolerance, final int maxIters, final int patience) {
+ super(tolerance, maxIters, patience);
+ myTab = new Complex[maxIters];
+ }
+
+ @Override
+ public final Complex next(final Complex e, final Complex term) {
+
+ // add next element
+ final int n = myIndex + 1;
+ myTab[myIndex] = e.abs();
+
+ // record the sign of the first term, since this method
+ // requires the first term of the sequence to be positive
+ if (myIndex == 0) {
+ mySign0 = e.sign();
+ if (mySign0.isZero()) {
+ mySign0 = Complex.ONE;
+ }
+ }
+
+ // initialize d value
+ double d = SimpleMath.pow(3.0 + 2.0 * Constants.SQRT2, n);
+ d = 0.5 * (d + 1.0 / d);
+
+ // apply the Chebychef polynomial recursively (Algorithm 1)
+ double b = -1.0;
+ double c = -d;
+ Complex s = Complex.ZERO;
+ for (int k = 0; k < n; ++k) {
+ c = b - c;
+ s = s.add(myTab[k].multiply(c));
+ final double numer = (k + n) * (k - n);
+ final double denom = (k + 0.5) * (k + 1);
+ b *= numer / denom;
+ }
+ ++myIndex;
+ return mySign0.multiply(s).divide(d);
+ }
+
+ @Override
+ public final String getName() {
+ return "Cohen";
+ }
+}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/DirectComplex.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/DirectComplex.java
new file mode 100644
index 0000000000..3d7cf012dd
--- /dev/null
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/DirectComplex.java
@@ -0,0 +1,26 @@
+package org.matheclipse.core.numerics.series.dp.complex;
+
+import org.hipparchus.complex.Complex;
+
+/**
+ * Naively computes the limit of a sequence or series by inspecting elements one a time. No
+ * transform of the original sequence is performed.
+ */
+public final class DirectComplex extends SeriesAlgorithmComplex {
+
+ public DirectComplex(final double tolerance, final int maxIters, final int patience) {
+ super(tolerance, maxIters, patience);
+ }
+
+
+ @Override
+ public final Complex next(final Complex e, final Complex term) {
+ ++myIndex;
+ return term;
+ }
+
+ @Override
+ public final String getName() {
+ return "Direct Complex Sum";
+ }
+}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/EnsembleComplex.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/EnsembleComplex.java
new file mode 100644
index 0000000000..705ac83e50
--- /dev/null
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/EnsembleComplex.java
@@ -0,0 +1,279 @@
+package org.matheclipse.core.numerics.series.dp.complex;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.hipparchus.complex.Complex;
+import org.matheclipse.core.numerics.series.dp.complex.LevinComplex.RemainderSequence;
+import org.matheclipse.core.numerics.series.dp.complex.WynnEpsilonComplex.ShanksMethod;
+
+/**
+ * An ensemble algorithm for evaluating the limits of sequences and series of complex values. This
+ * algorithms works by initializing a number of convergence acceleration algorithms with different
+ * properties, and running them in parallel. The estimates of the sequence's or corresponding series
+ * limit are extracted from the instance that has converged first to a stable limit to the desired
+ * tolerance.
+ */
+public final class EnsembleComplex extends SeriesAlgorithmComplex {
+
+ private static final int PRINT_DIGITS = 18;
+
+ private final int myPrint;
+ private final List myMethods;
+ private final List myForAlternating;
+ private final List myForSequence;
+
+ private int myMethodCount, myBestMethod;
+ private boolean myIsAlternating, myCheckForSequenceOnly;
+ private double myOldSignum;
+ private boolean[] myExcept;
+ private Complex[] myPrevEst, myEst;
+
+ /**
+ * Creates a new instance of an ensemble convergence acceleration algorithm.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of iterations used to assess whether or not a particular instance of
+ * convergence acceleration algorithm used by this algorithm has converged
+ * @param printProgress how often to print the progress of the current algorithm, e.g. the
+ * estimate of a sequence's or series's limit at each iteration according to each algorithm
+ * instance
+ */
+ public EnsembleComplex(final double tolerance, final int maxIters, final int patience,
+ final int printProgress) {
+ super(tolerance, maxIters, patience);
+ myPrint = printProgress;
+ myMethods = new ArrayList<>();
+ myForAlternating = new ArrayList<>();
+ myForSequence = new ArrayList<>();
+
+ // only for alternating series
+ myMethods.add(new CohenComplex(myTol, myMaxIters, 1));
+ myForAlternating.add(true);
+ myForSequence.add(false);
+ myMethods.add(new LevinComplex(myTol, myMaxIters, 1, RemainderSequence.D));
+ myForAlternating.add(true);
+ myForSequence.add(false);
+
+ // for alternating and linear series
+ myMethods.add(new AitkenComplex(myTol, myMaxIters, 1));
+ myForAlternating.add(false);
+ myForSequence.add(true);
+ myMethods.add(new WynnEpsilonComplex(myTol, myMaxIters, 1, ShanksMethod.WYNN));
+ myForAlternating.add(false);
+ myForSequence.add(true);
+ myMethods.add(new LevinComplex(myTol, myMaxIters, 1, RemainderSequence.T));
+ myForAlternating.add(false);
+ myForSequence.add(false);
+
+ // for logarithmic series
+ myMethods.add(new LevinComplex(myTol, myMaxIters, 1, RemainderSequence.U));
+ myForAlternating.add(false);
+ myForSequence.add(false);
+ myMethods.add(new LevinComplex(myTol, myMaxIters, 1, RemainderSequence.V));
+ myForAlternating.add(false);
+ myForSequence.add(false);
+ myMethods.add(new RichardsonComplex(myTol, myMaxIters, 1));
+ myForAlternating.add(false);
+ myForSequence.add(false);
+ myMethods.add(new BrezinskiThetaComplex(myTol, myMaxIters, 1));
+ myForAlternating.add(false);
+ myForSequence.add(true);
+ myMethods.add(new IteratedThetaComplex(myTol, myMaxIters, 1));
+ myForAlternating.add(false);
+ myForSequence.add(true);
+ myMethods.add(new WynnRhoComplex(myTol, myMaxIters, 1));
+ myForAlternating.add(false);
+ myForSequence.add(true);
+ myMethods.add(new WynnEpsilonComplex(myTol, myMaxIters, 1, ShanksMethod.ALTERNATING));
+ myForAlternating.add(false);
+ myForSequence.add(true);
+ }
+
+ public EnsembleComplex(final double tolerance, final int maxIters, final int patience) {
+ this(tolerance, maxIters, patience, 0);
+ }
+
+ @Override
+ public final Complex next(final Complex e, final Complex term) {
+
+ // ********************************************************************
+ // INITIALIZATION
+ // ********************************************************************
+ if (myIndex == 0) {
+
+ // initialize variables
+ myIsAlternating = true;
+ myMethodCount = myMethods.size();
+ myOldSignum = Math.signum(e.getReal());
+ myPrevEst = new Complex[myMethodCount];
+ myEst = new Complex[myMethodCount];
+ myExcept = new boolean[myMethodCount];
+
+ // disable methods for series only
+ if (myCheckForSequenceOnly) {
+ for (int m = 0; m < myMethodCount; ++m) {
+ if (!myForSequence.get(m)) {
+ myExcept[m] = true;
+ if (myPrint > 0) {
+ System.out.println("Disabling method " + myMethods.get(m).getName()
+ + " that is invalid for sequences");
+ }
+ }
+ }
+ myCheckForSequenceOnly = false;
+ }
+
+ // initialize each method
+ for (int m = 0; m < myMethodCount; ++m) {
+ if (!myExcept[m]) {
+ final SeriesAlgorithmComplex method = myMethods.get(m);
+ method.myIndex = 0;
+ myEst[m] = method.next(e, term);
+ }
+ }
+
+ // print header
+ if (myPrint > 0) {
+ String line = "";
+ line += pad("Index", PRINT_DIGITS + 5);
+ for (final SeriesAlgorithmComplex method : myMethods) {
+ line += "\t" + pad(method.getName(), PRINT_DIGITS + 5);
+ }
+ System.out.println(line);
+ }
+
+ // print values
+ if (myPrint > 0) {
+ printRow();
+ }
+
+ ++myIndex;
+ return term;
+ }
+
+ // ********************************************************************
+ // SEQUENCE INSPECTION
+ // ********************************************************************
+ // test for invalid value of series or sequence
+ if (!term.isFinite() || !e.isFinite()) {
+ if (myPrint > 0) {
+ System.out.println("Aborting due to NaN at iteration " + myIndex);
+ }
+ ++myIndex;
+ return Complex.NaN;
+ }
+
+ // track the sign of the sequence
+ final double signum = Math.signum(e.getReal());
+ if (myIndex > 0 && myIsAlternating && signum * myOldSignum >= 0) {
+ myIsAlternating = false;
+ }
+ myOldSignum = signum;
+
+ // exclude alternating series methods if the series does not alternate
+ if (!myIsAlternating) {
+ for (int m = 0; m < myMethodCount; ++m) {
+ if (!myExcept[m] && myForAlternating.get(m)) {
+ myExcept[m] = true;
+ if (myPrint > 0) {
+ System.out.println("Disabling method " + myMethods.get(m).getName()
+ + " because series is not alternating at iteration " + myIndex);
+ }
+ }
+ }
+ }
+
+ // ********************************************************************
+ // MAIN UPDATE
+ // ********************************************************************
+ myBestMethod = -1;
+ double bestError = Double.POSITIVE_INFINITY;
+ for (int m = 0; m < myMethodCount; ++m) {
+ if (myExcept[m]) {
+ continue;
+ }
+
+ // estimate the next terms
+ final SeriesAlgorithmComplex method = myMethods.get(m);
+ myPrevEst[m] = myEst[m];
+ myEst[m] = method.next(e, term);
+
+ // if a method produces an invalid estimate it is excluded
+ if (!myEst[m].isFinite()) {
+ if (myPrint > 0) {
+ System.out.println("Disabling method " + method.getName()
+ + " due to instability at iteration " + myIndex);
+ }
+ myExcept[m] = true;
+ continue;
+ }
+
+ // estimate error and best method
+ final double error = myPrevEst[m].subtract(myEst[m]).norm(); // Math.abs(myPrevEst[m] -
+ // myEst[m]);
+ if (error < bestError && myEst[m].getReal() != HUGE && myEst[m].getImaginary() != HUGE) {
+ myBestMethod = m;
+ bestError = error;
+ }
+ }
+
+ // ********************************************************************
+ // FINALIZE THIS ITERATION
+ // ********************************************************************
+ if (myPrint > 0 && myIndex % myPrint == 0) {
+ printRow();
+ }
+
+ // return the method with the best convergence so far
+ ++myIndex;
+ if (myBestMethod >= 0) {
+ return myEst[myBestMethod];
+ } else {
+ return Complex.NaN;
+ }
+ }
+
+ @Override
+ public final SeriesSolutionComplex limit(final Iterable seq, final boolean series,
+ final int extrapolateStart) {
+ myCheckForSequenceOnly = !series;
+ final SeriesSolutionComplex result = super.limit(seq, series, extrapolateStart);
+ if (myPrint > 0 && myBestMethod >= 0 && result.converged) {
+ System.out.println("Converged at iteration " + myIndex + " with method "
+ + myMethods.get(myBestMethod).getName());
+ }
+ return result;
+ }
+
+ @Override
+ public final String getName() {
+ return "Ensemble";
+ }
+
+ private final void printRow() {
+ String line = pad(myIndex + "", PRINT_DIGITS);
+ for (int m = 0; m < myMethodCount; ++m) {
+ final String str;
+ if (myExcept[m]) {
+ str = "-";
+ } else {
+ str = myEst[m].toString();
+ }
+ line += "\t" + pad(str, PRINT_DIGITS);
+ }
+ System.out.println(line);
+ }
+
+ private static final String pad(final String str, final int len) {
+ if (str.length() >= len) {
+ return str;
+ }
+ String result = str;
+ for (int i = str.length() + 1; i <= len; ++i) {
+ result += " ";
+ }
+ return result;
+ }
+}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/IteratedThetaComplex.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/IteratedThetaComplex.java
new file mode 100644
index 0000000000..7f2ae8ac59
--- /dev/null
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/IteratedThetaComplex.java
@@ -0,0 +1,65 @@
+package org.matheclipse.core.numerics.series.dp.complex;
+
+import org.hipparchus.complex.Complex;
+
+/**
+ * Implements the Iterated Theta algorithm for convergence of infinite series as described in [1].
+ *
+ *
+ * References:
+ *
+ * - [1] Weniger, Ernst Joachim. "Nonlinear sequence transformations for the acceleration of
+ * convergence and the summation of divergent series." arXiv preprint math/0306302 (2003).
+ *
+ *
+ */
+public final class IteratedThetaComplex extends SeriesAlgorithmComplex {
+
+ private final Complex[] myComplexTab;
+
+ public IteratedThetaComplex(final double tolerance, final int maxIters, final int patience) {
+ super(tolerance, maxIters, patience);
+ myComplexTab = new Complex[maxIters];
+ }
+
+ @Override
+ public final Complex next(final Complex e, final Complex term) {
+
+ // initial value for J
+ myComplexTab[myIndex] = term;
+ if (myIndex < 3) {
+ ++myIndex;
+ return term;
+ }
+
+ // higher-order J array
+ final int lmax = myIndex / 3;
+ int m = myIndex;
+ for (int l = 1; l <= lmax; ++l) {
+ m -= 3;
+ final Complex diff0 = myComplexTab[m + 1].subtract(myComplexTab[m]);
+ final Complex diff1 = myComplexTab[m + 2].subtract(myComplexTab[m + 1]);
+ final Complex diff2 = myComplexTab[m + 3].subtract(myComplexTab[m + 2]);
+ final Complex dfsq1 = diff1.subtract(diff0);
+ final Complex dfsq2 = diff2.subtract(diff1);
+ final Complex denom = diff2.multiply(dfsq1).subtract(diff0.multiply(dfsq2));
+ final Complex ratio = dfsq2.divide(denom);
+
+ // safeguarded against underflow
+ if (denom.norm() < TINY) {
+ myComplexTab[m] = new Complex(HUGE, HUGE);
+ } else {
+ myComplexTab[m] = myComplexTab[m + 1].subtract(diff0.multiply(diff1).multiply(ratio));
+ }
+ }
+ ++myIndex;
+
+ // return result
+ return myComplexTab[myIndex % 3];
+ }
+
+ @Override
+ public final String getName() {
+ return "Iterated Theta Complex";
+ }
+}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/LevinComplex.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/LevinComplex.java
new file mode 100644
index 0000000000..6071ceb074
--- /dev/null
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/LevinComplex.java
@@ -0,0 +1,164 @@
+package org.matheclipse.core.numerics.series.dp.complex;
+
+import org.hipparchus.complex.Complex;
+import org.matheclipse.core.numerics.utils.SimpleMath;
+
+/**
+ * Implements the family of Generalized Levin algorithms for convergence of infinite series based on
+ * remainder sequences described in [1, 2] and as outlined in [3].
+ *
+ *
+ * References:
+ *
+ * - [1] Levin, David. "Development of non-linear transformations for improving convergence of
+ * sequences." International Journal of Computer Mathematics 3.1-4 (1972): 371-388.
+ * - [2] Smith, David A., and William F. Ford. "Acceleration of linear and logarithmic
+ * convergence." SIAM Journal on Numerical Analysis 16.2 (1979): 223-240.
+ * - [3] Weniger, Ernst Joachim. "Nonlinear sequence transformations for the acceleration of
+ * convergence and the summation of divergent series." arXiv preprint math/0306302 (2003).
+ *
+ *
+ */
+public final class LevinComplex extends SeriesAlgorithmComplex {
+
+ /**
+ * The type of remainder sequence to assume when accelerating the convergence of a series.
+ */
+ public static enum RemainderSequence {
+ T, D, U, V
+ }
+
+ private final String prefix;
+ private final double myBeta;
+ private final RemainderSequence myRemainder;
+
+ private Complex myPrevE, myPrevTerm;
+ private final Complex[] myTabHi, myTabLo;
+
+ /**
+ * Creates a new instance of the Generalized Levin algorithm with customizable remainder
+ * sequences.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param beta the constant described in [1, 3] for the Levin u-transform remainder sequence: if
+ * this sequence is not selected this argument is ignored
+ * @param remainder the type of assumed remainder sequence as described in [3]
+ */
+ public LevinComplex(final double tolerance, final int maxIters, final int patience,
+ final double beta, final RemainderSequence remainder) {
+ super(tolerance, maxIters, patience);
+ myBeta = beta;
+ myRemainder = remainder;
+ prefix = remainder.name();
+
+ myTabHi = new Complex[maxIters];
+ myTabLo = new Complex[maxIters];
+ }
+
+ /**
+ * Creates a new instance of the Generalized Levin algorithm with customizable remainder
+ * sequences.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param remainder the type of assumed remainder sequence as described in [3]
+ */
+ public LevinComplex(final double tolerance, final int maxIters, final int patience,
+ final RemainderSequence remainder) {
+ this(tolerance, maxIters, patience, 1.0, remainder);
+ }
+
+ public LevinComplex(final double tolerance, final int maxIters, final int patience) {
+ this(tolerance, maxIters, patience, RemainderSequence.U);
+ }
+
+ @Override
+ public final Complex next(final Complex e, final Complex term) {
+
+ // shift over elements by one by skipping first iteration so that
+ // the iteration is actually computing the term one over from the
+ // one used in the current computation
+ if (myIndex == 0) {
+ myPrevE = e;
+ myPrevTerm = term;
+ if (myRemainder == RemainderSequence.V || myRemainder == RemainderSequence.D) {
+ ++myIndex;
+ return myPrevTerm;
+ }
+ }
+
+ // compute the remainder term
+ final int k;
+ final Complex rem;
+ final Complex currentTerm;
+ switch (myRemainder) {
+ case T:
+ k = myIndex;
+ rem = e;
+ currentTerm = term;
+ break;
+ case U:
+ k = myIndex;
+ rem = e.multiply(myBeta + k);
+ currentTerm = term;
+ break;
+ case V:
+ k = myIndex - 1;
+ rem = myPrevE.multiply(e).divide(myPrevE.subtract(e));
+ currentTerm = myPrevTerm;
+ break;
+ case D:
+ k = myIndex - 1;
+ rem = e;
+ currentTerm = myPrevTerm;
+ break;
+ default:
+ return Complex.NaN;
+ }
+ if (rem.norm() < TINY) {
+ return Complex.NaN;
+ }
+
+ // update table
+ myTabHi[k] = currentTerm.divide(rem);
+ myTabLo[k] = rem.reciprocal();
+ if (k > 0) {
+ myTabHi[k - 1] = myTabHi[k].subtract(myTabHi[k - 1]);
+ myTabLo[k - 1] = myTabLo[k].subtract(myTabLo[k - 1]);
+ if (k > 1) {
+ final double base = (myBeta + k - 1) / (myBeta + k);
+ for (int j = 2; j <= k; ++j) {
+ final int kmj = k - j;
+ final double cjm2 = SimpleMath.pow(base, j - 2);
+ final double fac = (myBeta + kmj) * cjm2 / (myBeta + k);
+ myTabHi[kmj] = myTabHi[kmj + 1].subtract(myTabHi[kmj].multiply(fac));
+ myTabLo[kmj] = myTabLo[kmj + 1].subtract(myTabLo[kmj].multiply(fac));
+ }
+ }
+ }
+
+ // increment counters and temporaries
+ myPrevE = e;
+ myPrevTerm = term;
+ ++myIndex;
+
+ // correct for underflow
+ if (myTabLo[0].norm() < TINY) {
+ return new Complex(HUGE, HUGE);
+ } else {
+ return myTabHi[0].divide(myTabLo[0]);
+ }
+ }
+
+ @Override
+ public final String getName() {
+ return "Levin" + " " + prefix;
+ }
+}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/RichardsonComplex.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/RichardsonComplex.java
new file mode 100644
index 0000000000..e773f4f25f
--- /dev/null
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/RichardsonComplex.java
@@ -0,0 +1,315 @@
+package org.matheclipse.core.numerics.series.dp.complex;
+
+import java.util.Iterator;
+import org.hipparchus.complex.Complex;
+
+/**
+ * Implements a generalized form of the Richardson extrapolation for evaluating infinite series.
+ * This is implemented using a d-transform of the original series and is called the W(m) algorithm
+ * [1].
+ *
+ *
+ * The method in [1] defines an auxiliary increasing sequence of integers, R(l), defined by the
+ * recursion R(l + 1) = (int)(sigma * R(l)) + s, where sigma and s are two tuning parameters. The
+ * series are evaluated at each R(l) of the auxiliary sequence and forms a subsequence of the
+ * original partial sums that converges to the same limit. The d-transform is then applied to
+ * accelerate this sequence to infinity.
+ *
+ *
+ *
+ * References:
+ *
+ * - [1] Ford, William & Sidi, Avram. (1987). An Algorithm for a Generalization of the Richardson
+ * Extrapolation Process. SIAM Journal on Numerical Analysis. 24. 10.1137/0724080.
+ *
+ *
+ */
+public final class RichardsonComplex extends SeriesAlgorithmComplex {
+
+ private final double mySigma;
+ private final int myIncr;
+
+ private int myPrevIndex;
+ private final Complex[] myElements;
+
+ private final int myM;
+ private final Complex[] myG;
+ private final Complex[][][] myPsiAI, myPsiQ, myPsiG;
+ private int evals;
+
+ /**
+ * Creates an instance of the d-transform for a given auxiliary sequence.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param m the parameter in the paper; must be >= 1
+ * @param sigma the parameter sigma defining the auxiliary sequence R(l); must be > 1
+ * @param s the parameter s defining the auxiliary sequence R(l); must be >= 1
+ */
+ public RichardsonComplex(final double tolerance, final int maxIters, final int patience,
+ final int m, final double sigma, final int s) {
+ super(tolerance, maxIters, patience);
+ myM = m;
+ mySigma = sigma;
+ myIncr = s;
+
+ myElements = new Complex[maxIters + 1];
+ myG = new Complex[myM];
+ myPsiAI = new Complex[maxIters + 1][2][2];
+ myPsiQ = new Complex[maxIters + 1][myM][2];
+ myPsiG = new Complex[maxIters + 1][myM][2];
+ }
+
+ /**
+ * Creates an instance of the d-transform for a given auxiliary sequence with sigma = 1. The case
+ * s = 1 is most suitable for alternating series or quickly-converging series, while s > 1 is
+ * suitable for power series, trigonometric series, and other series representing analytic
+ * functions, for arguments close to the functions' singular values. [1] notes that s = 1 is OK
+ * for arguments far away from the functions' singular values.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param m the parameter in the paper; must be >= 1
+ * @param s the parameter s defining the auxiliary sequence R(l); must be >= 1
+ */
+ public RichardsonComplex(final double tolerance, final int maxIters, final int patience,
+ final int m, final int s) {
+ this(tolerance, maxIters, patience, m, 1.0, s);
+ }
+
+ /**
+ * Creates an instance of the d-transform for a given auxiliary sequence with s = 1. Most suitable
+ * for monotonic series that converge slowly.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param m the parameter in the paper; must be >= 1
+ * @param sigma the parameter sigma defining the auxiliary sequence R(l); must be > 1
+ */
+ public RichardsonComplex(final double tolerance, final int maxIters, final int patience,
+ final int m, final double sigma) {
+ this(tolerance, maxIters, patience, m, sigma, 1);
+ }
+
+ /**
+ * Creates an instance of the d-transform for the simplest auxiliary sequence R(l) = l. Most
+ * suitable for alternating series.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param m the parameter in the paper; must be >= 1
+ */
+ public RichardsonComplex(final double tolerance, final int maxIters, final int patience,
+ final int m) {
+ this(tolerance, maxIters, patience, m, 1.0, 1);
+ }
+
+ /**
+ * Creates an instance of the d-transform for the simplest auxiliary sequence R(l) = l. Most
+ * suitable for alternating series.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ */
+ public RichardsonComplex(final double tolerance, final int maxIters, final int patience) {
+ this(tolerance, maxIters, patience, 2);
+ }
+
+ @Override
+ public final Complex next(final Complex e, final Complex term) {
+
+ // first m elements do not induce an update of g or psi
+ myElements[myIndex] = e;
+ ++myIndex;
+ if (myIndex < myM) {
+ return term;
+ }
+
+ // main updates for psi: in the iterative mode we can only use R(l) = l
+ final int l = myIndex - myM;
+ final int Rl = l;
+ Complex al = Complex.ZERO;
+ for (int i = 0; i <= Rl; ++i) {
+ al = al.add(myElements[i]);
+ }
+ updateG(Rl);
+ return updatePsi(al, Rl, l);
+ }
+
+ @Override
+ public final SeriesSolutionComplex limit(final Iterable seq, final boolean series,
+ final int extrapolateStart) {
+
+ // the sequence will be consumed one element at time
+ final Iterator it = seq.iterator();
+ myPrevIndex = -1;
+ myIndex = 0;
+ evals = 0;
+
+ // read the first elements in the partial sum
+ Complex partialSum = Complex.ZERO;
+ for (int i = 0; i < extrapolateStart; ++i) {
+ partialSum = partialSum.add(it.next());
+ ++myIndex;
+ ++evals;
+ }
+
+ // extract the first element of the transformed series
+ int Rl = computeR(0);
+ Complex al = computePartialSum(it, Rl);
+ if (al.isNaN()) {
+ return new SeriesSolutionComplex(Complex.NaN, Double.NaN, evals, false);
+ }
+ updateG(Rl);
+ updatePsi(al, Rl, 0);
+
+ // main loop
+ Complex result = al;
+ Complex oldResult = result;
+ int convergeSteps = 0;
+ for (int l = 1; l <= myMaxIters; ++l) {
+
+ // extract the next element of the transformed series
+ Rl = computeR(l);
+ al = computePartialSum(it, Rl);
+ if (al.isNaN()) {
+ break;
+ }
+ updateG(Rl);
+ result = updatePsi(al, Rl, l);
+
+ // check for convergence
+ if (l >= 2) {
+ final double error = result.subtract(oldResult).norm();// Math.abs(result - oldResult);
+ if (error <= myTol) {
+ ++convergeSteps;
+ } else {
+ convergeSteps = 0;
+ }
+ if (convergeSteps >= myPatience) {
+ return new SeriesSolutionComplex(result.add(partialSum), error, evals, true);
+ }
+ }
+ oldResult = result;
+ }
+
+ // could not converge
+ return new SeriesSolutionComplex(Complex.NaN, Double.NaN, evals, false);
+ }
+
+ @Override
+ public final String getName() {
+ return "W(" + myM + ") Algorithm";
+ }
+
+ private final Complex computePartialSum(final Iterator it, final int R) {
+
+ // cache the sequence elements so they only need to be consumed once
+ final int newIndex = R + myM - 1;
+ if (newIndex >= myMaxIters) {
+ return Complex.NaN;
+ }
+ for (int i = 1 + myPrevIndex; i <= newIndex; ++i) {
+ myElements[i] = it.next();
+ ++myIndex;
+ ++evals;
+ }
+ myPrevIndex = newIndex;
+
+ // compute the transformed series element a(l) (B.3)
+ Complex al = Complex.ZERO;
+ for (int i = 0; i <= R; ++i) {
+ al = al.add(myElements[i]);
+ }
+ return al;
+ }
+
+ private final Complex updatePsi(final Complex term, final int R, final int l) {
+ final int ic = (l + 1) & 1;
+ final int ip = 1 - ic;
+ myPsiAI[0][0][ip] = term.divide(myG[0]);
+ myPsiAI[0][1][ip] = myG[0].reciprocal();
+ myPsiQ[0][0][ip] = new Complex(R + 1.0);
+ for (int k = 1; k < myM; ++k) {
+ myPsiG[0][k - 1][ip] = myG[k].divide(myG[0]);
+ }
+ myPsiG[0][myM - 1][ip] = new Complex(R + 1.0).reciprocal();
+
+ double sign = -1.0;
+ for (int p = 1; p <= l; ++p) {
+ Complex d = Complex.ZERO;
+ if (p <= myM) {
+ d = myPsiG[p - 1][p - 1][ip].subtract(myPsiG[p - 1][p - 1][ic]);
+ for (int i = p + 2; i <= myM + 1; ++i) {
+ myPsiG[p][i - 2][ip] =
+ (myPsiG[p - 1][i - 2][ip].subtract(myPsiG[p - 1][i - 2][ic]).divide(d));
+ }
+ }
+ if (p < myM) {
+ myPsiQ[p][p][ip] = new Complex(sign).divide(myPsiG[p][myM - 1][ip]);
+ sign = -sign;
+ }
+ for (int q = 1; q <= p - 1 && q <= myM - 1; ++q) {
+ final Complex ps = myPsiQ[p - 2][q - 1][ic];
+ final Complex dq =
+ ps.divide(myPsiQ[p - 1][q - 1][ic]).subtract(ps.divide(myPsiQ[p - 1][q - 1][ip]));
+ myPsiQ[p][q][ip] = myPsiQ[p - 1][q][ip].subtract(myPsiQ[p - 1][q][ic]).divide(dq);
+ }
+ if (p > myM) {
+ final Complex ps = myPsiQ[p - 2][myM - 1][ic];
+ d = ps.divide(myPsiQ[p - 1][myM - 1][ic]).subtract(ps.divide(myPsiQ[p - 1][myM - 1][ip]));
+ }
+ myPsiQ[p][0][ip] = myPsiQ[p - 1][0][ip].subtract(myPsiQ[p - 1][0][ic]).divide(d);
+ myPsiAI[p][0][ip] = myPsiAI[p - 1][0][ip].subtract(myPsiAI[p - 1][0][ic]).divide(d);
+ myPsiAI[p][1][ip] = myPsiAI[p - 1][1][ip].subtract(myPsiAI[p - 1][1][ic]).divide(d);
+ }
+
+ final Complex denom = myPsiAI[l][1][ip];
+ if (denom.norm() < TINY) {
+ return new Complex(HUGE, HUGE);
+ } else {
+ return myPsiAI[l][0][ip].divide(denom);
+ }
+ }
+
+ private final void updateG(final int R) {
+
+ // compute the elements of the modified differences g(l) (B.4)
+ for (int k = 0; k < myM; ++k) {
+ myG[k] = myElements[R + k];
+ }
+ for (int i = 2; i <= myM; ++i) {
+ for (int j = myM; j >= i; --j) {
+ myG[j - 1] = myG[j - 1].subtract(myG[j - 2]);
+ }
+ }
+ double p = R + 1.0;
+ for (int k = 0; k < myM; ++k) {
+ myG[k] = myG[k].multiply(p);
+ p *= (R + 1.0);
+ }
+ }
+
+ private final int computeR(final int l) {
+ int r = 0;
+ for (int i = 0; i < l; ++i) {
+ r = (int) (mySigma * r) + myIncr;
+ }
+ return r;
+ }
+}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/SeriesAlgorithmComplex.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/SeriesAlgorithmComplex.java
new file mode 100644
index 0000000000..7c69bb4010
--- /dev/null
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/SeriesAlgorithmComplex.java
@@ -0,0 +1,256 @@
+package org.matheclipse.core.numerics.series.dp.complex;
+
+import java.util.Iterator;
+import java.util.function.Function;
+import org.hipparchus.complex.Complex;
+
+/**
+ * The base class of all convergence acceleration algorithms. Provides functionality for evaluating
+ * infinite series and limits of sequences of complex values.
+ */
+public abstract class SeriesAlgorithmComplex {
+
+
+ public static final class SeriesSolutionComplex {
+
+ public final Complex limit;
+ public final double error;
+ public final int evaluations;
+ public final boolean converged;
+
+ public SeriesSolutionComplex(final Complex est, final double err, final int evals,
+ final boolean success) {
+ limit = est;
+ error = err;
+ evaluations = evals;
+ converged = success;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%.08f", limit) + " +- " + String.format("%.08f", error) + "\n"
+ + "evaluations: " + evaluations + "\n" + "converged: " + converged;
+ }
+ }
+
+ protected static final double HUGE = 1e60;
+ protected static final double TINY = 1e-60;
+
+ protected final double myTol;
+ protected final int myMaxIters;
+ protected final int myPatience;
+
+ protected int myIndex;
+
+ /**
+ * Creates a new instance of the current convergence acceleration algorithm.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ */
+ public SeriesAlgorithmComplex(final double tolerance, final int maxIters, final int patience) {
+ myTol = tolerance;
+ myMaxIters = maxIters;
+ myPatience = patience;
+ }
+
+ /**
+ * Update the current algorithm after observing the specified element and partial sum.
+ *
+ * @param e the next element of the sequence
+ * @param term the partial sum of all observed elements of the sequence thus far
+ * @return the next estimate of the limit of the sequence
+ */
+ public abstract Complex next(Complex e, Complex term);
+
+ /**
+ * Returns a string representation of the current algorithm.
+ *
+ * @return a string representation of the current algorithm
+ */
+ public abstract String getName();
+
+ // ==========================================================================
+ // LIMITS
+ // ==========================================================================
+ /**
+ * Given a sequence represented as an Iterable object, numerically evaluates the limit of the
+ * sequence or the corresponding series.
+ *
+ * @param seq an Iterable object of type Complex whose limit to evaluate
+ * @param series a boolean variable indicating whether to evaluate the limit of the series or the
+ * sequence
+ * @param extrapolateStart the number of terms to observe (and sum trivially) before starting
+ * extrapolation
+ * @return a Double that approximates the limit of the sequence or corresponding series. If the
+ * limit cannot be determined, returns NaN
+ */
+ public SeriesSolutionComplex limit(final Iterable seq, final boolean series) {
+ return limit(seq, series, 0);
+ }
+
+ public SeriesSolutionComplex limit(final Iterable seq, final boolean series,
+ final int extrapolateStart) {
+ myIndex = 0;
+ int convergeSteps = 0;
+ int indexBeforeExtrap = 0;
+ int evals = 0;
+ Complex partial = Complex.ZERO;
+ Complex term = Complex.ZERO;
+ Complex est = Complex.NaN;
+
+ for (final Complex e : seq) {
+ ++evals;
+
+ // check if extrapolation starts
+ if (indexBeforeExtrap < extrapolateStart) {
+ if (series) {
+ partial = partial.add(e);
+ }
+ ++indexBeforeExtrap;
+ continue;
+ }
+
+ // get the next term of the sequence
+ if (series) {
+ term = term.add(e);
+ } else {
+ term = e;
+ }
+
+ // estimate the next term
+ final Complex oldest = est;
+ est = next(e, term);
+ if (est.isNaN()) {
+ break;
+ }
+
+ // monitor convergence
+ if (myIndex >= 2) {
+ final double error = oldest.subtract(est).norm();
+ if (error <= myTol && est.getReal() != HUGE && est.getImaginary() != HUGE) {
+ ++convergeSteps;
+ } else {
+ convergeSteps = 0;
+ }
+ if (convergeSteps >= myPatience) {
+ return new SeriesSolutionComplex(est.add(partial), error, evals, true);
+ }
+ if (myIndex >= myMaxIters || !Double.isFinite(error)) {
+ break;
+ }
+ }
+ }
+
+ // did not achieve the desired error
+ return new SeriesSolutionComplex(est.add(partial), Double.NaN, evals, false);
+ }
+
+ /**
+ * Accelerates a sequence or series according to the transformation of the current instance, and
+ * returns the transformed series.
+ *
+ * @param seq an Iterable object of type Double to transform
+ * @param series a boolean variable indicating whether to evaluate the limit of the series or the
+ * sequence
+ * @return an Iterable of type Double representing the transformed series or sequence
+ */
+ public Iterable transform(final Iterable seq, final boolean series) {
+ return () -> new Iterator<>() {
+
+ private final Iterator it = seq.iterator();
+
+ private Complex term = Complex.ZERO;
+ private int index = 0;
+
+ @Override
+ public final boolean hasNext() {
+ return index < SeriesAlgorithmComplex.this.myMaxIters && it.hasNext();
+ }
+
+ @Override
+ public final Complex next() {
+ final Complex e = it.next();
+ if (series) {
+ term = term.add(e);
+ } else {
+ term = e;
+ }
+ ++index;
+ return SeriesAlgorithmComplex.this.next(e, term);
+ }
+ };
+ }
+
+ // ==========================================================================
+ // TRANSLATION OR REPRESENTATION OF SEQUENCES/SERIES
+ // ==========================================================================
+ /**
+ * The Van Wijngaarden transformation converts a convergent sequence of positive terms into a
+ * sequence of alternating terms that has the same limit. This trick is incredibly useful for
+ * accelerating convergence of slowly-increasing sequences of positive terms, because alternating
+ * series are generally easier to accelerate.
+ *
+ *
+ * Formally, given a sequence of terms a(1), a(2)... converts this into an alternating sequence
+ * where the kth term is
+ *
+ * a(k) + 2a(2k) + 4a(4k) + ...
+ *
+ * This method uses the current algorithm's implementation to evaluate the limit of the above
+ * expression. The resulting series are evalated lazily and represented internally as a function.
+ *
+ *
+ *
+ * References:
+ *
+ * - [1] William H. Press, Saul A. Teukolsky, William T. Vetterling, and Brian P. Flannery.
+ * 2007. Numerical Recipes 3rd Edition: The Art of Scientific Computing (3rd. ed.). Cambridge
+ * University Press, USA.
+ *
+ *
+ *
+ * @param seq a sequence of non-negative terms represented as a Function
+ * @param start a long representing the starting index at which the sequence begins
+ * @return a Function representing the resulting alternating sequence obtained with Van
+ * Wijngaarden's transformation
+ */
+ public Function toAlternatingSeries(final Function super Long, Complex> func,
+ final long start) {
+ return (k) -> {
+
+ // create the condensation sequence with kth term 2^k a(2^k)
+ final Iterable condensed = () -> new Iterator<>() {
+
+ private long i = k;
+ private double coeff = 1.0;
+
+ @Override
+ public final boolean hasNext() {
+ return i <= (Long.MAX_VALUE >> 1L) && coeff <= (Double.MAX_VALUE / 2.0);
+ }
+
+ @Override
+ public final Complex next() {
+ final Complex term = func.apply(i + start - 1);
+ final Complex result = term.multiply(coeff);
+ i <<= 1L;
+ coeff *= 2.0;
+ return result;
+ }
+ };
+
+ // determine the next term as the infinite series of this sequence
+ final SeriesSolutionComplex sol = limit(condensed, true);
+ final Complex term = sol.limit;
+ if (((k - 1) & 1L) == 0) {
+ return term;
+ } else {
+ return term.negate();
+ }
+ };
+ }
+}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/WynnEpsilonComplex.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/WynnEpsilonComplex.java
new file mode 100644
index 0000000000..1ef25eb04d
--- /dev/null
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/WynnEpsilonComplex.java
@@ -0,0 +1,288 @@
+package org.matheclipse.core.numerics.series.dp.complex;
+
+import org.hipparchus.complex.Complex;
+
+/**
+ * Implements the Wynn Epsilon algorithm and its variants for convergence of infinite series.
+ * Fundamentally, this implementation contains several methods for implementing the Shanks transform
+ * efficiently. There are several implementations:
+ *
+ * - the classic moving lozenge technique due to Wynn [1, 2]
+ * - the Shanks transform, implemented using the G-transform with a certain remainder sequence
+ * [3]
+ * - the one-parameter generalized epsilon transform of [4]
+ * - the alternating epsilon transform of [5]
+ *
+ *
+ *
+ * References:
+ *
+ * - [1] Wynn, Peter. "On a device for computing the e m (S n) transformation." Mathematical
+ * Tables and Other Aids to Computation (1956): 91-96.
+ * - [2] Weniger, Ernst Joachim. "Nonlinear sequence transformations for the acceleration of
+ * convergence and the summation of divergent series." arXiv preprint math/0306302 (2003).
+ * - [3] Varga, R.S. Extrapolation methods: theory and practice. Numer Algor 4, 305 (1993).
+ * https://doi.org/10.1007/BF02144109
+ * - [4] Broeck, Jean-Marc Vanden, and Leonard W. Schwartz. "A one-parameter family of sequence
+ * transformations." SIAM Journal on Mathematical Analysis 10.3 (1979): 658-666.
+ * - [5] Barber, Michael N., and C. J. Hamer. "Extrapolation of sequences using a generalized
+ * epsilon-algorithm." The ANZIAM Journal 23.3 (1982): 229-240.
+ *
+ *
+ */
+public final class WynnEpsilonComplex extends SeriesAlgorithmComplex {
+
+ public static enum ShanksMethod {
+ WYNN, SHANKS, GENERALIZED, ALTERNATING
+ }
+
+ private final ShanksMethod myMethod;
+ private final double myAlpha;
+
+ private Complex myTerm;
+ private final Complex[] myTab;
+ private final Complex[] myTab2;
+ private final Complex[][] myEps;
+ private final Complex[][] myF;
+
+ /**
+ * Creates a new instance of the Shanks transform with customizable method.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param method the method to use in computing the Shanks transform
+ * @param alpha the parameter alpha to use for the one-parameter epsilon transform; ignored if
+ * method is not set appropriately
+ */
+ public WynnEpsilonComplex(final double tolerance, final int maxIters, final int patience,
+ final ShanksMethod method, final double alpha) {
+ super(tolerance, maxIters, patience);
+ myMethod = method;
+ myAlpha = alpha;
+
+ switch (method) {
+ case SHANKS:
+ myTab = new Complex[maxIters];
+ myTab2 = new Complex[maxIters];
+ myEps = myF = null;
+ break;
+ case GENERALIZED:
+ case ALTERNATING:
+ myTab = myTab2 = null;
+ myEps = new Complex[2][maxIters];
+ myF = new Complex[2][maxIters];
+ break;
+ case WYNN:
+ default:
+ myTab = new Complex[maxIters];
+ myTab2 = null;
+ myEps = myF = null;
+ break;
+ }
+ }
+
+ /**
+ * Creates a new instance of the Shanks transform with customizable method.
+ *
+ * @param tolerance the smallest acceptable change in series evaluation in consecutive iterations
+ * that indicates the algorithm has converged
+ * @param maxIters the maximum number of sequence terms to evaluate before giving up
+ * @param patience the number of consecutive iterations required for the tolerance criterion to be
+ * satisfied to stop the algorithm
+ * @param method the method to use in computing the Shanks transform
+ */
+ public WynnEpsilonComplex(final double tolerance, final int maxIters, final int patience,
+ final ShanksMethod method) {
+ this(tolerance, maxIters, patience, method, -1.0);
+ }
+
+ public WynnEpsilonComplex(final double tolerance, final int maxIters, final int patience) {
+ this(tolerance, maxIters, patience, ShanksMethod.WYNN);
+ }
+
+ @Override
+ public final Complex next(final Complex e, final Complex term) {
+ switch (myMethod) {
+ case SHANKS:
+ return nextGTransform(e, term);
+ case GENERALIZED:
+ case ALTERNATING:
+ return nextAlternatingEpsilon(e, term);
+ case WYNN:
+ default:
+ return nextWynnEpsilon(e, term);
+ }
+ }
+
+ private final Complex nextAlternatingEpsilon(final Complex e, final Complex term) {
+
+ // i is the index of old diagonal, j is new
+ final int i = myIndex & 1;
+ final int j = (i + 1) & 1;
+ final Complex[] eps0 = myEps[i], eps1 = myEps[j];
+ final Complex[] f0 = myF[i], f1 = myF[j];
+
+ // compute the new diagonals of the epsilon and f tables
+ Complex result = f1[0] = term;
+ for (int k = 0; k < myIndex; ++k) {
+ if ((k & 1) == 0) {
+
+ // finite difference for epsilon values
+ final int idx = k >> 1;
+ final Complex diff = f1[idx].subtract(f0[idx]);
+ if (diff.norm() < TINY) {
+ return Complex.NaN;
+ }
+
+ // determine the parameter alpha
+ final double alpha;
+ if (myMethod == ShanksMethod.ALTERNATING) {
+ alpha = -1.0 * (idx & 1);
+ } else {
+ alpha = myAlpha;
+ }
+
+ // update epsilon
+ if (idx == 0) {
+ eps1[idx] = diff.reciprocal();
+ } else {
+ eps1[idx] = eps0[idx - 1].multiply(alpha).add(diff.reciprocal());
+ }
+ } else {
+
+ // finite difference for f values
+ final int idx = (k >> 1) + 1;
+ final Complex diff = eps1[idx - 1].subtract(eps0[idx - 1]);
+ if (diff.norm() < TINY) {
+ return Complex.NaN;
+ }
+
+ // update f
+ result = f1[idx] = f0[idx - 1].add(diff.reciprocal());
+ }
+ }
+ ++myIndex;
+ return result;
+ }
+
+ private final Complex nextWynnEpsilon(final Complex e, final Complex term) {
+
+ // initialization
+ myTab[myIndex] = term;
+ if (myIndex == 0) {
+ ++myIndex;
+ return term;
+ }
+
+ // main loop of Wynn Epsilon algorithm
+ Complex aux = Complex.ZERO;
+ for (int j = myIndex; j >= 1; --j) {
+
+ // compute the next epsilon
+ final Complex temp = aux;
+ aux = myTab[j - 1];
+ Complex diff = myTab[j].subtract(aux);
+
+ // correct denominators for underflow
+ if (diff.norm() <= TINY) {
+ diff = new Complex(HUGE, HUGE);
+ } else {
+
+ diff = temp.add(diff.reciprocal());
+ }
+ myTab[j - 1] = diff;
+ }
+
+ // prepare result
+ final Complex result = myTab[myIndex & 1];
+ ++myIndex;
+ return result;
+ }
+
+ private final Complex nextGTransform(final Complex e, final Complex term) {
+
+ // wait to compute series difference
+ if (myIndex == 0) {
+ ++myIndex;
+ myTerm = term;
+ return term;
+ }
+
+ // initialize table on the first element and difference
+ if (myIndex == 1) {
+ myTab2[0] = term.subtract(myTerm);
+ myTab[0] = myTerm;
+ ++myIndex;
+ myTerm = term;
+ return term;
+ }
+
+ // process the next element
+ ++myIndex;
+ final int idx = Math.min(myIndex - 1, myMaxIters) - 1;
+ Complex tmp1 = term.subtract(myTerm);
+ Complex tmp2 = Complex.ZERO, tmp3 = Complex.ZERO;
+ Complex extrap = myTerm;
+ for (int i = 0; i <= idx - 1; ++i) {
+ if (myTab2[i].norm() < TINY) {
+ myTerm = term;
+ return Complex.NaN;
+ }
+ final Complex rs = tmp1.divide(myTab2[i]).subtract(1.0);
+ final Complex tmp0;
+ if (i == 0) {
+ tmp0 = rs;
+ } else {
+ tmp0 = rs.multiply(myTab2[i - 1]);
+ }
+ if ((i & 1) == 0) {
+ if (rs.norm() < TINY) {
+ myTerm = term;
+ return Complex.NaN;
+ }
+ if (i == 0) {
+ tmp3 = myTab[0].subtract(extrap.subtract(myTab[0])).divide(rs);
+ myTab[0] = extrap;
+ if (myIndex <= 4) {
+ extrap = tmp3;
+ }
+ } else {
+ extrap = myTab[i - 1].subtract(myTab[i].subtract(myTab[i - 1])).divide(rs);
+ myTab[i - 1] = myTab[i];
+ myTab[i] = tmp3;
+ tmp3 = extrap;
+ }
+ }
+ if (i > 0) {
+ myTab2[i - 1] = tmp2;
+ }
+ tmp2 = tmp1;
+ tmp1 = tmp0;
+ }
+ myTab2[idx - 1] = tmp2;
+ if (idx < myMaxIters) {
+ myTab2[idx] = tmp1;
+ }
+ myTab[idx] = extrap;
+ myTerm = term;
+ return extrap;
+ }
+
+ @Override
+ public final String getName() {
+ switch (myMethod) {
+ case SHANKS:
+ return "Shanks";
+ case GENERALIZED:
+ return "Gen. Epsilon(" + myAlpha + ")";
+ case ALTERNATING:
+ return "Alt. Epsilon";
+ case WYNN:
+ default:
+ return "Wynn Epsilon";
+ }
+ }
+}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/WynnRhoComplex.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/WynnRhoComplex.java
new file mode 100644
index 0000000000..4703b1a896
--- /dev/null
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/numerics/series/dp/complex/WynnRhoComplex.java
@@ -0,0 +1,109 @@
+package org.matheclipse.core.numerics.series.dp.complex;
+
+import org.hipparchus.complex.Complex;
+
+/**
+ * Implements the Wynn Rho algorithm for convergence of infinite series introduced in [1] and as
+ * described in [2].
+ *
+ *
+ * References:
+ *
+ * - [1] Wynn, Peter. "On a procrustean technique for the numerical transformation of slowly
+ * convergent sequences and series." Mathematical Proceedings of the Cambridge Philosophical
+ * Society. Vol. 52. No. 4. Cambridge University Press, 1956.
+ * - [2] Osada, Naoki. Acceleration methods for slowly convergent sequences and their
+ * applications. Diss. PhD thesis, Nagoya University, 1993.
+ *
+ *
+ */
+public final class WynnRhoComplex extends SeriesAlgorithmComplex {
+
+ private int status;
+ private Complex xx, dx, dd, xp, alpha;
+ private final Complex[] x;
+ private final Complex[][] myTab, myTab2;
+
+ public WynnRhoComplex(final double tolerance, final int maxIters, final int patience) {
+ super(tolerance, maxIters, patience);
+ x = new Complex[maxIters + 1];
+ myTab = new Complex[2][maxIters + 1];
+ myTab2 = new Complex[2][maxIters + 1];
+ }
+
+ @Override
+ public final Complex next(final Complex e, final Complex term) {
+ if (myIndex == 0) {
+ xx = dx = dd = Complex.ZERO;
+ }
+ final Complex xxp = xx;
+ x[myIndex] = xx = term;
+ if (myIndex == 0) {
+ dx = xx;
+ } else if (myIndex == 1) {
+ final Complex dxp = dx;
+ dx = xx.subtract(xxp);
+ dd = dx.divide(dx.subtract(dxp));
+ } else {
+ final Complex dxp = dx;
+ final Complex ddp = dd;
+ dx = xx.subtract(xxp);
+ dd = dx.divide(dx.subtract(dxp));
+ alpha = dd.subtract(ddp).reciprocal().add(1.0);
+ alpha = wynnrho(alpha, myTab2, myIndex - 1, new Complex(-2.0));
+ }
+ xp = xx;
+ if (myIndex >= 2) {
+ for (int m = 1; m <= myIndex + 1; ++m) {
+ xp = wynnrho(x[m - 1], myTab, m, alpha);
+ }
+ }
+ ++myIndex;
+ if (status == 1) {
+ return Complex.NaN;
+ } else {
+ return xp;
+ }
+ }
+
+ private final Complex wynnrho(final Complex xx, final Complex[][] tab, final int n,
+ final Complex theta) {
+ status = 0;
+ if (n != 1) {
+ System.arraycopy(tab[1], 0, tab[0], 0, n);
+ }
+ tab[1][0] = xx;
+ if (n == 1) {
+ tab[1][1] = theta.negate().divide(xx);
+ return xx;
+ }
+ Complex drho = tab[1][0].subtract(tab[0][0]);
+ if (drho.norm() < TINY) {
+ status = 1;
+ return xx;
+ }
+ tab[1][1] = theta.negate().divide(drho);
+ for (int k = 2; k <= n; ++k) {
+ drho = tab[1][k - 1].subtract(tab[0][k - 1]);
+ if (drho.norm() < TINY) {
+ if ((k & 1) == 0) {
+ status = 1;
+ return xx;
+ } else {
+ return tab[1][k - 1];
+ }
+ }
+ tab[1][k] = tab[0][k - 2].add((new Complex(k - 1.0).subtract(theta)).divide(drho));
+ }
+ if ((n & 1) == 0) {
+ return tab[1][n];
+ } else {
+ return tab[1][n - 1];
+ }
+ }
+
+ @Override
+ public final String getName() {
+ return "Wynn Rho";
+ }
+}
diff --git a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/reflection/system/NSum.java b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/reflection/system/NSum.java
index 1d2d452bd0..6015912d9b 100644
--- a/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/reflection/system/NSum.java
+++ b/symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/reflection/system/NSum.java
@@ -5,13 +5,15 @@
import org.matheclipse.core.eval.EvalEngine;
import org.matheclipse.core.expression.F;
import org.matheclipse.core.expression.ImplementationStatus;
+import org.matheclipse.core.expression.S;
import org.matheclipse.core.interfaces.IAST;
import org.matheclipse.core.interfaces.IASTMutable;
import org.matheclipse.core.interfaces.IExpr;
import org.matheclipse.core.interfaces.ISymbol;
-import org.matheclipse.core.numerics.series.dp.Ensemble;
-import org.matheclipse.core.numerics.series.dp.SeriesAlgorithm;
-import org.matheclipse.core.numerics.series.dp.SeriesAlgorithm.SeriesSolution;
+import org.matheclipse.core.numerics.series.dp.complex.DirectComplex;
+import org.matheclipse.core.numerics.series.dp.complex.EnsembleComplex;
+import org.matheclipse.core.numerics.series.dp.complex.SeriesAlgorithmComplex;
+import org.matheclipse.core.numerics.series.dp.complex.SeriesAlgorithmComplex.SeriesSolutionComplex;
import org.matheclipse.core.numerics.utils.Sequences;
public class NSum extends Sum {
@@ -67,36 +69,46 @@ public IExpr evaluate(IAST ast, EvalEngine engine) {
return arg1.mapThread(ast, 1);
}
- if (ast.isAST2() && ast.arg2().isList3() || ast.arg2().isList4()) {
- // if (Summations.isConvergent(ast)) {
- // ast = ast.apply(S.Sum);
- // IAST preevaledSum = engine.preevalForwardBackwardAST(ast, 1);
- //
- // IExpr temp = evaluateSum(preevaledSum, engine);
- // if (temp.isPresent()) {
- // if (temp.isNumericFunction(true)) {
- // return engine.evalN(temp);
- // }
- // if (!temp.isFree(S.Sum, true)) {
- // temp = F.subst(temp, x -> x == S.Sum ? S.NSum : F.NIL);
- // }
- // return temp;
- // }
- // }
-
- if (ast.isAST2() && ast.arg2().isList3()) {
- IExpr function = arg1;
- IAST limits = (IAST) engine.evaluate(ast.arg2());
- IExpr variable = limits.arg1();
- IExpr lowerLimit = limits.arg2();
- IExpr upperLimit = limits.arg3();
- IASTMutable copy = ast.copy();
- copy.set(1, function);
- copy.set(2, limits);
- IExpr temp = nsum(function, variable, lowerLimit, upperLimit, copy);
+ if (ast.isAST2() && ast.arg2().isList3()) {
+ IAST limits = (IAST) engine.evaluate(ast.arg2());
+ IExpr variable = limits.arg1();
+ IExpr lowerLimit = limits.arg2();
+ IExpr upperLimit = limits.arg3();
+ long start = lowerLimit.toLongDefault();
+ long end = upperLimit.toLongDefault() + 1;
+ boolean preevaluateSymbolic = true;
+ if (start != Long.MIN_VALUE || end != Long.MIN_VALUE) {
+ long range = end - start;
+ if (range > 10000 || range < 10000) {
+ preevaluateSymbolic = false;
+ }
+ }
+ if (preevaluateSymbolic) {
+ // if (Summations.isConvergent(ast)) {
+ ast = ast.apply(S.Sum);
+ IAST preevaledSum = engine.preevalForwardBackwardAST(ast, 1);
+
+ IExpr temp = evaluateSum(preevaledSum, engine);
if (temp.isPresent()) {
+ if (temp.isNumericFunction(true)) {
+ return engine.evalN(temp);
+ }
+ if (!temp.isFree(S.Sum, true)) {
+ temp = F.subst(temp, x -> x == S.Sum ? S.NSum : F.NIL);
+ }
return temp;
}
+ // }
+ }
+
+ IExpr function = arg1;
+
+ IASTMutable copy = ast.copy();
+ copy.set(1, function);
+ copy.set(2, limits);
+ IExpr temp = nsum(function, variable, lowerLimit, upperLimit, copy);
+ if (temp.isPresent()) {
+ return temp;
}
}
@@ -150,41 +162,65 @@ private static IExpr sumStartToInfinity(IExpr function, IExpr variable, IExpr lo
long start = lowerLimit.toLongDefault();
if (start != Long.MIN_VALUE) {
// if (Summations.isConvergent(ast)) {
- LongDoubleFunction longDoubleFunction = new LongDoubleFunction(function, variable);
- Iterable iter = Sequences.toIterable(longDoubleFunction, start);
- SeriesAlgorithm alg = new Ensemble(1e-8, 1000, 5);
- SeriesSolution limit = alg.limit(iter, true);
- return F.num(limit.limit);
+ LongComplexFunction longComplexFunction = new LongComplexFunction(function, variable);
+ Iterable seq = Sequences.toIterable(longComplexFunction, start);
+ SeriesAlgorithmComplex alg = new EnsembleComplex(1e-8, 1000, 5);
+ SeriesSolutionComplex limit = alg.limit(seq, true);
+ return F.inexactNum(limit.limit);
- // LongComplexFunction longComplexFunction = new LongComplexFunction(function, variable);
+ // LongDoubleFunction longDoubleFunction = new LongDoubleFunction(function, variable);
//
- // Iterable iter = Sequences.toIterable(longComplexFunction, start);
+ // Iterable seq = Sequences.toIterable(longDoubleFunction, start);
// SeriesAlgorithm alg = new Ensemble(1e-8, 1000, 5);
- // SeriesSolution limit = alg.limit(iter, true);
+ // SeriesSolution limit = alg.limit(seq, true);
// return F.num(limit.limit);
- // }
}
} else {
long start = lowerLimit.toLongDefault();
- long end = upperLimit.toLongDefault();
- if (start != Long.MIN_VALUE && end != Long.MIN_VALUE) {
- LongDoubleFunction longDoubleFunction = new LongDoubleFunction(function, variable);
-
- Iterable iter = Sequences.toIterable(longDoubleFunction, start, end);
- SeriesAlgorithm alg = new Ensemble(1e-8, 1000, 5);
- SeriesSolution limit = alg.limit(iter, true);
- return F.num(limit.limit);
-
- // LongComplexFunction longComplexFunction = new LongComplexFunction(function, variable);
- // Iterator iter = Sequences.toIterable(longComplexFunction, start,
- // end).iterator();
- // Complex result = Complex.ZERO;
- // for (long i = start; i < end; i++) {
- // Complex c = iter.next();
- // result = result.add(c);
- // }
- // return F.complexNum(result);
+ long end = upperLimit.toLongDefault() + 1;
+ if (start != Long.MIN_VALUE && end != Long.MIN_VALUE && start < end) {
+
+
+ long range = end + 1 - start;
+ if (range < Integer.MAX_VALUE) {
+ LongComplexFunction longComplexFunction = new LongComplexFunction(function, variable);
+ Iterable seq = Sequences.toIterable(longComplexFunction, start, end);
+ SeriesAlgorithmComplex alg = new DirectComplex(1e-8, (int) range, (int) range);
+ SeriesSolutionComplex limit = alg.limit(seq, true);
+ Complex result = limit.limit;
+ if (result.isNaN()) {
+ alg = new EnsembleComplex(1e-8, 1000, 5);
+ limit = alg.limit(seq, true);
+ return F.inexactNum(limit.limit);
+ }
+ return F.inexactNum(result);
+ // Complex result = Complex.ZERO;
+ // for (final Complex e : seq) {
+ // result = result.add(e);
+ // }
+ // return F.complexNum(result);
+
+ // LongDoubleFunction longDoubleFunction = new LongDoubleFunction(function, variable);
+ // Iterable seq = Sequences.toIterable(longDoubleFunction, start, end);
+ // double result = 0.0;
+ // for (final double e : seq) {
+ // result += e;
+ // }
+ // return F.num(result);
+ } else {
+ LongComplexFunction longComplexFunction = new LongComplexFunction(function, variable);
+ Iterable seq = Sequences.toIterable(longComplexFunction, start, end);
+ SeriesAlgorithmComplex alg = new EnsembleComplex(1e-8, 1000, 5);
+ SeriesSolutionComplex limit = alg.limit(seq, true);
+ return F.inexactNum(limit.limit);
+
+ // LongDoubleFunction longDoubleFunction = new LongDoubleFunction(function, variable);
+ // Iterable seq = Sequences.toIterable(longDoubleFunction, start, end);
+ // SeriesAlgorithm alg = new Ensemble(1e-8, 1000, 5);
+ // SeriesSolution limit = alg.limit(seq, true);
+ // return F.num(limit.limit);
+ }
}
}
return F.NIL;
diff --git a/symja_android_library/matheclipse-core/src/main/resources/doc/functions/NSum.md b/symja_android_library/matheclipse-core/src/main/resources/doc/functions/NSum.md
new file mode 100644
index 0000000000..c0223c8d89
--- /dev/null
+++ b/symja_android_library/matheclipse-core/src/main/resources/doc/functions/NSum.md
@@ -0,0 +1,25 @@
+## NSum
+
+```
+NSum(expr, {i, imin, imax})
+```
+
+> evaluates the numerical approximated sum of `expr` with `i` ranging from `imin` to `imax`.
+
+```
+NSum(expr, {i, imin, imax, di})
+```
+
+> `i` ranges from `imin` to `imax` in steps `di`
+
+See
+* [Wikipedia - Series (mathematics)](https://en.wikipedia.org/wiki/Series_(mathematics))
+* [Wikipedia - Summation](https://en.wikipedia.org/wiki/Summation)
+* [Wikipedia - Convergence_tests](https://en.wikipedia.org/wiki/Convergence_tests)
+
+### Examples
+
+```
+>> NSum(k, {k, 1, 10})
+55
+```
diff --git a/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/SumTest.java b/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/SumTest.java
index a4557449b4..7f64808ce5 100644
--- a/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/SumTest.java
+++ b/symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/SumTest.java
@@ -452,11 +452,12 @@ public void testSumPolyLogInifinity() {
@Test
public void testNSum001() {
-
// TODO multi dimensional case
// check("approxSum = NSum((-1)^n*(2/n)^k/k^2, {n,2,Infinity}, {k,1,Infinity})", //
// "");
+ check("NSum(k, {k, 1, 10})", //
+ "55.0");
check("approxSum = NSum(1/i^2, {i,100,Infinity})", //
"0.0100502");
check("restSum = NSum(1/i^2, {i, 10^6,Infinity})", //
@@ -464,12 +465,12 @@ public void testNSum001() {
check("approxSum - restSum", //
"0.0100497");
check("NSum(1/i^2, {i, 100, 10^6})", //
- "0.0100502");
+ "0.0100492");
check("NSum((-5)^i/i!, {i, 0, Infinity})", //
"0.00673795");
//
- // check("NSum(Log(x)/x^(2+2*I), {x, 1,Infinity})", //
- // "-0.181854+I*(-0.13788)");
+ check("NSum(Log(x)/x^(2+2*I), {x, 1,Infinity})", //
+ "-0.182175+I*(-0.136618)");
}
@Test
@@ -480,7 +481,6 @@ public void testNSum002() {
check("NSum(1/(n^2*Log(n)), {n,2,Infinity})", //
"0.605523");
-
// reciprocal power
checkNumeric("NSum((-1)^(k-1)/k^1.5, {k,1,Infinity})", //
"0.7651470246254165");
@@ -533,6 +533,6 @@ public void testNSum003() {
// check("NSum(E^x*Piecewise({{1/(E*x!), x >= 0}}, 0),{x,-Infinity,Infinity})", //
// "");
checkNumeric("NSum(1/E^(k^2), {k,-Infinity,Infinity})", //
- "1.7726372048266523");
+ "1.7726372048266525");
}
}