_Rational

it.grypho.scala.leonardo.core._Rational
See the_Rational companion class
object _Rational

Companion for _Rational: smart factories, constants, and the shared reduction step.

Attributes

Companion
class
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
_Rational.type

Members list

Value members

Concrete methods

def apply(n: BigInt): _Rational

Builds a rational from an integer.

Builds a rational from an integer.

Value parameters

n

the integer value

Attributes

Returns

n/1

def apply(n: Int): _Rational

Builds a rational from an Int.

Builds a rational from an Int.

Value parameters

n

the integer value

Attributes

Returns

n/1

def containsExact(e: _Expression): Boolean

Whether e carries an exact value anywhere in it.

Whether e carries an exact value anywhere in it.

The question every algorithm that synthesises a numeric constant has to answer. Float contagion is about the tier, not the value: _Number(-1) is an exact integer but an inexact-tier one, so multiplying an exact operand by it correctly demotes the result. That is right when the -1 came from user input and wrong when the algorithm invented it — Sum(lhs, Product(_Number(-1), rhs)) would silently pull an entire exactly-stated equation into Double before the solver ever saw it.

Value parameters

e

the expression to inspect

Attributes

Returns

true when any node is a _Rational

def fromApproximation(d: Double, digits: Int): Option[_Rational]

Brings a Double result of a non-closed operation back into the exact tier.

Brings a Double result of a non-closed operation back into the exact tier.

Used wherever the rationals are left and re-entered — fractional powers and the transcendental functions. The requested precision is capped at DoubleReliableDigits so the result claims no more accuracy than its source had; without that cap the "approximation" would faithfully reproduce the Double's dyadic expansion and call it thirty digits.

Value parameters

d

the computed value

digits

the working precision requested

Attributes

Returns

the rational approximation, or None when d is NaN or infinite

def fromDecimalString(s: String): Option[_Rational]

Converts a decimal literal to the exact rational it denotes.

Converts a decimal literal to the exact rational it denotes.

The counterpart of fromDouble, and the one the parser uses. The distinction is the whole reason exact mode has to live at parse time: fromDecimalString("0.1") is 1/10, while fromDouble(0.1) is the dyadic the hardware rounded it to. Only the first makes 0.1 + 0.2 == 0.3 come out exactly.

Exponents are handled exactly too, so 1e-320 is 1/10^320 rather than the Double denormal it would otherwise become.

Value parameters

s

the literal as written

Attributes

Returns

the exact rational, or None when s is not a decimal literal

def fromDouble(d: Double): Option[_Rational]

Converts a Double to the exact rational it denotes.

Converts a Double to the exact rational it denotes.

Every finite Double is a dyadic rational, so this conversion is lossless — it recovers the value the hardware actually holds, not the decimal literal that was written. fromDouble(0.1) is therefore 3602879701896397/36028797018963968, which is the honest answer and the reason a number that was already parsed as a Double cannot be repaired after the fact — hence the parser's own exact mode, and hence fromDecimalString beside this.

Value parameters

d

the value to convert

Attributes

Returns

the exact rational, or None when d is NaN or infinite

def literalLike(n: Int, like: _Expression): _Value

An integer constant in the same tier as like.

An integer constant in the same tier as like.

Use wherever an algorithm needs a constant of its own — a -1 to negate with, the 2 of a quadratic denominator — so that it joins the expression it is about to combine with rather than dragging it down a tier. The parser's literalInt is the parse-time counterpart of this.

Value parameters

like

an expression from the surrounding computation

n

the integer value

Attributes

Returns

a _Rational when like is exact, a _Number otherwise

def of(n: BigInt, d: BigInt, policy: GcdPolicy): Option[_Rational]

Builds a rational from a numerator and denominator.

Builds a rational from a numerator and denominator.

Returns None for a zero denominator rather than throwing, following the house rule that an undefined result leaves the caller symbolic instead of propagating an infinity (the same contract as SpecialFunctions' kernels).

Value parameters

d

the denominator

n

the numerator

policy

the reduction policy applied to the result

Attributes

Returns

the rational n/d, or None when d is zero

def thresholdFor(digits: Int): GcdPolicy

The reduction bound appropriate to a given working precision.

The reduction bound appropriate to a given working precision.

8 · digits is a little over twice the digits · log2 10 ≈ 3.32 · digits bits one re-approximated operand occupies, which is what a product of two of them needs before reduction is worth paying for. Floored at DefaultThresholdBits so a low working precision does not collapse the policy back onto GcdPolicy.Eager.

Value parameters

digits

the working precision in decimal digits

Attributes

Returns

a GcdPolicy.Threshold sized for that precision

Concrete fields

The reduction policy chosen by the gcd-policy benchmark, and what a caller with no opinion should get.

The reduction policy chosen by the gcd-policy benchmark, and what a caller with no opinion should get.

GcdPolicy.Lazy was rejected on the exact arm — where nothing re-approximates, which is the normal mode for the closed operations (Sum, Product, Ratio, integer Power, and all of matrix) rather than a synthetic control. There it reached 2.5 million bits and 2.65 GB on an 8×8 Hilbert solve against Eager's 30 bits, an 83 000× operand-size ratio. Threshold keeps Lazy's cheapness wherever re-approximation is already bounding growth, and behaves as a guard where it is not.

Attributes

Default bit-length bound for GcdPolicy.Threshold, chosen by the gcd-policy benchmark.

Default bit-length bound for GcdPolicy.Threshold, chosen by the gcd-policy benchmark.

It has to clear the operand size a working precision implies, or the policy fires on every operation and is GcdPolicy.Eager under another name — the benchmark shows this directly, with a 64-bit bound at 30 working digits reproducing Eager's operand sizes exactly (105 / 30 bits) and a 256-bit bound reproducing Lazy's (186 / 75). A 30-digit value needs 30 · log2 10 ≈ 100 bits and a product of two reaches ~200, so 256 clears it with headroom. See thresholdFor for the scaling rule at other precisions.

Attributes

Significant decimal digits a Double actually carries.

Significant decimal digits a Double actually carries.

The ceiling on fromApproximation, and an honest one: an operation that leaves the rationals — every transcendental, and any fractional power — is currently evaluated in Double and re-approximated, so asking for thirty digits of sin(1/3) would manufacture fifteen digits that are not there. Lifting this needs an arbitrary precision engine for the functions themselves (spire's Real); the working precision meanwhile bounds the arithmetic around them, which is where the cancellation lives.

Attributes

val MaxDisplayDigits: Int

Longest numerator or denominator, in decimal digits, still shown as a fraction.

Longest numerator or denominator, in decimal digits, still shown as a fraction.

Beyond this a _Rational displays as a decimal instead. The bound exists because the two things a rational can be are very different to read: 1/3 is clearer as a fraction than as 0.33333, while a 30-digit rational approximation of pi is a 31-over-31-digit wall that tells the reader nothing. Display only — Session serializes the exact fraction regardless, so :save never loses the value.

Attributes

val MaxExactPowerBits: Long

Size ceiling, in bits, for the result of an exact integer power.

Size ceiling, in bits, for the result of an exact integer power.

2^20 bits is roughly a 315 000-digit number: far past anything a session needs, and still computed in milliseconds. The cap exists for the same reason scalar.MaxExactFactorial does — "computable in principle" is not "should be attempted". BigInt.pow has no bound of its own, so in exact mode a typo such as (123/7)^2000000000 asks for a ~1.4e10-bit operand and hangs or OOMs the session rather than answering. The estimate is maxBitLength × |k|, which needs no allocation to compute; past it _Rational.pow returns None and the expression stays symbolic, the give-up rule used throughout the library.

Attributes

val One: _Rational

The multiplicative identity, 1/1.

The multiplicative identity, 1/1.

Attributes

The additive identity, 0/1.

The additive identity, 0/1.

Attributes