001    /*
002     * $Id: ComplexRootsSturm.java 3613 2011-04-28 08:45:32Z kredel $
003     */
004    
005    package edu.jas.root;
006    
007    
008    import java.util.ArrayList;
009    //import java.util.Arrays;
010    import java.util.List;
011    
012    import org.apache.log4j.Logger;
013    
014    import edu.jas.arith.Rational;
015    import edu.jas.arith.BigRational;
016    import edu.jas.poly.Complex;
017    import edu.jas.poly.ComplexRing;
018    import edu.jas.poly.GenPolynomial;
019    import edu.jas.poly.PolyUtil;
020    import edu.jas.structure.RingElem;
021    import edu.jas.structure.RingFactory;
022    import edu.jas.util.ArrayUtil;
023    
024    
025    /**
026     * Complex roots implemented by Sturm sequences. Algorithms use exact method
027     * derived from Wilf's numeric Routh-Hurwitz method.
028     * @param <C> coefficient type.
029     * @author Heinz Kredel
030     */
031    public class ComplexRootsSturm<C extends RingElem<C> & Rational> extends ComplexRootsAbstract<C> {
032    
033    
034        private static final Logger logger = Logger.getLogger(ComplexRootsSturm.class);
035    
036    
037        private final boolean debug = logger.isDebugEnabled();
038    
039    
040        /**
041         * Constructor.
042         * @param cf coefficient factory.
043         */
044        public ComplexRootsSturm(RingFactory<Complex<C>> cf) {
045            super(cf);
046            //ufd = GCDFactory.<Complex<C>> getImplementation(cf);
047        }
048    
049    
050        /**
051         * Cauchy index of rational function f/g on interval.
052         * @param a interval bound for I = [a,b].
053         * @param b interval bound for I = [a,b].
054         * @param f univariate polynomial.
055         * @param g univariate polynomial.
056         * @return winding number of f/g in I.
057         */
058        public long indexOfCauchy(C a, C b, GenPolynomial<C> f, GenPolynomial<C> g) {
059            List<GenPolynomial<C>> S = sturmSequence(g, f);
060            //System.out.println("S = " + S);
061            if (debug) {
062                logger.info("sturmSeq = " + S);
063            }
064            RingFactory<C> cfac = f.ring.coFac;
065            List<C> l = PolyUtil.<C> evaluateMain(cfac, S, a);
066            List<C> r = PolyUtil.<C> evaluateMain(cfac, S, b);
067            long v = RootUtil.<C> signVar(l) - RootUtil.<C> signVar(r);
068            //System.out.println("v = " + v);
069            //         if (v < 0L) {
070            //             v = -v;
071            //         }
072            return v;
073        }
074    
075    
076        /**
077         * Routh index of complex function f + i g on interval.
078         * @param a interval bound for I = [a,b].
079         * @param b interval bound for I = [a,b].
080         * @param f univariate polynomial.
081         * @param g univariate polynomial != 0.
082         * @return index number of f + i g.
083         */
084        public long[] indexOfRouth(C a, C b, GenPolynomial<C> f, GenPolynomial<C> g) {
085            List<GenPolynomial<C>> S = sturmSequence(f, g);
086            //System.out.println("S = " + S);
087            RingFactory<C> cfac = f.ring.coFac;
088            List<C> l = PolyUtil.<C> evaluateMain(cfac, S, a);
089            List<C> r = PolyUtil.<C> evaluateMain(cfac, S, b);
090            long v = RootUtil.<C> signVar(l) - RootUtil.<C> signVar(r);
091            //System.out.println("v = " + v);
092    
093            long d = f.degree(0);
094            if (d < g.degree(0)) {
095                d = g.degree(0);
096            }
097            //System.out.println("d = " + d);
098            long ui = (d - v) / 2;
099            long li = (d + v) / 2;
100            //System.out.println("upper = " + ui);
101            //System.out.println("lower = " + li);
102            return new long[] { ui, li };
103        }
104    
105    
106        /**
107         * Sturm sequence.
108         * @param f univariate polynomial.
109         * @param g univariate polynomial.
110         * @return a Sturm sequence for f and g.
111         */
112        public List<GenPolynomial<C>> sturmSequence(GenPolynomial<C> f, GenPolynomial<C> g) {
113            List<GenPolynomial<C>> S = new ArrayList<GenPolynomial<C>>();
114            if (f == null || f.isZERO()) {
115                return S;
116            }
117            if (f.isConstant()) {
118                S.add(f.monic());
119                return S;
120            }
121            GenPolynomial<C> F = f;
122            S.add(F);
123            GenPolynomial<C> G = g; //PolyUtil.<C> baseDeriviative(f);
124            while (!G.isZERO()) {
125                GenPolynomial<C> r = F.remainder(G);
126                F = G;
127                G = r.negate();
128                S.add(F/*.monic()*/);
129            }
130            //System.out.println("F = " + F);
131            if (F.isConstant()) {
132                return S;
133            }
134            // make squarefree
135            List<GenPolynomial<C>> Sp = new ArrayList<GenPolynomial<C>>(S.size());
136            for (GenPolynomial<C> p : S) {
137                p = p.divide(F);
138                Sp.add(p);
139            }
140            return Sp;
141        }
142    
143    
144        /**
145         * Complex root count of complex polynomial on rectangle.
146         * @param rect rectangle.
147         * @param a univariate complex polynomial.
148         * @return root count of a in rectangle.
149         */
150        @Override
151        public long complexRootCount(Rectangle<C> rect, GenPolynomial<Complex<C>> a)
152                throws InvalidBoundaryException {
153            return windingNumber(rect, a);
154        }
155    
156    
157        /**
158         * Winding number of complex function A on rectangle.
159         * @param rect rectangle.
160         * @param A univariate complex polynomial.
161         * @return winding number of A arround rect.
162         */
163        public long windingNumber(Rectangle<C> rect, GenPolynomial<Complex<C>> A) throws InvalidBoundaryException {
164            Boundary<C> bound = new Boundary<C>(rect, A); // throws InvalidBoundaryException
165            ComplexRing<C> cr = (ComplexRing<C>) A.ring.coFac;
166            RingFactory<C> cf = cr.ring;
167            C zero = cf.getZERO();
168            C one = cf.getONE();
169            long ix = 0L;
170            for (int i = 0; i < 4; i++) {
171                long ci = indexOfCauchy(zero, one, bound.getRealPart(i), bound.getImagPart(i));
172                //System.out.println("ci["+i+","+(i+1)+"] = " + ci);
173                ix += ci;
174            }
175            if (ix % 2L != 0) {
176                throw new InvalidBoundaryException("odd winding number " + ix);
177            }
178            return ix / 2L;
179        }
180    
181    
182        /**
183         * List of complex roots of complex polynomial a on rectangle.
184         * @param rect rectangle.
185         * @param a univariate squarefree complex polynomial.
186         * @return list of complex roots.
187         */
188        @Override
189        public List<Rectangle<C>> complexRoots(Rectangle<C> rect, GenPolynomial<Complex<C>> a)
190                throws InvalidBoundaryException {
191            ComplexRing<C> cr = (ComplexRing<C>) a.ring.coFac;
192            List<Rectangle<C>> roots = new ArrayList<Rectangle<C>>();
193            if ( a.isConstant() || a.isZERO() ) {
194                return roots;
195            }
196            //System.out.println("rect = " + rect); 
197            long n = windingNumber(rect, a);
198            if (n < 0) { // can this happen?
199                throw new RuntimeException("negative winding number " + n);
200                //System.out.println("negative winding number " + n);
201                //return roots;
202            }
203            if (n == 0) {
204                return roots;
205            }
206            if (n == 1) {
207                roots.add(rect);
208                return roots;
209            }
210            Complex<C> eps = cr.fromInteger(1);
211            eps = eps.divide(cr.fromInteger(1000)); // 1/1000
212            //System.out.println("eps = " + eps);
213            //System.out.println("rect = " + rect); 
214            // construct new center
215            Complex<C> delta = rect.corners[3].subtract(rect.corners[1]);
216            delta = delta.divide(cr.fromInteger(2));
217            //System.out.println("delta = " + delta); 
218            boolean work = true;
219            while (work) {
220                Complex<C> center = rect.corners[1].sum(delta);
221                //System.out.println("center = " + toDecimal(center)); 
222                if (debug) {
223                    logger.info("new center = " + center);
224                }
225                try {
226                    Complex<C>[] cp = (Complex<C>[]) copyOfComplex(rect.corners, 4);
227                        // (Complex<C>[]) new Complex[4];  cp[0] = rect.corners[0];
228                    // cp[0] fix
229                    cp[1] = new Complex<C>(cr, cp[1].getRe(), center.getIm());
230                    cp[2] = center;
231                    cp[3] = new Complex<C>(cr, center.getRe(), cp[3].getIm());
232                    Rectangle<C> nw = new Rectangle<C>(cp);
233                    //System.out.println("nw = " + nw); 
234                    List<Rectangle<C>> nwr = complexRoots(nw, a);
235                    //System.out.println("#nwr = " + nwr.size()); 
236                    roots.addAll(nwr);
237                    if ( roots.size() == a.degree(0) ) {
238                        work = false;
239                        break;
240                    }
241    
242                    cp = (Complex<C>[]) copyOfComplex(rect.corners, 4);
243                    cp[0] = new Complex<C>(cr, cp[0].getRe(), center.getIm());
244                    // cp[1] fix
245                    cp[2] = new Complex<C>(cr, center.getRe(), cp[2].getIm());
246                    cp[3] = center;
247                    Rectangle<C> sw = new Rectangle<C>(cp);
248                    //System.out.println("sw = " + sw); 
249                    List<Rectangle<C>> swr = complexRoots(sw, a);
250                    //System.out.println("#swr = " + swr.size()); 
251                    roots.addAll(swr);
252                    if ( roots.size() == a.degree(0) ) {
253                        work = false;
254                        break;
255                    }
256    
257                    cp = (Complex<C>[]) copyOfComplex(rect.corners, 4);
258                    cp[0] = center;
259                    cp[1] = new Complex<C>(cr, center.getRe(), cp[1].getIm());
260                    // cp[2] fix
261                    cp[3] = new Complex<C>(cr, cp[3].getRe(), center.getIm());
262                    Rectangle<C> se = new Rectangle<C>(cp);
263                    //System.out.println("se = " + se); 
264                    List<Rectangle<C>> ser = complexRoots(se, a);
265                    //System.out.println("#ser = " + ser.size()); 
266                    roots.addAll(ser);
267                    if ( roots.size() == a.degree(0) ) {
268                        work = false;
269                        break;
270                    }
271    
272                    cp = (Complex<C>[]) copyOfComplex(rect.corners, 4);
273                    cp[0] = new Complex<C>(cr, center.getRe(), cp[0].getIm());
274                    cp[1] = center;
275                    cp[2] = new Complex<C>(cr, cp[2].getRe(), center.getIm());
276                    // cp[3] fix
277                    Rectangle<C> ne = new Rectangle<C>(cp);
278                    //System.out.println("ne = " + ne); 
279                    List<Rectangle<C>> ner = complexRoots(ne, a);
280                    //System.out.println("#ner = " + ner.size()); 
281                    roots.addAll(ner);
282                    work = false;
283                } catch (InvalidBoundaryException e) {
284                    // repeat with new center
285                    delta = delta.sum(delta.multiply(eps)); // distort
286                    //System.out.println("new delta = " + toDecimal(delta)); 
287                    eps = eps.sum(eps.multiply(cr.getIMAG()));
288                }
289            }
290            return roots;
291        }
292    
293    
294        /**
295         * Invariant rectangle for algebraic number.
296         * @param rect root isolating rectangle for f which contains exactly one root.
297         * @param f univariate polynomial, non-zero.
298         * @param g univariate polynomial, gcd(f,g) == 1.
299         * @return v a new rectangle contained in rect such that g(w) != 0 for w in v.
300         */
301        @Override
302        public Rectangle<C> invariantRectangle(Rectangle<C> rect, 
303                                               GenPolynomial<Complex<C>> f, 
304                                               GenPolynomial<Complex<C>> g) 
305                            throws InvalidBoundaryException {
306            Rectangle<C> v = rect;
307            if (g == null || g.isZERO()) {
308                return v;
309            }
310            if (g.isConstant()) {
311                return v;
312            }
313            if (f == null || f.isZERO() || f.isConstant()) { // ?
314                return v;
315            }
316            BigRational len = v.rationalLength();
317            BigRational half = new BigRational(1,2);
318            while (true) {
319                long n = windingNumber(v, g);
320                //System.out.println("n = " + n);
321                if (n < 0) { // can this happen?
322                    throw new RuntimeException("negative winding number " + n);
323                }
324                if (n == 0) {
325                    return v;
326                }
327                len = len.multiply(half);
328                Rectangle<C> v1 = v;
329                v = complexRootRefinement(v,f,len);
330                if ( v.equals(v1) ) {
331                    //System.out.println("len = " + len);
332                    if ( !f.gcd(g).isONE() ) {
333                        System.out.println("f.gcd(g) = " + f.gcd(g));
334                        throw new RuntimeException("no convergence " + v);
335                    }
336                    //break; // no convergence
337                }
338            }
339            //return v;
340        }
341    
342    }