001/* 002 * $Id: ComplexRootsSturm.java 5590 2016-08-17 21:36:43Z 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 static 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 //System.out.println("complexRootCount: rl = " + rl + ", il = " + il); 155 // only linear polynomials have zero length intervals 156 if (rl.isZERO() && il.isZERO()) { 157 Complex<C> e = PolyUtil.<Complex<C>> evaluateMain(a.ring.coFac, a, rect.getSW()); 158 if (e.isZERO()) { 159 return 1; 160 } 161 return 0; 162 } 163 if (rl.isZERO() || il.isZERO()) { 164 //RingFactory<C> cf = (RingFactory<C>) rl.factory(); 165 //GenPolynomialRing<C> rfac = new GenPolynomialRing<C>(cf,a.ring); 166 //cf = (RingFactory<C>) il.factory(); 167 //GenPolynomialRing<C> ifac = new GenPolynomialRing<C>(cf,a.ring); 168 //GenPolynomial<C> rp = PolyUtil.<C> realPartFromComplex(rfac, a); 169 //GenPolynomial<C> ip = PolyUtil.<C> imaginaryPartFromComplex(ifac, a); 170 //RealRoots<C> rr = new RealRootsSturm<C>(); 171 if (rl.isZERO()) { 172 //logger.info("lengthReal == 0: " + rect); 173 //Complex<C> r = rect.getSW(); 174 //r = new Complex<C>(r.ring,r.getRe()/*,0*/); 175 //Complex<C> e = PolyUtil.<Complex<C>> evaluateMain(a.ring.coFac, a, r); 176 //logger.info("a(re(rect)): " + e); 177 //if ( !e.getRe().isZERO() ) { 178 // return 0; 179 //} 180 //C ev = PolyUtil.<C> evaluateMain(rp.ring.coFac, rp, rl); 181 //logger.info("re(a)(re(rect)): " + ev); 182 //Interval<C> iv = new Interval<C>(rect.getSW().getIm(),rect.getNE().getIm()); 183 //logger.info("iv: " + iv); 184 //long ic = rr.realRootCount(iv,ip); 185 //logger.info("ic: " + ic); 186 187 Complex<C> sw = rect.getSW(); 188 Complex<C> ne = rect.getNE(); 189 C delta = sw.ring.ring.getONE(); //parse("1"); // works since linear polynomial 190 Complex<C> cd = new Complex<C>(sw.ring, delta/*, 0*/); 191 sw = sw.subtract(cd); 192 ne = ne.sum(cd); 193 rect = rect.exchangeSW(sw); 194 rect = rect.exchangeNE(ne); 195 logger.info("new rectangle: " + rect.toScript()); 196 } 197 if (il.isZERO()) { 198 //logger.info("lengthImag == 0: " + rect); 199 //Interval<C> rv = new Interval<C>(rect.getSW().getRe(),rect.getNE().getRe()); 200 //logger.info("rv: " + rv); 201 //long rc = rr.realRootCount(rv,rp); 202 //logger.info("rc: " + rc); 203 204 Complex<C> sw = rect.getSW(); 205 Complex<C> ne = rect.getNE(); 206 C delta = sw.ring.ring.getONE(); //parse("1"); // works since linear polynomial 207 Complex<C> cd = new Complex<C>(sw.ring, sw.ring.ring.getZERO(), delta); 208 sw = sw.subtract(cd); 209 ne = ne.sum(cd); 210 rect = rect.exchangeSW(sw); 211 rect = rect.exchangeNE(ne); 212 logger.info("new rectangle: " + rect.toScript()); 213 } 214 } 215 long wn = windingNumber(rect, a); 216 //System.out.println("complexRootCount: wn = " + wn); 217 return wn; 218 } 219 220 221 /** 222 * Winding number of complex function A on rectangle. 223 * @param rect rectangle. 224 * @param A univariate complex polynomial. 225 * @return winding number of A arround rect. 226 */ 227 public long windingNumber(Rectangle<C> rect, GenPolynomial<Complex<C>> A) 228 throws InvalidBoundaryException { 229 Boundary<C> bound = new Boundary<C>(rect, A); // throws InvalidBoundaryException 230 ComplexRing<C> cr = (ComplexRing<C>) A.ring.coFac; 231 RingFactory<C> cf = cr.ring; 232 C zero = cf.getZERO(); 233 C one = cf.getONE(); 234 long ix = 0L; 235 for (int i = 0; i < 4; i++) { 236 long ci = indexOfCauchy(zero, one, bound.getRealPart(i), bound.getImagPart(i)); 237 //System.out.println("ci[" + i + "," + (i + 1) + "] = " + ci); 238 ix += ci; 239 } 240 if (ix % 2L != 0) { 241 throw new InvalidBoundaryException("odd winding number " + ix); 242 } 243 return ix / 2L; 244 } 245 246 247 /** 248 * List of complex roots of complex polynomial a on rectangle. 249 * @param rect rectangle. 250 * @param a univariate squarefree complex polynomial. 251 * @return list of complex roots. 252 */ 253 @SuppressWarnings({"cast","unchecked"}) 254 @Override 255 public List<Rectangle<C>> complexRoots(Rectangle<C> rect, GenPolynomial<Complex<C>> a) 256 throws InvalidBoundaryException { 257 ComplexRing<C> cr = (ComplexRing<C>) a.ring.coFac; 258 List<Rectangle<C>> roots = new ArrayList<Rectangle<C>>(); 259 if (a.isConstant() || a.isZERO()) { 260 return roots; 261 } 262 //System.out.println("rect = " + rect); 263 long n = windingNumber(rect, a); 264 if (n < 0) { // can this happen? 265 throw new RuntimeException("negative winding number " + n); 266 //System.out.println("negative winding number " + n); 267 //return roots; 268 } 269 if (n == 0) { 270 return roots; 271 } 272 if (n == 1) { 273 //not ok: rect = excludeZero(rect, a); 274 roots.add(rect); 275 return roots; 276 } 277 Complex<C> eps = cr.fromInteger(1); 278 eps = eps.divide(cr.fromInteger(1000)); // 1/1000 279 //System.out.println("eps = " + eps); 280 //System.out.println("rect = " + rect); 281 // construct new center 282 Complex<C> delta = rect.corners[3].subtract(rect.corners[1]); 283 delta = delta.divide(cr.fromInteger(2)); 284 //System.out.println("delta = " + delta); 285 boolean work = true; 286 while (work) { 287 Complex<C> center = rect.corners[1].sum(delta); 288 //System.out.println("center = " + toDecimal(center)); 289 if (debug) { 290 logger.info("new center = " + center); 291 } 292 try { 293 Complex<C>[] cp = (Complex<C>[]) copyOfComplex(rect.corners, 4); 294 // (Complex<C>[]) new Complex[4]; cp[0] = rect.corners[0]; 295 // cp[0] fix 296 cp[1] = new Complex<C>(cr, cp[1].getRe(), center.getIm()); 297 cp[2] = center; 298 cp[3] = new Complex<C>(cr, center.getRe(), cp[3].getIm()); 299 Rectangle<C> nw = new Rectangle<C>(cp); 300 //System.out.println("nw = " + nw); 301 List<Rectangle<C>> nwr = complexRoots(nw, a); 302 //System.out.println("#nwr = " + nwr.size()); 303 roots.addAll(nwr); 304 if (roots.size() == a.degree(0)) { 305 work = false; 306 break; 307 } 308 309 cp = (Complex<C>[]) copyOfComplex(rect.corners, 4); 310 cp[0] = new Complex<C>(cr, cp[0].getRe(), center.getIm()); 311 // cp[1] fix 312 cp[2] = new Complex<C>(cr, center.getRe(), cp[2].getIm()); 313 cp[3] = center; 314 Rectangle<C> sw = new Rectangle<C>(cp); 315 //System.out.println("sw = " + sw); 316 List<Rectangle<C>> swr = complexRoots(sw, a); 317 //System.out.println("#swr = " + swr.size()); 318 roots.addAll(swr); 319 if (roots.size() == a.degree(0)) { 320 work = false; 321 break; 322 } 323 324 cp = (Complex<C>[]) copyOfComplex(rect.corners, 4); 325 cp[0] = center; 326 cp[1] = new Complex<C>(cr, center.getRe(), cp[1].getIm()); 327 // cp[2] fix 328 cp[3] = new Complex<C>(cr, cp[3].getRe(), center.getIm()); 329 Rectangle<C> se = new Rectangle<C>(cp); 330 //System.out.println("se = " + se); 331 List<Rectangle<C>> ser = complexRoots(se, a); 332 //System.out.println("#ser = " + ser.size()); 333 roots.addAll(ser); 334 if (roots.size() == a.degree(0)) { 335 work = false; 336 break; 337 } 338 339 cp = (Complex<C>[]) copyOfComplex(rect.corners, 4); 340 cp[0] = new Complex<C>(cr, center.getRe(), cp[0].getIm()); 341 cp[1] = center; 342 cp[2] = new Complex<C>(cr, cp[2].getRe(), center.getIm()); 343 // cp[3] fix 344 Rectangle<C> ne = new Rectangle<C>(cp); 345 //System.out.println("ne = " + ne); 346 List<Rectangle<C>> ner = complexRoots(ne, a); 347 //System.out.println("#ner = " + ner.size()); 348 roots.addAll(ner); 349 work = false; 350 } catch (InvalidBoundaryException e) { 351 // repeat with new center 352 delta = delta.sum(delta.multiply(eps)); // distort 353 //System.out.println("new delta = " + toDecimal(delta)); 354 eps = eps.sum(eps.multiply(cr.getIMAG())); 355 } 356 } 357 return roots; 358 } 359 360 361 /** 362 * Invariant rectangle for algebraic number. 363 * @param rect root isolating rectangle for f which contains exactly one 364 * root. 365 * @param f univariate polynomial, non-zero. 366 * @param g univariate polynomial, gcd(f,g) == 1. 367 * @return v a new rectangle contained in rect such that g(w) != 0 for w in 368 * v. 369 */ 370 @Override 371 public Rectangle<C> invariantRectangle(Rectangle<C> rect, GenPolynomial<Complex<C>> f, 372 GenPolynomial<Complex<C>> g) throws InvalidBoundaryException { 373 Rectangle<C> v = rect; 374 if (g == null || g.isZERO()) { 375 return v; 376 } 377 if (g.isConstant()) { 378 return v; 379 } 380 if (f == null || f.isZERO() || f.isConstant()) { // ? 381 return v; 382 } 383 BigRational len = v.rationalLength(); 384 BigRational half = new BigRational(1, 2); 385 while (true) { 386 long n = windingNumber(v, g); 387 //System.out.println("n = " + n); 388 if (n < 0) { // can this happen? 389 throw new RuntimeException("negative winding number " + n); 390 } 391 if (n == 0) { 392 return v; 393 } 394 len = len.multiply(half); 395 Rectangle<C> v1 = v; 396 v = complexRootRefinement(v, f, len); 397 if (v.equals(v1)) { 398 //System.out.println("len = " + len); 399 if (!f.gcd(g).isONE()) { 400 System.out.println("f.gcd(g) = " + f.gcd(g)); 401 throw new RuntimeException("no convergence " + v); 402 } 403 //break; // no convergence 404 } 405 } 406 //return v; 407 } 408 409 410 /** 411 * Exclude zero. If an axis intersects with the rectangle, it is shrinked 412 * to exclude the axis. 413 * Not used. 414 * @param rect root isolating rectangle for f which contains exactly one 415 * root. 416 * @return a new rectangle r such that re(r) < 0 or (re)r > 0 and 417 * im(r) < 0 or (im)r > 0. 418 */ 419 public Rectangle<C> excludeZero(Rectangle<C> rect, GenPolynomial<Complex<C>> f) 420 throws InvalidBoundaryException { 421 if (f == null || f.isZERO()) { 422 return rect; 423 } 424 System.out.println("\nexcludeZero: rect = " + rect + ", f = " + f); 425 Complex<C> zero = f.ring.coFac.getZERO(); 426 ComplexRing<C> cr = zero.ring; 427 Complex<C> sw = rect.getSW(); 428 Complex<C> ne = rect.getNE(); 429 Interval<C> ir = new Interval<C>(sw.getRe(), ne.getRe()); 430 Interval<C> ii = new Interval<C>(sw.getIm(), ne.getIm()); 431 System.out.println("intervals, ir = " + ir + ", ii = " + ii); 432 if (!(ir.contains(zero.getRe()) || ii.contains(zero.getIm()))) { 433 // !rect.contains(zero) not correct 434 return rect; 435 } 436 //System.out.println("contains: ir = " + ir.contains(zero.getRe()) + ", ii = " + ii.contains(zero.getIm()) ); 437 Rectangle<C> rn = rect; 438 // shrink real part 439 if (ir.contains(zero.getRe())) { 440 Complex<C> sw0 = new Complex<C>(cr, zero.getRe(), sw.getIm()); 441 Complex<C> ne0 = new Complex<C>(cr, zero.getRe(), ne.getIm()); 442 Rectangle<C> rl = new Rectangle<C>(sw, ne0); 443 Rectangle<C> rr = new Rectangle<C>(sw0, ne); 444 System.out.println("rectangle, rl = " + rl + ", rr = " + rr); 445 if (complexRootCount(rr, f) == 1) { 446 rn = rr; 447 } else { // complexRootCount(rl,f) == 1 448 rn = rl; 449 } 450 System.out.println("rectangle, real = " + rn); 451 } 452 // shrink imaginary part 453 sw = rn.getSW(); 454 ne = rn.getNE(); 455 ii = new Interval<C>(sw.getIm(), ne.getIm()); 456 System.out.println("interval, ii = " + ii); 457 if (ii.contains(zero.getIm())) { 458 Complex<C> sw1 = new Complex<C>(cr, sw.getRe(), zero.getIm()); 459 Complex<C> ne1 = new Complex<C>(cr, ne.getRe(), zero.getIm()); 460 Rectangle<C> iu = new Rectangle<C>(sw1, ne); 461 Rectangle<C> il = new Rectangle<C>(sw, ne1); 462 System.out.println("rectangle, il = " + il + ", iu = " + iu); 463 if (complexRootCount(il, f) == 1) { 464 rn = il;; 465 } else { // complexRootCount(iu,f) == 1 466 rn = iu; 467 } 468 System.out.println("rectangle, imag = " + rn); 469 } 470 return rn; 471 } 472 473}