All Downloads are FREE. Search and download functionalities are using the official Maven repository.

spire.math.poly.RootIsolator.scala Maven / Gradle / Ivy

package spire.math
package poly

import spire.std.bigInt._

/**
 * A type class for retreiving isolated roots.
 */
sealed trait RootIsolator[A] {

  /**
   * Isolates the roots of the [[Rational]] polynomial `poly`. This returns a
   * sequence of intervals that each contain a single root of `poly`. A root
   * will appear in the sequence as many times as its multiplicity in the
   * polynomial. Other than this, all root intervals are disjoint and are
   * either open on both ends or is a single point.
   */
  def isolateRoots(poly: Polynomial[A]): Vector[Interval[Rational]]
}

object RootIsolator {

  implicit val RationalRootIsolator: RootIsolator[Rational] = new RootIsolator[Rational] {
    def isolateRoots(poly: Polynomial[Rational]): Vector[Interval[Rational]] =
      VAS(Roots.removeFractions(poly))
  }

  implicit val BigDecimalRootIsolator: RootIsolator[BigDecimal] = new RootIsolator[BigDecimal] {
    def isolateRoots(poly: Polynomial[BigDecimal]): Vector[Interval[Rational]] =
      VAS(Roots.removeDecimal(poly))
  }

  implicit val BigIntRootIsolator: RootIsolator[BigInt] = new RootIsolator[BigInt] {
    def isolateRoots(poly: Polynomial[BigInt]): Vector[Interval[Rational]] =
      VAS(poly)
  }

  /**
   * An implementation of the VAS real root isolation algorithm.
   *
   * See "A Comparative Study of Two Real Root Isolation Methods" for the paper
   * that originally presented the method implemented here, and "Complexity
   * Analysis of Algorithms in Algebraic Computation" by Vikram Sharma which
   * goes into greater detail.
   */
  private final def VAS(poly: Polynomial[BigInt]): Vector[Interval[Rational]] = {
    val x = Polynomial.x[BigInt]
    val one = Polynomial.one[BigInt]

    case class TransformedPoly(p: Polynomial[BigInt], a: BigInt, b: BigInt, c: BigInt, d: BigInt)

    // Find all roots recursively that are between (0, 1) and (1, infinity).
    def split1(p: Polynomial[BigInt], a: BigInt, b: BigInt, c: BigInt, d: BigInt): List[TransformedPoly] = {
      val r = p.compose(x + one)
      val rRoots = TransformedPoly(r, a, b + a, c, d + c)
      if (r.signVariations < p.signVariations) {
        var l = p.reciprocal.compose(x + one)
        while (l(0) == 0)
          l = l.mapTerms { case Term(coeff, exp) => Term(coeff, exp - 1) }
        val lRoots = TransformedPoly(l, b, a + b, d, c + d)
        lRoots :: rRoots :: Nil
      } else {
        rRoots :: Nil
      }
    }

    // Isolate all positive roots in polynomial p.
    def rec(polys: List[TransformedPoly], acc: Vector[Interval[Rational]] = Vector.empty): Vector[Interval[Rational]] = polys match {
      case TransformedPoly(p, a, b, c, d) :: rest =>
        if (p(BigInt(0)) == BigInt(0)) {
          val p0 = p.mapTerms { case Term(coeff, exp) => Term(coeff, exp - 1) }
          rec(TransformedPoly(p0, a, b, c, d) :: rest, acc :+ Interval.point(Rational(b, d)))
        } else {
          p.signVariations match {
            case 0 => // No roots.
              rec(rest, acc)

            case 1 => // Isolated exactly 1 root.
              def ub: Rational = {
                val exp = Roots.upperBound(p)
                // This is an upper bound for p, but not for the initial poly.
                val ub0 =
                  if (exp >= 0) Rational(BigInt(1) << exp)
                  else Rational(1, BigInt(1) << -exp)
                // We map the upper bound for p back to a bound for the initial
                // polynomial by using the inverse Mobius transformation.
                (Rational(d) * ub0 - Rational(b)) / (Rational(-c) * ub0 + Rational(a))
              }
              val i0 = if (c == 0) ub else Rational(a, c)
              val i1 = if (d == 0) ub else Rational(b, d)
              if (i0 < i1) rec(rest, acc :+ Interval.open(i0, i1))
              else rec(rest, acc :+ Interval.open(i1, i0))

            case _ => // Exists 0 or 2 or more roots.
              val lb = Roots.lowerBound(p)
              if (lb < 0) {
                val more = split1(p, a, b, c, d)
                rec(more reverse_::: rest, acc)
              } else {
                val flr = BigInt(1) << lb
                val more = split1(p.compose(x + Polynomial.constant(flr)), a, b + a * flr, c, d + c * flr)
                rec(more reverse_::: rest, acc)
              }
          }
        }

      case Nil =>
        acc
    }

    if (poly.isConstant) {
      // A degenerate case we cannot handle.
      Vector.empty
    } else {
      val zeroInterval = Interval.point(Rational.zero)
      val posRoots = rec(TransformedPoly(poly, 1, 0, 0, 1) :: Nil)
      val negRoots = rec(TransformedPoly(poly.flip, 1, 0, 0, 1) :: Nil).map(-_).filter(_ != zeroInterval)
      negRoots ++ posRoots
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy