001/*
002 * $Id$
003 */
004
005package edu.jas.root;
006
007
008import java.util.ArrayList;
009import java.util.Collections;
010import java.util.List;
011
012import junit.framework.Test;
013import junit.framework.TestCase;
014import junit.framework.TestSuite;
015
016import edu.jas.arith.BigDecimal;
017import edu.jas.arith.BigRational;
018import edu.jas.arith.Roots;
019import edu.jas.poly.GenPolynomial;
020import edu.jas.poly.GenPolynomialRing;
021import edu.jas.poly.TermOrder;
022import edu.jas.structure.Power;
023import edu.jas.structure.RingFactory;
024
025
026/**
027 * RealRoot tests with JUnit.
028 * @author Heinz Kredel
029 */
030
031public class RealRootTest extends TestCase {
032
033
034    /**
035     * main.
036     */
037    public static void main(String[] args) {
038        junit.textui.TestRunner.run(suite());
039    }
040
041
042    /**
043     * Constructs a <CODE>RealRootTest</CODE> object.
044     * @param name String.
045     */
046    public RealRootTest(String name) {
047        super(name);
048    }
049
050
051    /**
052     */
053    public static Test suite() {
054        TestSuite suite = new TestSuite(RealRootTest.class);
055        return suite;
056    }
057
058
059    TermOrder to = new TermOrder(TermOrder.INVLEX);
060
061
062    GenPolynomialRing<BigRational> dfac;
063
064
065    BigRational ai, bi, ci, di, ei, eps;
066
067
068    GenPolynomial<BigRational> a, b, c, d, e;
069
070
071    int rl = 1;
072
073
074    int kl = 5;
075
076
077    int ll = 7;
078
079
080    int el = 7;
081
082
083    float q = 0.7f;
084
085
086    @Override
087    protected void setUp() {
088        a = b = c = d = e = null;
089        ai = bi = ci = di = ei = null;
090        String[] vars = new String[] { "x" };
091        dfac = new GenPolynomialRing<BigRational>(new BigRational(1), rl, to, vars);
092        // eps = new BigRational(1L,1000000L*1000000L*1000000L);
093        eps = Power.positivePower(new BigRational(1L, 10L), BigDecimal.DEFAULT_PRECISION);
094    }
095
096
097    @Override
098    protected void tearDown() {
099        a = b = c = d = e = null;
100        ai = bi = ci = di = ei = null;
101        dfac = null;
102        eps = null;
103    }
104
105
106    /**
107     * Test Sturm sequence.
108     */
109    public void testSturmSequence() {
110        a = dfac.random(kl, ll, el, q);
111        //System.out.println("a = " + a);
112
113        RealRootsSturm<BigRational> rrs = new RealRootsSturm<BigRational>();
114
115        List<GenPolynomial<BigRational>> S = rrs.sturmSequence(a);
116        //System.out.println("S = " + S);
117
118        try {
119            b = a.remainder(S.get(0));
120        } catch (Exception e) {
121            fail("not S(0)|f " + e);
122        }
123        assertTrue("a mod S(0) == 0 ", b.isZERO());
124
125        assertTrue("S(-1) == 1 ", S.get(S.size() - 1).isConstant());
126    }
127
128
129    /**
130     * Test root bound.
131     */
132    public void testRootBound() {
133        a = dfac.random(kl, ll, el, q);
134        //System.out.println("a = " + a);
135
136        RealRootsAbstract<BigRational> rr = new RealRootsSturm<BigRational>();
137
138        // used root bound
139        BigRational M = rr.realRootBound(a);
140        //System.out.println("M = " + M);
141        assertTrue("M >= 1 ", M.compareTo(BigRational.ONE) >= 0);
142        Interval<BigRational> v1 = new Interval<BigRational>(M.negate(), M);
143        long r1 = rr.realRootCount(v1, a);
144        //System.out.println("v1 = " + v1 + ", r1 = " + r1);
145
146        a = a.monic();
147        //System.out.println("a = " + a);
148        BigDecimal ar = M.getDecimal();
149        //System.out.println("ar = " + ar);
150        assertTrue("ar >= 1 ", ar.compareTo(BigDecimal.ONE) >= 0);
151
152        // maxNorm root bound
153        BigRational mr = a.maxNorm().getRational().sum(BigRational.ONE);
154        BigDecimal dr = mr.getDecimal();
155        //System.out.println("dr = " + dr);
156        //assertTrue("ar >= maxNorm(a): " + (ar.subtract(dr)), ar.compareTo(dr) >= 0);
157        Interval<BigRational> v2 = new Interval<BigRational>(mr.negate(), mr);
158        long r2 = rr.realRootCount(v2,a);
159        //System.out.println("v2 = " + v2 + ", r2 = " + r2);
160        assertTrue("r1 == r2: " + (r2-r1), r1 == r2);
161
162        // squareNorm root bound
163        BigRational qr = a.squareNorm().getRational();
164        BigDecimal ir = Roots.sqrt(qr.getDecimal());
165        //qr = Roots.sqrt(qr);
166        //System.out.println("ir = " + ir);
167        //assertTrue("ar >= squareNorm(a): " + (ar.subtract(ir)), ar.compareTo(ir) >= 0);
168        Interval<BigRational> v3 = new Interval<BigRational>(qr.negate(), qr);
169        long r3 = rr.realRootCount(v3,a);
170        //System.out.println("v3 = " + v3 + ", r3 = " + r3);
171        assertTrue("r1 == r3: " + (r3-r1), r1 == r3);
172
173        // sumNorm root bound
174        BigRational pr = a.sumNorm().getRational();
175        BigDecimal sr = pr.getDecimal();
176        //System.out.println("sr = " + sr);
177        //assertTrue("ar >= squareNorm(a): " + (ar.subtract(sr)), ar.compareTo(sr) >= 0);
178        Interval<BigRational> v4 = new Interval<BigRational>(pr.negate(), pr);
179        long r4 = rr.realRootCount(v4,a);
180        //System.out.println("v4 = " + v4 + ", r4 = " + r4);
181        assertTrue("r1 == r4: " + (r4-r1), r1 == r4);
182
183        // minimal root bound
184        BigDecimal dri = dr.sum(BigDecimal.ONE).inverse();
185        //System.out.println("dri = " + dri + ", sign(dri) = " + dri.signum());
186        assertTrue("minimal root > 0: " + dri, dri.signum() > 0);
187        BigDecimal mri = rr.realMinimalRootBound(a).getDecimal();
188        //System.out.println("mri = " + mri + ", sign(mri) = " + mri.signum());
189        BigDecimal s = dri.subtract(mri).abs();
190        eps = eps.multiply(BigRational.ONE.fromInteger(10));
191        //System.out.println("s = " + s + ", eps = " + eps.getDecimal());
192        assertTrue("minimal root: " + dri, s.compareTo(eps.getDecimal()) < 0);
193
194        // minimal root separation
195        long n = a.degree();
196        if (n > 0) {
197            BigDecimal sep = sr.sum(BigDecimal.ONE).power(2*n).multiply(sr.fromInteger(n).power(n+1)).inverse();
198            //System.out.println("sep = " + sep + ", sign(sep) = " + sep.signum());
199            assertTrue("separation(a) > 0: " + sep, sep.signum() > 0);
200            BigDecimal sri = rr.realMinimalRootSeparation(a).getDecimal();
201            BigDecimal ss = sep.subtract(sri).abs();
202            assertTrue("minimal separation: " + sep, ss.compareTo(eps.getDecimal()) < 0);
203        }
204    }
205
206
207    /**
208     * Test real root isolation.
209     */
210    public void testRealRootIsolation() {
211        a = dfac.random(kl, ll * 2, el * 2, q);
212        //a = a.multiply( dfac.univariate(0) );
213        //System.out.println("a = " + a);
214
215        RealRoots<BigRational> rr = new RealRootsSturm<BigRational>();
216
217        List<Interval<BigRational>> R = rr.realRoots(a);
218        //System.out.println("R = " + R);
219        //assertTrue("#roots >= 0 ", R.size() >= 0);
220        assertTrue("#roots >= 0 ", R != null);
221    }
222
223
224    /**
225     * Test Thom lemma real root sign sequence.
226     */
227    public void testRealRootSignSequence() {
228        a = dfac.random(kl, ll * 2, el * 2, q);
229        if (a.degree() % 2 == 0) {
230            a = a.multiply( dfac.univariate(0).subtract(dfac.getONE()) );
231        }
232        //System.out.println("a = " + a);
233        RealRootsAbstract<BigRational> rr = new RealRootsSturm<BigRational>();
234
235        List<Interval<BigRational>> R = rr.realRoots(a);
236        //System.out.println("R = " + R);
237        //assertTrue("#roots >= 0 ", R.size() >= 0);
238        assertTrue("#roots >= 0 ", R != null);
239
240        int l = R.size();
241        Interval<BigRational> v = R.get(l-1);
242        Interval<BigRational> u = R.get(0);
243        if (u.left.isZERO() && u.right.isZERO()) {
244            Interval<BigRational> w = v;
245            v = u;
246            u = w;
247        }
248        Interval<BigRational> vm = new Interval<BigRational>(u.left,v.right);
249        //System.out.println("v  = " + v);
250        //System.out.println("u  = " + u);
251        //System.out.println("vm = " + vm);
252        long rc = rr.realRootCount(vm,a);
253        //System.out.println("rc = " + rc);
254        assertTrue("root number: R = " + R + ", rc = " + rc, rc == l);
255        long rn = rr.realRootNumber(a,vm);
256        assertTrue("root number == " + rn, rn == l);
257
258        long d = a.degree();
259        List<GenPolynomial<BigRational>> fs = rr.fourierSequence(a);
260        //System.out.println("fs = " + fs);
261        assertTrue("len(fs) == " + (d+1-fs.size()), fs.size() == (d+1));
262
263        //List<Integer> ss = rr.signSequence(a, v);
264        //System.out.println("ss = " + ss);
265        //assertTrue("len(ss) == " + (d-ss.size()), ss.size() == d);
266        for (Interval<BigRational> t : R) {
267            List<Integer> ss = rr.signSequence(a, t);
268            //System.out.println("ss = " + ss);
269            assertTrue("len(ss) == " + (d-ss.size()), ss.size() == d);
270        }
271    }
272
273
274    /**
275     * Test real root isolation Wilkinson polynomials.
276     * p = (x-0)*(x-1)*(x-2)*(x-3)*...*(x-n)
277     */
278    public void testRealRootIsolationWilkinson() {
279        final int N = 10;
280        d = dfac.getONE();
281        e = dfac.univariate(0);
282
283        List<Interval<BigRational>> Rn = new ArrayList<Interval<BigRational>>(N);
284        a = d;
285        for (int i = 0; i < N; i++) {
286            c = dfac.fromInteger(i);
287            Rn.add(new Interval<BigRational>(c.leadingBaseCoefficient()));
288            b = e.subtract(c);
289            a = a.multiply(b);
290        }
291        //System.out.println("a = " + a);
292
293        RealRoots<BigRational> rr = new RealRootsSturm<BigRational>();
294
295        List<Interval<BigRational>> R = rr.realRoots(a);
296        //System.out.println("R = " + R);
297
298        assertTrue("#roots = " + N + " ", R.size() == N);
299
300        eps = eps.multiply(new BigRational("1/10"));
301        //System.out.println("eps = " + eps);
302
303        R = rr.refineIntervals(R, a, eps);
304        //System.out.println("R = " + R);
305        int i = 0;
306        for (Interval<BigRational> v : R) {
307            BigDecimal dd = v.toDecimal(); 
308            BigDecimal di = Rn.get(i++).toDecimal();
309            //System.out.println("v  = " + dd);
310            //System.out.println("vi = " + di);
311            //System.out.println("|dd - di| < eps: " + dd.compareTo(di));
312            assertTrue("|dd - di| < eps ", dd.compareTo(di) == 0);
313        }
314    }
315
316
317    /**
318     * Test real root isolation Wilkinson polynomials inverse.
319     * p = (x-1)*(x-1/2)*(x-1/3)*...*(x-1/n)
320     */
321    public void testRealRootIsolationWilkinsonInverse() {
322        final int N = 9;
323        d = dfac.getONE();
324        e = dfac.univariate(0);
325
326        List<Interval<BigRational>> Rn = new ArrayList<Interval<BigRational>>(N);
327        a = d;
328        for (int i = 1; i < N; i++) { // use only for i > 0, since reverse
329            c = dfac.fromInteger(i);
330            if (i != 0) {
331                c = d.divide(c);
332            }
333            Rn.add(new Interval<BigRational>(c.leadingBaseCoefficient()));
334            b = e.subtract(c);
335            a = a.multiply(b);
336        }
337        //System.out.println("a = " + a);
338        //System.out.println("Rn = " + Rn);
339        Collections.reverse(Rn);
340        //System.out.println("Rn = " + Rn);
341
342        RealRoots<BigRational> rr = new RealRootsSturm<BigRational>();
343
344        List<Interval<BigRational>> R = rr.realRoots(a);
345        //System.out.println("R = " + R);
346
347        assertTrue("#roots = " + (N - 1) + " ", R.size() == (N - 1));
348
349        eps = eps.multiply(new BigRational("1/100"));
350        //System.out.println("eps = " + eps);
351
352        R = rr.refineIntervals(R, a, eps);
353        //System.out.println("R = " + R);
354        int i = 0;
355        for (Interval<BigRational> v : R) {
356            BigDecimal dd = v.toDecimal(); //.sum(eps1);
357            BigDecimal di = Rn.get(i++).toDecimal();
358            //System.out.println("v  = " + dd);
359            //System.out.println("vi = " + di);
360            //System.out.println("|dd - di| < eps: " + dd.compareTo(di));
361            assertTrue("|dd - di| < eps ", dd.compareTo(di) == 0);
362        }
363    }
364
365
366    /**
367     * Test real algebraic number sign.
368     */
369    public void testRealAlgebraicNumberSign() {
370        d = dfac.fromInteger(2);
371        e = dfac.univariate(0);
372
373        a = e.multiply(e);
374        // a = a.multiply(e).multiply(e).multiply(e);
375        a = a.subtract(d); // x^2 -2
376        //System.out.println("a = " + a);
377
378        RealRoots<BigRational> rr = new RealRootsSturm<BigRational>();
379
380        ai = new BigRational(1);
381        bi = new BigRational(2);
382        Interval<BigRational> iv = new Interval<BigRational>(ai, bi);
383        //System.out.println("iv = " + iv);
384        assertTrue("sign change", rr.signChange(iv, a));
385
386        b = dfac.random(kl, (int) a.degree() + 1, (int) a.degree(), 1.0f);
387        //b = dfac.getZERO();
388        //b = dfac.random(kl,ll,el,q);
389        //b = b.multiply(b);
390        //b = b.abs().negate();
391        //System.out.println("b = " + b);
392        if (b.isZERO()) {
393            int s = rr.realSign(iv, a, b);
394            assertTrue("algebraic sign", s == 0);
395            return;
396        }
397
398        int as = rr.realSign(iv, a, b);
399        //System.out.println("as = " + as);
400        // how to test?
401        int asn = rr.realSign(iv, a, b.negate());
402        //System.out.println("asn = " + asn);
403        assertTrue("algebraic sign", as != asn);
404
405        iv = new Interval<BigRational>(bi.negate(), ai.negate());
406        //System.out.println("iv = " + iv);
407        assertTrue("sign change", rr.signChange(iv, a));
408
409        int as1 = rr.realSign(iv, a, b);
410        //System.out.println("as1 = " + as1);
411        // how to test?
412        int asn1 = rr.realSign(iv, a, b.negate());
413        //System.out.println("asn1 = " + asn1);
414        assertTrue("algebraic sign", as1 != asn1);
415
416        assertTrue("algebraic sign", as * as1 == asn * asn1);
417    }
418
419
420    /**
421     * Test real root isolation and decimal refinement of Wilkinson polynomials.
422     * p = (x-0)*(x-1)*(x-2)*(x-3)*...*(x-n)
423     */
424    public void testRealRootIsolationDecimalWilkinson() {
425        final int N = 10;
426        d = dfac.getONE();
427        e = dfac.univariate(0);
428
429        List<Interval<BigRational>> Rn = new ArrayList<Interval<BigRational>>(N);
430        a = d;
431        for (int i = 0; i < N; i++) {
432            c = dfac.fromInteger(i);
433            Rn.add(new Interval<BigRational>(c.leadingBaseCoefficient()));
434            b = e.subtract(c);
435            a = a.multiply(b);
436        }
437        //System.out.println("a = " + a);
438
439        RealRootsAbstract<BigRational> rr = new RealRootsSturm<BigRational>();
440
441        List<Interval<BigRational>> R = rr.realRoots(a);
442        //System.out.println("R = " + R);
443
444        assertTrue("#roots = " + N + " ", R.size() == N);
445
446        eps = eps.multiply(new BigRational(100000));
447        //System.out.println("eps = " + eps);
448        BigDecimal eps1 = new BigDecimal(eps);
449        BigDecimal eps2 = eps1.multiply(new BigDecimal("100"));
450        //System.out.println("eps1 = " + eps1);
451        //System.out.println("eps2 = " + eps2);
452
453        try {
454            int i = 0;
455            for (Interval<BigRational> v : R) {
456                //System.out.println("v = " + v);
457                BigDecimal dd = rr.approximateRoot(v,a,eps);
458                BigDecimal di = Rn.get(i++).toDecimal();
459                //System.out.println("di = " + di);
460                //System.out.println("dd = " + dd);
461                assertTrue("|dd - di| < eps ", dd.subtract(di).abs().compareTo(eps2) <= 0);
462            }
463        } catch (NoConvergenceException e) {
464            fail(e.toString());
465        }
466    }
467
468
469    /**
470     * Test real root isolation and decimal refinement of Wilkinson polynomials, inverse roots.
471     * p = (x-1)*(x-1/2)*(x-1/3)*...*(x-1/n)
472     */
473    public void testRealRootIsolationDecimalWilkinsonInverse() {
474        final int N = 10;
475        d = dfac.getONE();
476        e = dfac.univariate(0);
477
478        List<Interval<BigRational>> Rn = new ArrayList<Interval<BigRational>>(N);
479        a = d;
480        for (int i = 1; i < N; i++) { // use only for i > 0, since reverse
481            c = dfac.fromInteger(i);
482            if (i != 0) {
483                c = d.divide(c);
484            }
485            Rn.add(new Interval<BigRational>(c.leadingBaseCoefficient()));
486            b = e.subtract(c);
487            a = a.multiply(b);
488        }
489        //System.out.println("a = " + a);
490        //System.out.println("Rn = " + Rn);
491        Collections.reverse(Rn);
492        //System.out.println("Rn = " + Rn);
493
494        RealRootsAbstract<BigRational> rr = new RealRootsSturm<BigRational>();
495
496        List<Interval<BigRational>> R = rr.realRoots(a);
497        //System.out.println("R = " + R);
498
499        assertTrue("#roots = " + (N - 1) + " ", R.size() == (N - 1));
500
501        eps = eps.multiply(new BigRational(1000000));
502        //System.out.println("eps = " + eps);
503        BigDecimal eps1 = new BigDecimal(eps);
504        BigDecimal eps2 = eps1.multiply(new BigDecimal("10"));
505        //System.out.println("eps1 = " + eps1);
506        //System.out.println("eps2 = " + eps2);
507
508        try {
509            int i = 0;
510            for (Interval<BigRational> v : R) {
511                //System.out.println("v = " + v);
512                BigDecimal dd = rr.approximateRoot(v,a,eps);
513                BigDecimal di = Rn.get(i++).toDecimal();
514                //System.out.println("di = " + di);
515                //System.out.println("dd = " + dd);
516                assertTrue("|dd - di| < eps ", dd.subtract(di).abs().compareTo(eps2) <= 0);
517            }
518        } catch (NoConvergenceException e) {
519            fail(e.toString());
520        }
521    }
522
523
524    /**
525     * Test real root isolation and decimal refinement of Wilkinson polynomials, all roots.
526     * p = (x-0)*(x-1)*(x-2)*(x-3)*...*(x-n)
527     */
528    public void testRealRootIsolationDecimalWilkinsonAll() {
529        final int N = 10; 
530        d = dfac.getONE();
531        e = dfac.univariate(0);
532
533        List<Interval<BigRational>> Rn = new ArrayList<Interval<BigRational>>(N);
534        a = d;
535        for (int i = 0; i < N; i++) {
536            c = dfac.fromInteger(i);
537            Rn.add(new Interval<BigRational>(c.leadingBaseCoefficient()));
538            b = e.subtract(c);
539            a = a.multiply(b);
540        }
541        //System.out.println("a = " + a);
542
543        RealRootsAbstract<BigRational> rr = new RealRootsSturm<BigRational>();
544
545        eps = eps.multiply(new BigRational(10000));
546        //System.out.println("eps = " + eps);
547        BigDecimal eps1 = new BigDecimal(eps);
548        BigDecimal eps2 = eps1.multiply(new BigDecimal("100"));
549        //System.out.println("eps1 = " + eps1);
550        //System.out.println("eps2 = " + eps2);
551
552        List<BigDecimal> R = null;
553        R = rr.approximateRoots(a,eps);
554        //System.out.println("R = " + R);
555        assertTrue("#roots = " + N + " ", R.size() == N);
556
557        int i = 0;
558        for (BigDecimal dd : R) {
559            //System.out.println("dd = " + dd);
560            BigDecimal di = Rn.get(i++).toDecimal();
561            //System.out.println("di = " + di);
562            assertTrue("|dd - di| < eps ", dd.subtract(di).abs().compareTo(eps2) <= 0);
563        }
564        boolean t = rr.isApproximateRoot(R,a,eps);
565        assertTrue("some |a(dd)| < eps ", t);
566    }
567
568
569    /**
570     * Test real root isolation and decimal refinement of Wilkinson polynomials, inverse roots, all roots.
571     * p = (x-1)*(x-1/2)*(x-1/3)*...*(x-1/n)
572     */
573    public void testRealRootIsolationDecimalWilkinsonInverseAll() {
574        final int N = 10;
575        d = dfac.getONE();
576        e = dfac.univariate(0);
577
578        List<Interval<BigRational>> Rn = new ArrayList<Interval<BigRational>>(N);
579        a = d;
580        for (int i = 1; i < N; i++) { // use only for i > 0, since reverse
581            c = dfac.fromInteger(i);
582            if (i != 0) {
583                c = d.divide(c);
584            }
585            Rn.add(new Interval<BigRational>(c.leadingBaseCoefficient()));
586            b = e.subtract(c);
587            a = a.multiply(b);
588        }
589        //System.out.println("a = " + a);
590        //System.out.println("Rn = " + Rn);
591        Collections.reverse(Rn);
592        //System.out.println("Rn = " + Rn);
593
594        RealRootsAbstract<BigRational> rr = new RealRootsSturm<BigRational>();
595
596        eps = eps.multiply(new BigRational(1000000));
597        //System.out.println("eps = " + eps);
598        BigDecimal eps1 = new BigDecimal(eps);
599        BigDecimal eps2 = eps1.multiply(new BigDecimal("10"));
600        //System.out.println("eps1 = " + eps1);
601        //System.out.println("eps2 = " + eps2);
602
603        List<BigDecimal> R = null;
604        R = rr.approximateRoots(a,eps);
605        //System.out.println("R = " + R);
606        assertTrue("#roots = " + (N - 1) + " ", R.size() == (N - 1));
607
608        int i = 0;
609        for (BigDecimal dd : R) {
610            //System.out.println("dd = " + dd);
611            BigDecimal di = Rn.get(i++).toDecimal();
612            //System.out.println("di = " + di);
613            assertTrue("|dd - di| < eps ", dd.subtract(di).abs().compareTo(eps2) <= 0);
614        }
615        boolean t = rr.isApproximateRoot(R,a,eps);
616        assertTrue("some |a(dd)| < eps ", t);
617    }
618
619}