Skip to content

Commit 0a9e4c1

Browse files
committed
WIP #1056 call diophantine solvers from Function FindInstance
- set ToggleFeature.SOLVE_DIOPHANTINE = true to test the experimental implementation
1 parent 724c550 commit 0a9e4c1

File tree

5 files changed

+187
-5
lines changed

5 files changed

+187
-5
lines changed

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/basic/ToggleFeature.java

+6
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,11 @@ public class ToggleFeature {
5858
*/
5959
public static boolean SERIES_DENOMINATOR = false;
6060

61+
/**
62+
* If <code>true</code>, enable solvers from package <code>io.github.mangara.diophantine</code> to
63+
* find some solutions in {@link S#FindInstance}
64+
*/
65+
public static boolean SOLVE_DIOPHANTINE = true;
66+
6167
public static boolean SHOW_STEPS = true;
6268
}

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

+127
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.math.BigInteger;
1717
import java.util.ArrayList;
1818
import java.util.HashMap;
19+
import java.util.Iterator;
1920
import java.util.List;
2021
import java.util.Map;
2122
import java.util.SortedMap;
@@ -79,6 +80,7 @@
7980
import org.matheclipse.core.interfaces.ISymbol;
8081
import org.matheclipse.core.numbertheory.GaussianInteger;
8182
import org.matheclipse.core.numbertheory.Primality;
83+
import org.matheclipse.core.polynomials.QuarticSolver;
8284
import org.matheclipse.core.sympy.series.Sequences;
8385
import org.matheclipse.core.visit.VisitorExpr;
8486
import com.google.common.math.BigIntegerMath;
@@ -88,8 +90,14 @@
8890
import edu.jas.arith.ModInteger;
8991
import edu.jas.arith.ModIntegerRing;
9092
import edu.jas.poly.GenPolynomial;
93+
import edu.jas.poly.Monomial;
9194
import edu.jas.ufd.FactorAbstract;
9295
import edu.jas.ufd.FactorFactory;
96+
import io.github.mangara.diophantine.QuadraticSolver;
97+
import io.github.mangara.diophantine.Utils;
98+
import io.github.mangara.diophantine.XYPair;
99+
import io.github.mangara.diophantine.quadratic.ParabolicSolver;
100+
import io.github.mangara.diophantine.quadratic.PellsSolver;
93101

94102
public final class NumberTheory {
95103

@@ -6616,4 +6624,123 @@ private static IAST quadraticIrrationalPlus(IAST plusAST, IASTMutable resultList
66166624
}
66176625
return F.NIL;
66186626
}
6627+
6628+
public static IAST diophantinePolynomial(final IExpr expr, IAST varList,
6629+
int maximumNumberOfResults) {
6630+
VariablesSet varSet = new VariablesSet(varList);
6631+
IASTMutable result = F.NIL;
6632+
try {
6633+
// try to generate a common expression polynomial
6634+
JASConvert<edu.jas.arith.BigInteger> jas = new JASConvert<edu.jas.arith.BigInteger>(
6635+
varSet.getArrayList(), edu.jas.arith.BigInteger.ZERO);
6636+
GenPolynomial<edu.jas.arith.BigInteger> ePoly = jas.expr2JAS(expr, false);
6637+
result = diophantinePolynomial(ePoly, varList, maximumNumberOfResults);
6638+
result = QuarticSolver.sortASTArguments(result);
6639+
return result;
6640+
} catch (JASConversionException e2) {
6641+
e2.printStackTrace();
6642+
}
6643+
return result;
6644+
}
6645+
6646+
private static IASTAppendable diophantinePolynomial(
6647+
GenPolynomial<edu.jas.arith.BigInteger> polynomial, IAST varList,
6648+
int maximumNumberOfResults) {
6649+
long varDegree = polynomial.degree(0);
6650+
6651+
if (polynomial.isConstant()) {
6652+
return F.ListAlloc(1);
6653+
}
6654+
// a*x^2 + b*x*y + c*y^2 + d*x + e*y + f = 0
6655+
BigInteger a = BigInteger.ZERO;
6656+
BigInteger b = BigInteger.ZERO;
6657+
BigInteger c = BigInteger.ZERO;
6658+
BigInteger d = BigInteger.ZERO;
6659+
BigInteger e = BigInteger.ZERO;
6660+
BigInteger f = BigInteger.ZERO;
6661+
try {
6662+
if (varDegree <= 2) {
6663+
if (varList.argSize() == 1) {
6664+
// x is only variable => b=0;c=0;e=0;
6665+
for (Monomial<edu.jas.arith.BigInteger> monomial : polynomial) {
6666+
edu.jas.arith.BigInteger coeff = monomial.coefficient();
6667+
BigInteger zz = coeff.val;
6668+
long xExp = monomial.exponent().getVal(0);
6669+
if (xExp == 2) {
6670+
a = zz;
6671+
} else if (xExp == 1) {
6672+
d = zz;
6673+
} else if (xExp == 2) {
6674+
f = zz;
6675+
} else {
6676+
throw new ArithmeticException(
6677+
"diophantinePolynomial::Unexpected exponent value: " + xExp);
6678+
}
6679+
}
6680+
} else if (varList.argSize() == 2) {
6681+
// x and y are both variables
6682+
// a*x^2 + b*x*y + c*y^2 + d*x + e*y + f = 0
6683+
for (Monomial<edu.jas.arith.BigInteger> monomial : polynomial) {
6684+
edu.jas.arith.BigInteger coeff = monomial.coefficient();
6685+
BigInteger zz = coeff.val;
6686+
int xi = monomial.exponent().varIndex(0);
6687+
int yi = monomial.exponent().varIndex(1);
6688+
long xExp = monomial.exponent().getVal(xi);
6689+
long yExp = monomial.exponent().getVal(yi);
6690+
if (xExp == 2 && yExp == 0) {
6691+
a = zz;
6692+
} else if (xExp == 1 && yExp == 1) {
6693+
b = zz;
6694+
} else if (xExp == 0 && yExp == 2) {
6695+
c = zz;
6696+
} else if (xExp == 1 && yExp == 0) {
6697+
d = zz;
6698+
} else if (xExp == 0 && yExp == 1) {
6699+
e = zz;
6700+
} else if (xExp == 0 && yExp == 0) {
6701+
f = zz;
6702+
} else {
6703+
throw new ArithmeticException(
6704+
"diophantinePolynomial::Unexpected exponent value: " + xExp);
6705+
}
6706+
}
6707+
}
6708+
Iterator<XYPair> diophantineSolver = null;
6709+
IASTAppendable result = F.ListAlloc();
6710+
if (maximumNumberOfResults == 1 && c.signum() < 0 && f.equals(BigInteger.valueOf(-4))
6711+
&& b.signum() == 0 && d.signum() == 0 && e.signum() == 0) {
6712+
BigInteger cNegate = c.negate();
6713+
if (!Utils.isSquare(cNegate)) {
6714+
// use Pell's equation if -c is not a perfect square
6715+
XYPair xyPair = PellsSolver.leastPositivePellsFourSolution(cNegate);
6716+
result.append(F.List(F.Rule(varList.arg1(), F.ZZ(xyPair.x)),
6717+
F.Rule(varList.arg2(), F.ZZ(xyPair.y))));
6718+
return result;
6719+
}
6720+
}
6721+
if (a.signum() != 0 || b.signum() != 0 || c.signum() != 0) {
6722+
// D := b^2 - 4ac && D == 0
6723+
BigInteger D = b.multiply(b).subtract(a.multiply(c).multiply(BigInteger.valueOf(4)));
6724+
if (D.signum() == 0) {
6725+
diophantineSolver = ParabolicSolver.solve(a, b, c, d, e, f);
6726+
}
6727+
}
6728+
if (diophantineSolver == null) {
6729+
diophantineSolver = QuadraticSolver.solve(a, b, c, d, e, f);
6730+
}
6731+
6732+
int n = 0;
6733+
while (diophantineSolver.hasNext() && n++ < maximumNumberOfResults) {
6734+
XYPair xyPair = diophantineSolver.next();
6735+
result.append(F.List(F.Rule(varList.arg1(), F.ZZ(xyPair.x)),
6736+
F.Rule(varList.arg2(), F.ZZ(xyPair.y))));
6737+
}
6738+
return result;
6739+
}
6740+
} catch (ArithmeticException aex) {
6741+
//
6742+
}
6743+
6744+
return F.NIL;
6745+
}
66196746
}

symja_android_library/matheclipse-core/src/main/java/org/matheclipse/core/convert/JASConvert.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,14 @@ private GenPolynomial<C> expr2Poly(final IExpr exprPoly, boolean numeric2Rationa
356356
// "JASConvert:expr2Poly - invalid exponent: " + ast.arg2().toString());
357357
}
358358
try {
359-
return fPolyFactory.univariate(base.getSymbolName(), exponent);
359+
GenPolynomial<C> v = fPolyFactory.univariate(base.getSymbolName(), 1L);
360+
return v.power(exponent);
361+
// int indexOf = fVariables.indexOf(base);
362+
// if (indexOf >= 0) {
363+
// ExpVectorLong v = new ExpVectorLong(fVariables.size(), indexOf, exponent);
364+
// return fPolyFactory.valueOf(fRingFactory.getONE(), v);
365+
// }
366+
// return fPolyFactory.univariate(base.getSymbolName(), exponent);
360367
} catch (IllegalArgumentException iae) {
361368
// fall through
362369
}

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

+15
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
import org.apache.logging.log4j.Logger;
1010
import org.hipparchus.linear.FieldMatrix;
1111
import org.matheclipse.core.basic.Config;
12+
import org.matheclipse.core.basic.ToggleFeature;
1213
import org.matheclipse.core.builtin.Algebra;
1314
import org.matheclipse.core.builtin.BooleanFunctions;
1415
import org.matheclipse.core.builtin.LinearAlgebra;
16+
import org.matheclipse.core.builtin.NumberTheory;
1517
import org.matheclipse.core.builtin.PolynomialFunctions;
1618
import org.matheclipse.core.builtin.RootsFunctions;
1719
import org.matheclipse.core.convert.ChocoConvert;
@@ -1289,6 +1291,19 @@ public static IExpr solveIntegers(final IAST ast, IAST equationVariables,
12891291
return F.NIL;
12901292
}
12911293
try {
1294+
if (ToggleFeature.SOLVE_DIOPHANTINE) {
1295+
if (equationsAndInequations.argSize() == 1) {
1296+
IExpr eq1 = equationsAndInequations.arg1();
1297+
if (eq1.isEqual() && eq1.second().isZero()) {
1298+
IAST diophantineResult = NumberTheory.diophantinePolynomial(eq1.first(),
1299+
equationVariables, maximumNumberOfResults);
1300+
if (diophantineResult.isPresent()) {
1301+
return diophantineResult;
1302+
}
1303+
}
1304+
}
1305+
}
1306+
12921307
if (equationsAndInequations.isFreeAST(x -> chocoSolver(x))) {
12931308
// choco-solver doesn't handle Power() expressions very well at the moment!
12941309
try {

symja_android_library/matheclipse-core/src/test/java/org/matheclipse/core/system/FindInstanceTest.java

+31-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,43 @@
11
package org.matheclipse.core.system;
22

33
import org.junit.Test;
4+
import org.matheclipse.core.basic.ToggleFeature;
45

56
/** Tests for FindInstance function */
67
public class FindInstanceTest extends ExprEvaluatorTestCase {
78

89
@Test
9-
public void testDiophantine() {
10+
public void testDiophantine001() {
11+
if (ToggleFeature.SOLVE_DIOPHANTINE) {
12+
// ParabolicSolver
13+
check("FindInstance(9*x^2 - 12*x*y + 4*y^2 + 3*x + 2*y == 12,{x,y},Integers, 3)", //
14+
"{{x->0,y->-2},{x->1,y->0},{x->2,y->3}}");
15+
// QuadraticSolver
16+
check("FindInstance(3*x^2 - 8*x*y + 7*y^2 - 4*x + 2*y - 109 == 0,{x,y},Integers, 3)", //
17+
"{{x->2,y->-3},{x->2,y->5},{x->14,y->9}}");
18+
// Pell 4
19+
check("FindInstance(x^2-29986*y^2-4 ==0,{x,y},Integers,1)", //
20+
"{{x->135915148103491619905402044543098,y->784889635731418443294120995460}}");
21+
22+
check("FindInstance(x^2-29986*y^2-4 ==0,{x,y},Integers,3)", //
23+
"{{x->-135915148103491619905402044543098,y->784889635731418443294120995460},{x->-\n" //
24+
+ "2,y->0},{x->135915148103491619905402044543098,y->-784889635731418443294120995460}}");
25+
}
26+
}
27+
28+
@Test
29+
public void testDiophantine002() {
1030
// TODO return condition with extra variable C1
11-
check("FindInstance(13*x+51*y==0, {x,y}, Integers, 6)", //
12-
"{{x->-153,y->39},{x->-102,y->26},{x->-51,y->13},{x->0,y->0},{x->51,y->-13},{x->\n"
13-
+ "102,y->-26}}");
31+
if (ToggleFeature.SOLVE_DIOPHANTINE) {
32+
check("FindInstance(13*x+51*y==0, {x,y}, Integers, 6)", //
33+
"{{x->-102,y->26},{x->-51,y->13},{x->0,y->0},{x->51,y->-13},{x->102,y->-26},{x->\n" //
34+
+ "153,y->-39}}");
35+
} else {
36+
// choco-solver solutions:
37+
check("FindInstance(13*x+51*y==0, {x,y}, Integers, 6)", //
38+
"{{x->-153,y->39},{x->-102,y->26},{x->-51,y->13},{x->0,y->0},{x->51,y->-13},{x->\n" //
39+
+ "102,y->-26}}");
40+
}
1441
}
1542

1643
@Test

0 commit comments

Comments
 (0)