001/* 002 * $Id$ 003 */ 004 005package edu.jas.root; 006 007 008import java.util.ArrayList; 009import java.util.List; 010 011import org.apache.logging.log4j.LogManager; 012import org.apache.logging.log4j.Logger; 013 014import edu.jas.arith.BigRational; 015import edu.jas.arith.Rational; 016import edu.jas.poly.ExpVector; 017import edu.jas.poly.GenPolynomial; 018import edu.jas.poly.GenPolynomialRing; 019import edu.jas.poly.PolyUtil; 020import edu.jas.structure.RingElem; 021import edu.jas.structure.RingFactory; 022 023 024/** 025 * Real root isolation using Sturm sequences. 026 * @param <C> coefficient type. 027 * @author Heinz Kredel 028 */ 029public class RealRootsSturm<C extends RingElem<C> & Rational> extends RealRootsAbstract<C> { 030 031 032 private static final Logger logger = LogManager.getLogger(RealRootsSturm.class); 033 034 035 private static final boolean debug = logger.isDebugEnabled(); 036 037 038 /** 039 * Sturm sequence. 040 * @param f univariate polynomial. 041 * @return a Sturm sequence for f. 042 */ 043 public List<GenPolynomial<C>> sturmSequence(GenPolynomial<C> f) { 044 List<GenPolynomial<C>> S = new ArrayList<GenPolynomial<C>>(); 045 if (f == null || f.isZERO()) { 046 return S; 047 } 048 if (f.isConstant()) { 049 S.add(f.monic()); 050 return S; 051 } 052 GenPolynomial<C> F = f; 053 S.add(F); 054 GenPolynomial<C> G = PolyUtil.<C> baseDeriviative(f); 055 while (!G.isZERO()) { 056 GenPolynomial<C> r = F.remainder(G); 057 F = G; 058 G = r.negate(); 059 S.add(F/*.monic()*/); 060 } 061 //System.out.println("F = " + F); 062 if (F.isConstant()) { 063 return S; 064 } 065 // make squarefree 066 List<GenPolynomial<C>> Sp = new ArrayList<GenPolynomial<C>>(S.size()); 067 for (GenPolynomial<C> p : S) { 068 p = p.divide(F); 069 Sp.add(p); 070 } 071 return Sp; 072 } 073 074 075 /** 076 * Isolating intervals for the real roots. 077 * @param f univariate polynomial. 078 * @return a list of isolating intervals for the real roots of f. 079 */ 080 @SuppressWarnings("cast") 081 @Override 082 public List<Interval<C>> realRoots(GenPolynomial<C> f) { 083 List<Interval<C>> R = new ArrayList<Interval<C>>(); 084 if (f == null) { 085 return R; 086 } 087 GenPolynomialRing<C> pfac = f.ring; 088 if (f.isZERO()) { 089 C z = pfac.coFac.getZERO(); 090 R.add(new Interval<C>(z)); 091 return R; 092 } 093 // check trailing degree 094 ExpVector et = f.trailingExpVector(); 095 if (!et.isZERO()) { 096 GenPolynomial<C> tr = pfac.valueOf(et); 097 if (logger.isInfoEnabled()) { 098 logger.info("trailing term = " + tr); 099 } 100 f = PolyUtil.<C> basePseudoDivide(f, tr); 101 R.add(new Interval<C>(pfac.coFac.getZERO())); 102 } 103 if (f.isConstant()) { 104 return R; 105 } 106 if (f.degree(0) == 1L) { 107 C z = f.monic().trailingBaseCoefficient().negate(); 108 R.add(new Interval<C>(z)); 109 return R; 110 } 111 //if (f.degree(0) == 2L) ... 112 GenPolynomial<C> F = f; 113 C M = realRootBound(F); // M != 0, since >= 2 114 Interval<C> iv = new Interval<C>(M.negate(), M); 115 //System.out.println("iv = " + iv); 116 List<GenPolynomial<C>> S = sturmSequence(F); 117 //System.out.println("S = " + S); 118 //System.out.println("f_S = " + S.get(0)); 119 List<Interval<C>> Rp = realRoots(iv, S); 120 if (logger.isInfoEnabled() && !(((Object) f.ring.coFac) instanceof BigRational)) { 121 //logger.info("realRoots bound: " + iv); 122 logger.info("realRoots: " + Rp); 123 } 124 R.addAll(Rp); 125 return R; 126 } 127 128 129 /** 130 * Isolating intervals for the real roots. 131 * @param iv interval with f(left) * f(right) != 0. 132 * @param S sturm sequence for f and I. 133 * @return a list of isolating intervals for the real roots of f in I. 134 */ 135 public List<Interval<C>> realRoots(Interval<C> iv, List<GenPolynomial<C>> S) { 136 List<Interval<C>> R = new ArrayList<Interval<C>>(); 137 GenPolynomial<C> f = S.get(0); // squarefree part 138 if (f.isZERO()) { 139 C z = f.leadingBaseCoefficient(); 140 if (!iv.contains(z)) { 141 throw new IllegalArgumentException( 142 "root not in interval: f = " + f + ", iv = " + iv + ", z = " + z); 143 } 144 Interval<C> iv1 = new Interval<C>(z); 145 R.add(iv1); 146 return R; 147 } 148 if (f.isConstant()) { 149 return R; 150 //throw new IllegalArgumentException("f has no root: f = " + f + ", iv = " + iv); 151 } 152 if (f.degree(0) == 1L) { 153 C z = f.monic().trailingBaseCoefficient().negate(); 154 if (!iv.contains(z)) { 155 return R; 156 //throw new IllegalArgumentException("root not in interval: f = " + f + ", iv = " + iv + ", z = " + z); 157 } 158 Interval<C> iv1 = new Interval<C>(z); 159 R.add(iv1); 160 return R; 161 } 162 //System.out.println("iv = " + iv); 163 // check sign variations at interval bounds 164 long v = realRootCount(iv, S); 165 //System.out.println("v = " + v); 166 if (v == 0) { 167 return R; 168 } 169 if (v == 1) { 170 iv = excludeZero(iv, S); 171 R.add(iv); 172 return R; 173 } 174 // now v > 1 175 // bi-sect interval, such that f(c) != 0 176 C c = bisectionPoint(iv, f); 177 //System.out.println("c = " + c); 178 // recursion on both sub-intervals 179 Interval<C> iv1 = new Interval<C>(iv.left, c); 180 Interval<C> iv2 = new Interval<C>(c, iv.right); 181 List<Interval<C>> R1 = realRoots(iv1, S); 182 //System.out.println("R1 = " + R1); 183 if (debug) { 184 logger.info("R1 = " + R1); 185 } 186 List<Interval<C>> R2 = realRoots(iv2, S); 187 //System.out.println("R2 = " + R2); 188 if (debug) { 189 logger.info("R2 = " + R2); 190 } 191 192 // refine isolating intervals if adjacent 193 if (R1.isEmpty()) { 194 R.addAll(R2); 195 return R; 196 } 197 if (R2.isEmpty()) { 198 R.addAll(R1); 199 return R; 200 } 201 iv1 = R1.get(R1.size() - 1); // last 202 iv2 = R2.get(0); // first 203 if (iv1.right.compareTo(iv2.left) < 0) { 204 R.addAll(R1); 205 R.addAll(R2); 206 return R; 207 } 208 // now iv1.right == iv2.left 209 //System.out.println("iv1 = " + iv1); 210 //System.out.println("iv2 = " + iv2); 211 R1.remove(iv1); 212 R2.remove(iv2); 213 while (iv1.right.equals(iv2.left)) { 214 C d1 = bisectionPoint(iv1, f); 215 C d2 = bisectionPoint(iv2, f); 216 Interval<C> iv11 = new Interval<C>(iv1.left, d1); 217 Interval<C> iv12 = new Interval<C>(d1, iv1.right); 218 Interval<C> iv21 = new Interval<C>(iv2.left, d2); 219 Interval<C> iv22 = new Interval<C>(d2, iv2.right); 220 221 boolean b11 = signChange(iv11, f); 222 boolean b12 = signChange(iv12, f); // TODO check unnecessary 223 //boolean b21 = signChange(iv21, f); // TODO check unused or unnecessary 224 boolean b22 = signChange(iv22, f); 225 if (b11) { 226 iv1 = iv11; 227 if (b22) { 228 iv2 = iv22; 229 } else { 230 iv2 = iv21; 231 } 232 break; // done, refine 233 } 234 if (b22) { 235 iv2 = iv22; 236 if (b12) { 237 iv1 = iv12; 238 } else { 239 iv1 = iv11; 240 } 241 break; // done, refine 242 } 243 iv1 = iv12; 244 iv2 = iv21; 245 //System.out.println("iv1 = " + iv1); 246 //System.out.println("iv2 = " + iv2); 247 } 248 R.addAll(R1); 249 R.add(iv1); 250 R.add(iv2); 251 R.addAll(R2); 252 return R; 253 } 254 255 256 /** 257 * Number of real roots in interval. 258 * @param iv interval with f(left) * f(right) != 0. 259 * @param S sturm sequence for f and I. 260 * @return number of real roots of f in I. 261 */ 262 public long realRootCount(Interval<C> iv, List<GenPolynomial<C>> S) { 263 // check sign variations at interval bounds 264 GenPolynomial<C> f = S.get(0); // squarefree part 265 //System.out.println("iv = " + iv); 266 RingFactory<C> cfac = f.ring.coFac; 267 List<C> l = PolyUtil.<C> evaluateMain(cfac, S, iv.left); 268 List<C> r = PolyUtil.<C> evaluateMain(cfac, S, iv.right); 269 long v = RootUtil.<C> signVar(l) - RootUtil.<C> signVar(r); 270 //System.out.println("v = " + v); 271 if (v < 0L) { 272 v = -v; 273 } 274 return v; 275 } 276 277 278 /** 279 * Number of real roots in interval. 280 * @param iv interval with f(left) * f(right) != 0. 281 * @param f univariate polynomial. 282 * @return number of real roots of f in I. 283 */ 284 @Override 285 public long realRootCount(Interval<C> iv, GenPolynomial<C> f) { 286 if (f == null || f.isConstant()) { // ? 287 return 0L; 288 } 289 if (f.isZERO()) { 290 C z = f.leadingBaseCoefficient(); 291 if (!iv.contains(z)) { 292 return 0L; 293 } 294 return 1L; 295 } 296 List<GenPolynomial<C>> S = sturmSequence(f); 297 return realRootCount(iv, S); 298 } 299 300 301 /** 302 * Invariant interval for algebraic number sign. 303 * @param iv root isolating interval for f, with f(left) * f(right) < 0. 304 * @param f univariate polynomial, non-zero. 305 * @param g univariate polynomial, gcd(f,g) == 1. 306 * @return v with v a new interval contained in iv such that g(w) != 0 for w 307 * in v. 308 */ 309 @Override 310 public Interval<C> invariantSignInterval(Interval<C> iv, GenPolynomial<C> f, GenPolynomial<C> g) { 311 Interval<C> v = iv; 312 if (g == null || g.isZERO()) { 313 //throw new IllegalArgumentException("g == 0"); 314 return v; 315 } 316 if (g.isConstant()) { 317 return v; 318 } 319 if (f == null || f.isZERO()) { // ? || f.isConstant() 320 throw new IllegalArgumentException("f == 0"); 321 //return v; 322 } 323 List<GenPolynomial<C>> Sg = sturmSequence(g.monic()); 324 Interval<C> ivp = invariantSignInterval(iv, f, Sg); 325 return ivp; 326 } 327 328 329 /** 330 * Invariant interval for algebraic number sign. 331 * @param iv root isolating interval for f, with f(left) * f(right) < 0. 332 * @param f univariate polynomial, non-zero. 333 * @param Sg Sturm sequence for (f,g), a univariate polynomial with gcd(f,g) == 334 * 1. 335 * @return v with v a new interval contained in iv such that g(w) != 0 for w 336 * in v. 337 */ 338 public Interval<C> invariantSignInterval(Interval<C> iv, GenPolynomial<C> f, List<GenPolynomial<C>> Sg) { 339 Interval<C> v = iv; 340 GenPolynomial<C> g = Sg.get(0); 341 if (g == null || g.isZERO()) { 342 return v; 343 } 344 if (g.isConstant()) { 345 return v; 346 } 347 if (f == null || f.isZERO()) { // ? || f.isConstant() 348 return v; 349 } 350 RingFactory<C> cfac = f.ring.coFac; 351 C two = cfac.fromInteger(2); 352 353 while (true) { 354 long n = realRootCount(v, Sg); 355 logger.debug("n = " + n); 356 if (n == 0) { 357 return v; 358 } 359 C c = v.left.sum(v.right); 360 c = c.divide(two); 361 Interval<C> im = new Interval<C>(c, v.right); 362 if (signChange(im, f)) { 363 v = im; 364 } else { 365 v = new Interval<C>(v.left, c); 366 } 367 } 368 // return v; 369 } 370 371 372 /** 373 * Exclude zero, old version. 374 * @param iv root isolating interval with f(left) * f(right) < 0. 375 * @param S sturm sequence for f and I. 376 * @return a new interval v such that v < 0 or v > 0. 377 */ 378 public Interval<C> excludeZeroOld(Interval<C> iv, List<GenPolynomial<C>> S) { 379 if (S == null || S.isEmpty()) { 380 return iv; 381 } 382 C zero = S.get(0).ring.coFac.getZERO(); 383 if (!iv.contains(zero)) { 384 return iv; 385 } 386 Interval<C> vn = new Interval<C>(iv.left, zero); 387 if (realRootCount(vn,S) == 1) { 388 return vn; 389 } 390 vn = new Interval<C>(zero, iv.right); 391 return vn; 392 } 393 394 395 /** 396 * Exclude zero v2. 397 * @param iv root isolating interval with f(left) * f(right) < 0. 398 * @param S sturm sequence for f and I. 399 * @return a new interval v such that v < 0 or v > 0 or v == 0. 400 */ 401 public Interval<C> excludeZero(Interval<C> iv, List<GenPolynomial<C>> S) { 402 if (S == null || S.isEmpty()) { 403 return iv; 404 } 405 GenPolynomial<C> f = S.get(0); 406 C zero = f.ring.coFac.getZERO(); 407 if (!iv.contains(zero)) { // left <= 0 <= right 408 return iv; 409 } 410 if (iv.left.isZERO() && iv.right.isZERO()) { // (0, 0) 411 return iv; 412 } 413 C m = realMinimalRootBound(f); 414 Interval<C> vi = iv; 415 Interval<C> vn; 416 if (vi.left.isZERO()) { 417 vi = new Interval<C>(m, vi.right); 418 } else if (vi.right.isZERO()) { 419 vi = new Interval<C>(vi.left, m.negate()); 420 } 421 vn = new Interval<C>(vi.left, m.negate()); // l != 0 422 if (realRootCount(vn, S) == 1) { 423 return vn; 424 } 425 vn = new Interval<C>(m, vi.right); // r != 0 426 if (realRootCount(vn, S) == 1) { 427 return vn; 428 } 429 vn = new Interval<C>(zero); 430 logger.warn("interval is zero: iv = " + iv + ", trail = " + f.trailingExpVector().degree() 431 + ", vi = " + vi + ", vn = " + vn); 432 return vn; 433 } 434 435}