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