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