Skip to content

Commit 461ba62

Browse files
committed
refactor ProductLog - avoid StackOverflow because of recursion
- introduce new EvalEngine#evalNumericFunction() method
1 parent 6413070 commit 461ba62

File tree

4 files changed

+70
-18
lines changed

4 files changed

+70
-18
lines changed

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/builtin/SpecialFunctions.java

+43
Original file line numberDiff line numberDiff line change
@@ -2120,12 +2120,55 @@ public void setUp(final ISymbol newSymbol) {
21202120

21212121
@Override
21222122
public IExpr e1ObjArg(final IExpr o) {
2123+
IExpr temp = functionExpandLogArg(o);
2124+
if (temp.isPresent()) {
2125+
return temp;
2126+
}
21232127
if (o.equals(F.C0) || o.equals(F.CD0)) {
21242128
return F.C0;
21252129
}
21262130
return F.NIL;
21272131
}
21282132

2133+
private static IExpr functionExpandLogArg(final IExpr o) {
2134+
if (o.isTimes() && o.first().isFraction() && o.argSize() == 3) {
2135+
// ProductLog(Rational(k_,n_)*b_^Rational(c_,n_)*Log(b_)) :=
2136+
// Module( {a, v},
2137+
// a = N( (n*ProductLog((b^(c/n)*k*Log(b))/n))/Log(b) );
2138+
// v = Rationalize(a);
2139+
// v*Log(b)/n
2140+
// /; IntegerQ(v) && v >= 1 && PossibleZeroQ( (((-b^(c/n))*k + b^(v/n)*v)*Log(b))/n ))
2141+
IAST times = (IAST) o;
2142+
if (times.arg2().isPower() && times.arg3().isLog()) {
2143+
IAST power = (IAST) times.arg2();
2144+
if (power.base().isInteger() && power.base().equals(times.arg3().first())
2145+
&& power.exponent().isFraction()) {
2146+
IInteger b = (IInteger) times.arg3().first();
2147+
IFraction arg1 = (IFraction) times.arg1();
2148+
IInteger k = arg1.numerator();
2149+
IInteger n = arg1.denominator();
2150+
IFraction powExponent = (IFraction) power.exponent();
2151+
if (n.equals(powExponent.denominator())) {
2152+
EvalEngine engine = EvalEngine.get();
2153+
IExpr a = engine
2154+
.evalNumericFunction(F.Times(n, F.ProductLog(F.Times(arg1, F.Power(b, powExponent), F.Log(b))),
2155+
F.Power(F.Log(b), F.CN1)));
2156+
IExpr v = engine.evaluate(F.Rationalize(a));
2157+
if (v.isInteger() && ((IInteger) v).isGE(F.C1)) {
2158+
IFraction resultFactor = F.QQ((IInteger) v, n);
2159+
IExpr isZero = engine.evaluate(F.Plus(F.Times(k.negate(), F.Power(b, powExponent)),
2160+
F.Times(v, F.Power(b, resultFactor))));
2161+
if (isZero.isZero()) {
2162+
return engine.evaluate(F.Times(resultFactor, F.Log(b)));
2163+
}
2164+
}
2165+
}
2166+
}
2167+
}
2168+
}
2169+
return F.NIL;
2170+
}
2171+
21292172
@Override
21302173
public IExpr e2ObjArg(IExpr k, IExpr z) {
21312174
int ki = Integer.MIN_VALUE;

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/eval/EvalEngine.java

+24-4
Original file line numberDiff line numberDiff line change
@@ -787,8 +787,8 @@ private IAST endTrace() {
787787
*
788788
* @param result0 store the result of the evaluation in the i-th argument of the ast in <code>
789789
* result0[0]</code>. <code>result0[0]</code> should be <code>F.NIL</code> if no evaluation
790-
* occured.
791-
* @param ast the original <code>ast</code> for whixh the arguments should be evaluated
790+
* occurred.
791+
* @param ast the original <code>ast</code> for which the arguments should be evaluated
792792
* @param arg the i-th argument of <code>ast</code>
793793
* @param i <code>arg</code> is the i-th argument of <code>ast</code>
794794
* @param isNumericFunction if <code>true</code> the <code>NumericFunction</code> attribute is set
@@ -1671,14 +1671,14 @@ public final int evalInt(final IExpr expr) throws ArgumentTypeException {
16711671
result = expr.toIntDefault();
16721672
}
16731673
if (expr.isNumericFunction(true)) {
1674-
IExpr numericResult = evalN(expr);
1674+
IExpr numericResult = evalNumericFunction(expr);
16751675
if (numericResult.isReal()) {
16761676
result = numericResult.toIntDefault();
16771677
}
16781678
} else {
16791679
IExpr temp = evaluateNIL(expr);
16801680
if (temp.isNumericFunction(true)) {
1681-
IExpr numericResult = evalN(temp);
1681+
IExpr numericResult = evalNumericFunction(temp);
16821682
if (numericResult.isReal()) {
16831683
result = numericResult.toIntDefault();
16841684
}
@@ -2093,6 +2093,26 @@ private void printOnOffTrace(IExpr unevaledExpr, IExpr evaledExpr) {
20932093
}
20942094
}
20952095

2096+
public final IExpr evalNumericFunction(final IExpr expr) {
2097+
if (expr.isNumericFunction(true)) {
2098+
final boolean oldNumericMode = isNumericMode();
2099+
final long oldDigitPrecision = getNumericPrecision();
2100+
final int oldSignificantFigures = getSignificantFigures();
2101+
try {
2102+
setNumericMode(true, oldDigitPrecision, oldSignificantFigures);
2103+
IExpr temp = evalWithoutNumericReset(expr);
2104+
if (temp.isListOrAssociation() || temp.isRuleAST()) {
2105+
return ((IAST) temp).mapThread(arg -> evalNumericFunction(arg));
2106+
}
2107+
return temp;
2108+
} finally {
2109+
setNumericMode(oldNumericMode);
2110+
setNumericPrecision(oldDigitPrecision);
2111+
}
2112+
}
2113+
return expr;
2114+
}
2115+
20962116
/**
20972117
* Evaluates <code>expr</code> numerically.
20982118
*

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/reflection/system/rules/ProductLogRules.java

+2-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class ProductLogRules {
1313
* <li>index 0 - number of equal rules in <code>RULES</code></li>
1414
* </ul>
1515
*/
16-
final public static int[] SIZES = { 12, 5 };
16+
final public static int[] SIZES = { 12, 4 };
1717

1818
final public static IAST RULES = List(
1919
IInit(ProductLog, SIZES),
@@ -64,9 +64,6 @@ public class ProductLogRules {
6464
Condition(x,GreaterEqual(x,CN1))),
6565
// ProductLog(-1,E^x_*x_):=x/;x<=-1
6666
ISetDelayed(ProductLog(CN1,Times(Exp(x_),x_)),
67-
Condition(x,LessEqual(x,CN1))),
68-
// ProductLog(Log(b_)*k_/n_*b_^(c_/n_)):=Module({a,v},a=N((n*ProductLog((b^(c/n)*k*Log(b))/n))/Log(b));v=Rationalize(a);v*Log(b)/n/;IntegerQ(v)&&v>=1&&PossibleZeroQ(((-b^(c/n)*k+b^(v/n)*v)*Log(b))/n))
69-
ISetDelayed(ProductLog(Times(Log(b_),Rational(k_,n_),Power(b_,Rational(c_,n_)))),
70-
Module(list(a,v),CompoundExpression(Set(a,N(Times(n,Power(Log(b),CN1),ProductLog(Times(Power(b,Times(c,Power(n,CN1))),k,Power(n,CN1),Log(b)))))),Set(v,Rationalize(a)),Condition(Times(v,Power(n,CN1),Log(b)),And(IntegerQ(v),GreaterEqual(v,C1),PossibleZeroQ(Times(Power(n,CN1),Plus(Times(CN1,Power(b,Times(c,Power(n,CN1))),k),Times(Power(b,Times(Power(n,CN1),v)),v)),Log(b))))))))
67+
Condition(x,LessEqual(x,CN1)))
7168
);
7269
}

symja_android_library/rules/ProductLogRules.m

+1-9
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,5 @@
2121
ProductLog(x_*E^x_) := x
2222
/; x>=-1,
2323
ProductLog(-1, x_*E^x_) := x
24-
/; x<=-1,
25-
ProductLog(Rational(k_,n_)*b_^Rational(c_,n_)*Log(b_)) :=
26-
Module( {a, v},
27-
a = N( (n*ProductLog((b^(c/n)*k*Log(b))/n))/Log(b) );
28-
v = Rationalize(a);
29-
v*Log(b)/n
30-
/; IntegerQ(v) &&
31-
v >= 1 &&
32-
PossibleZeroQ( (((-b^(c/n))*k + b^(v/n)*v)*Log(b))/n ))
24+
/; x<=-1
3325
}

0 commit comments

Comments
 (0)