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