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