001/*
002 * $Id: RealRootsSturm.java 5573 2016-08-07 14:24:42Z kredel $
003 */
004
005package edu.jas.root;
006
007
008import java.util.ArrayList;
009import java.util.List;
010
011import org.apache.log4j.Logger;
012
013import edu.jas.arith.BigRational;
014import edu.jas.arith.Rational;
015import edu.jas.poly.GenPolynomial;
016import edu.jas.poly.PolyUtil;
017import edu.jas.structure.RingElem;
018import edu.jas.structure.RingFactory;
019
020
021/**
022 * Real root isolation using Sturm sequences.
023 * @param <C> coefficient type.
024 * @author Heinz Kredel
025 */
026public class RealRootsSturm<C extends RingElem<C> & Rational> extends RealRootsAbstract<C> {
027
028
029    private static final Logger logger = Logger.getLogger(RealRootsSturm.class);
030
031
032    private static final boolean debug = logger.isDebugEnabled();
033
034
035    /**
036     * Sturm sequence.
037     * @param f univariate polynomial.
038     * @return a Sturm sequence for f.
039     */
040    public List<GenPolynomial<C>> sturmSequence(GenPolynomial<C> f) {
041        List<GenPolynomial<C>> S = new ArrayList<GenPolynomial<C>>();
042        if (f == null || f.isZERO()) {
043            return S;
044        }
045        if (f.isConstant()) {
046            S.add(f.monic());
047            return S;
048        }
049        GenPolynomial<C> F = f;
050        S.add(F);
051        GenPolynomial<C> G = PolyUtil.<C> baseDeriviative(f);
052        while (!G.isZERO()) {
053            GenPolynomial<C> r = F.remainder(G);
054            F = G;
055            G = r.negate();
056            S.add(F/*.monic()*/);
057        }
058        //System.out.println("F = " + F);
059        if (F.isConstant()) {
060            return S;
061        }
062        // make squarefree
063        List<GenPolynomial<C>> Sp = new ArrayList<GenPolynomial<C>>(S.size());
064        for (GenPolynomial<C> p : S) {
065            p = p.divide(F);
066            Sp.add(p);
067        }
068        return Sp;
069    }
070
071
072    /**
073     * Isolating intervals for the real roots.
074     * @param f univariate polynomial.
075     * @return a list of isolating intervals for the real roots of f.
076     */
077    @SuppressWarnings("cast")
078    @Override
079    public List<Interval<C>> realRoots(GenPolynomial<C> f) {
080        List<Interval<C>> R = new ArrayList<Interval<C>>();
081        if (f == null || f.isConstant()) {
082            return R;
083        }
084        if (f.isZERO()) {
085            C z = f.ring.coFac.getZERO();
086            R.add(new Interval<C>(z));
087            return R;
088        }
089        if (f.degree(0) == 1L) {
090            C z = f.monic().trailingBaseCoefficient().negate();
091            R.add(new Interval<C>(z));
092            return R;
093        }
094        GenPolynomial<C> F = f;
095        C M = realRootBound(F); // M != 0, since >= 2
096        Interval<C> iv = new Interval<C>(M.negate(), M);
097        //System.out.println("iv = " + iv);
098        List<GenPolynomial<C>> S = sturmSequence(F);
099        //System.out.println("S = " + S);
100        //System.out.println("f_S = " + S.get(0));
101        List<Interval<C>> Rp = realRoots(iv, S);
102        if (logger.isInfoEnabled() && !(((Object) f.ring.coFac) instanceof BigRational)) {
103            //logger.info("realRoots bound: " + iv);
104            logger.info("realRoots: " + Rp);
105        }
106        R.addAll(Rp);
107        return R;
108    }
109
110
111    /**
112     * Isolating intervals for the real roots.
113     * @param iv interval with f(left) * f(right) != 0.
114     * @param S sturm sequence for f and I.
115     * @return a list of isolating intervals for the real roots of f in I.
116     */
117    public List<Interval<C>> realRoots(Interval<C> iv, List<GenPolynomial<C>> S) {
118        List<Interval<C>> R = new ArrayList<Interval<C>>();
119        GenPolynomial<C> f = S.get(0); // squarefree part
120        if (f.isZERO()) {
121            C z = f.leadingBaseCoefficient();
122            if (!iv.contains(z)) {
123                throw new IllegalArgumentException(
124                                "root not in interval: f = " + f + ", iv = " + iv + ", z = " + z);
125            }
126            Interval<C> iv1 = new Interval<C>(z);
127            R.add(iv1);
128            return R;
129        }
130        if (f.isConstant()) {
131            return R;
132            //throw new IllegalArgumentException("f has no root: f = " + f + ", iv = " + iv);
133        }
134        if (f.degree(0) == 1L) {
135            C z = f.monic().trailingBaseCoefficient().negate();
136            if (!iv.contains(z)) {
137                return R;
138                //throw new IllegalArgumentException("root not in interval: f = " + f + ", iv = " + iv + ", z = " + z);
139            }
140            Interval<C> iv1 = new Interval<C>(z);
141            R.add(iv1);
142            return R;
143        }
144        //System.out.println("iv = " + iv);
145        // check sign variations at interval bounds
146        long v = realRootCount(iv, S);
147        //System.out.println("v = " + v);
148        if (v == 0) {
149            return R;
150        }
151        if (v == 1) {
152            iv = excludeZero(iv, S);
153            R.add(iv);
154            return R;
155        }
156        // now v &gt; 1
157        // bi-sect interval, such that f(c) != 0
158        C c = bisectionPoint(iv, f);
159        //System.out.println("c = " + c);
160        // recursion on both sub-intervals
161        Interval<C> iv1 = new Interval<C>(iv.left, c);
162        Interval<C> iv2 = new Interval<C>(c, iv.right);
163        List<Interval<C>> R1 = realRoots(iv1, S);
164        //System.out.println("R1 = " + R1);
165        if (debug) {
166            logger.info("R1 = " + R1);
167        }
168        List<Interval<C>> R2 = realRoots(iv2, S);
169        //System.out.println("R2 = " + R2);
170        if (debug) {
171            logger.info("R2 = " + R2);
172        }
173
174        // refine isolating intervals if adjacent 
175        if (R1.isEmpty()) {
176            R.addAll(R2);
177            return R;
178        }
179        if (R2.isEmpty()) {
180            R.addAll(R1);
181            return R;
182        }
183        iv1 = R1.get(R1.size() - 1); // last
184        iv2 = R2.get(0); // first
185        if (iv1.right.compareTo(iv2.left) < 0) {
186            R.addAll(R1);
187            R.addAll(R2);
188            return R;
189        }
190        // now iv1.right == iv2.left
191        //System.out.println("iv1 = " + iv1);
192        //System.out.println("iv2 = " + iv2);
193        R1.remove(iv1);
194        R2.remove(iv2);
195        while (iv1.right.equals(iv2.left)) {
196            C d1 = bisectionPoint(iv1, f);
197            C d2 = bisectionPoint(iv2, f);
198            Interval<C> iv11 = new Interval<C>(iv1.left, d1);
199            Interval<C> iv12 = new Interval<C>(d1, iv1.right);
200            Interval<C> iv21 = new Interval<C>(iv2.left, d2);
201            Interval<C> iv22 = new Interval<C>(d2, iv2.right);
202
203            boolean b11 = signChange(iv11, f);
204            boolean b12 = signChange(iv12, f); // TODO check unnecessary
205            //boolean b21 = signChange(iv21, f); // TODO check unused or unnecessary
206            boolean b22 = signChange(iv22, f);
207            if (b11) {
208                iv1 = iv11;
209                if (b22) {
210                    iv2 = iv22;
211                } else {
212                    iv2 = iv21;
213                }
214                break; // done, refine
215            }
216            if (b22) {
217                iv2 = iv22;
218                if (b12) {
219                    iv1 = iv12;
220                } else {
221                    iv1 = iv11;
222                }
223                break; // done, refine
224            }
225            iv1 = iv12;
226            iv2 = iv21;
227            //System.out.println("iv1 = " + iv1);
228            //System.out.println("iv2 = " + iv2);
229        }
230        R.addAll(R1);
231        R.add(iv1);
232        R.add(iv2);
233        R.addAll(R2);
234        return R;
235    }
236
237
238    /**
239     * Number of real roots in interval.
240     * @param iv interval with f(left) * f(right) != 0.
241     * @param S sturm sequence for f and I.
242     * @return number of real roots of f in I.
243     */
244    public long realRootCount(Interval<C> iv, List<GenPolynomial<C>> S) {
245        // check sign variations at interval bounds
246        GenPolynomial<C> f = S.get(0); // squarefree part
247        //System.out.println("iv = " + iv);
248        RingFactory<C> cfac = f.ring.coFac;
249        List<C> l = PolyUtil.<C> evaluateMain(cfac, S, iv.left);
250        List<C> r = PolyUtil.<C> evaluateMain(cfac, S, iv.right);
251        long v = RootUtil.<C> signVar(l) - RootUtil.<C> signVar(r);
252        //System.out.println("v = " + v);
253        if (v < 0L) {
254            v = -v;
255        }
256        return v;
257    }
258
259
260    /**
261     * Number of real roots in interval.
262     * @param iv interval with f(left) * f(right) != 0.
263     * @param f univariate polynomial.
264     * @return number of real roots of f in I.
265     */
266    @Override
267    public long realRootCount(Interval<C> iv, GenPolynomial<C> f) {
268        if (f == null || f.isConstant()) { // ? 
269            return 0L;
270        }
271        if (f.isZERO()) {
272            C z = f.leadingBaseCoefficient();
273            if (!iv.contains(z)) {
274                return 0L;
275            }
276            return 1L;
277        }
278        List<GenPolynomial<C>> S = sturmSequence(f);
279        return realRootCount(iv, S);
280    }
281
282
283    /**
284     * Invariant interval for algebraic number sign.
285     * @param iv root isolating interval for f, with f(left) * f(right) &lt; 0.
286     * @param f univariate polynomial, non-zero.
287     * @param g univariate polynomial, gcd(f,g) == 1.
288     * @return v with v a new interval contained in iv such that g(w) != 0 for w
289     *         in v.
290     */
291    @Override
292    public Interval<C> invariantSignInterval(Interval<C> iv, GenPolynomial<C> f, GenPolynomial<C> g) {
293        Interval<C> v = iv;
294        if (g == null || g.isZERO()) {
295            //throw new IllegalArgumentException("g == 0");
296            return v;
297        }
298        if (g.isConstant()) {
299            return v;
300        }
301        if (f == null || f.isZERO()) { // ? || f.isConstant()
302            throw new IllegalArgumentException("f == 0");
303            //return v;
304        }
305        List<GenPolynomial<C>> Sg = sturmSequence(g.monic());
306        Interval<C> ivp = invariantSignInterval(iv, f, Sg);
307        return ivp;
308    }
309
310
311    /**
312     * Invariant interval for algebraic number sign.
313     * @param iv root isolating interval for f, with f(left) * f(right) &lt; 0.
314     * @param f univariate polynomial, non-zero.
315     * @param Sg Sturm sequence for g, a univariate polynomial with gcd(f,g) ==
316     *            1.
317     * @return v with v a new interval contained in iv such that g(w) != 0 for w
318     *         in v.
319     */
320    public Interval<C> invariantSignInterval(Interval<C> iv, GenPolynomial<C> f, List<GenPolynomial<C>> Sg) {
321        Interval<C> v = iv;
322        GenPolynomial<C> g = Sg.get(0);
323        if (g == null || g.isZERO()) {
324            return v;
325        }
326        if (g.isConstant()) {
327            return v;
328        }
329        if (f == null || f.isZERO()) { // ? || f.isConstant()
330            return v;
331        }
332        RingFactory<C> cfac = f.ring.coFac;
333        C two = cfac.fromInteger(2);
334
335        while (true) {
336            long n = realRootCount(v, Sg);
337            logger.debug("n = " + n);
338            if (n == 0) {
339                return v;
340            }
341            C c = v.left.sum(v.right);
342            c = c.divide(two);
343            Interval<C> im = new Interval<C>(c, v.right);
344            if (signChange(im, f)) {
345                v = im;
346            } else {
347                v = new Interval<C>(v.left, c);
348            }
349        }
350        // return v;
351    }
352
353
354    /**
355     * Exclude zero.
356     * @param iv root isolating interval with f(left) * f(right) &lt; 0.
357     * @param S sturm sequence for f and I.
358     * @return a new interval v such that v &lt; 0 or v &gt; 0.
359     */
360    public Interval<C> excludeZero(Interval<C> iv, List<GenPolynomial<C>> S) {
361        if (S == null || S.isEmpty()) {
362            return iv;
363        }
364        C zero = S.get(0).ring.coFac.getZERO();
365        if (!iv.contains(zero)) {
366            return iv;
367        }
368        Interval<C> vn = new Interval<C>(iv.left, zero);
369        if (realRootCount(vn,S) == 1) {
370            return vn;
371        }
372        vn = new Interval<C>(zero, iv.right);
373        return vn;
374    }
375
376}