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