Stéphane Laurent 2024-07-26
Fractions of multivariate polynomials with rational coefficients.
The qspray package allows arithmetic (and more) on multivariate polynomials with rational coefficients. Based on this one, the ratioOfQsprays package allows to manipulate fractions of multivariate polynomials with rational coefficients.
These notes about the ratioOfQsprays package assume that the reader is a bit familiar with the qspray package.
ratioOfQsprays
A ratioOfQsprays
object represents a fraction of two
multivariate polynomial with rational coefficients. Such polynomials are
represented by qspray
objects. The easiest way to create a
ratioOfQsprays
is to introduce the variables of the
polynomials with the qlone
function (from the
qspray package), and then to build a
qspray
numerator and a qspray
denominator with
the arithmetic operations. For example:
library(ratioOfQsprays)
<- function(x1, x2, x3) {
f 2*x1^2 + x2*x3) / (4*x1 - 3*x3 + 1)
(
}# variables:
<- qlone(1)
x1 <- qlone(2)
x2 <- qlone(3)
x3 # the 'ratioOfQsprays':
<- f(x1, x2, x3) )
( roq ## [ 1/2*x^2 + 1/4*y.z ] %//% [ x - 3/4*z + 1/4 ]
The denominator of a ratioOfQsprays
fraction of
polynomials is always monic. That means it is a polynomial
whose leading coefficient is 1.
Arithmetic on ratioOfQsprays
objects is available:
^2
roq## [ 1/4*x^4 + 1/4*x^2.y.z + 1/16*y^2.z^2 ] %//% [ x^2 - 3/2*x.z + 1/2*x + 9/16*z^2 - 3/8*z + 1/16 ]
- roq
roq ## [ 0 ]
1 / roq
## [ 2*x - 3/2*z + 1/2 ] %//% [ x^2 + 1/2*y.z ]
2*roq + (x2 + x3)/x1
## [ x^3 + 1/2*x.y.z + x.y + x.z - 3/4*y.z + 1/4*y - 3/4*z^2 + 1/4*z ] %//% [ x^2 - 3/4*x.z + 1/4*x ]
You don’t like my quotient bar %//%
? Be patient, we will
see how to change it later. I adopted this large quotient bar because it
is more easy to find it than a single slash /
in a
ratioOfQsprays
having a long expression.
Rational numbers and qspray
polynomials are coercible to
ratioOfQsprays
objects, and then you can also perform
arithmetic operations between a ratioOfQsprays
and such an
object:
2 * roq
## [ x^2 + 1/2*y.z ] %//% [ x - 3/4*z + 1/4 ]
"1/2" * roq
## [ 1/4*x^2 + 1/8*y.z ] %//% [ x - 3/4*z + 1/4 ]
+ gmp::as.bigq("7/3")
roq ## [ 1/2*x^2 + 7/3*x + 1/4*y.z - 7/4*z + 7/12 ] %//% [ x - 3/4*z + 1/4 ]
+ roq + x3^2
x1 ## [ 3/2*x^2 + x.z^2 - 3/4*x.z + 1/4*x + 1/4*y.z - 3/4*z^3 + 1/4*z^2 ] %//% [ x - 3/4*z + 1/4 ]
The result of an arithmetic operation is always an irreducible fraction. To perform this step, the C++ library CGAL is used to compute a greatest common divisor of the numerator and the denominator of the possibly non-reduced fraction resulting from the arithmetic operation, and then to divide both of them by this greatest common divisor. This is very efficient in general.
ratioOfQsprays
Use evalRatioOfQsprays
to evaluate a
ratioOfQsprays
. This function returns a bigq
number:
library(gmp) # rational numbers
<- c("4", "3", "2/5")
x evalRatioOfQsprays(roq, x)
## Big Rational ('bigq') :
## [1] 166/79
<- as.bigq(x)
x evalRatioOfQsprays(roq, x)
## Big Rational ('bigq') :
## [1] 166/79
f(x[1], x[2], x[3])
## Big Rational ('bigq') :
## [1] 166/79
It is also possible to substitute some values to only a subset of the
variables, with the help of the function
substituteRatioOfQsprays
. You have to indicate the
variables you don’t want to replace with NA
:
<- c(NA, "3", "2/5")
x substituteRatioOfQsprays(roq, x)
## [ 1/2*x^2 + 3/10 ] %//% [ x - 1/20 ]
<- as.bigq(x)
x f(x1, x[2], x[3])
## [ 1/2*x^2 + 3/10 ] %//% [ x - 1/20 ]
And it is possible to convert a ratioOfQsprays
to a
function which is evaluated by Ryacas:
<- as.function(roq)
fyac fyac("4", "3", "2/5") # = evalRatioOfQsprays(roq, c("4", "3", "2/5"))
## [1] "166/79"
Actually you can pass some literal variables to this function:
fyac("x", "3", "2/5") # = substituteRatioOfQsprays(roq, c(NA, "3", "2/5"))
## [1] "(2*(5*x^2+3))/(20*x-1)"
fyac("x", "y", "z") # = roq
## [1] "(z*y+2*x^2)/(4*x-3*z+1)"
fyac("x", "x", "x")
## [1] "(3*x^2)/(x+1)"
Complex numbers and allowed; the imaginary unit is denoted by
I
. See the Yacas
documentation for more information.
fyac("Sqrt(2)", "2 + 2*I", "3")
## [1] "Complex(10/(Sqrt(32)-8),6/(Sqrt(32)-8))"
You can get numerical approximations by setting the option
N=TRUE
in as.function
:
<- as.function(roq, N = TRUE)
fyacN fyacN("4", "3", "2/5")
## [1] 2.101266
fyacN("x", "3", "2/5")
## expression((2 * (5 * x^2 + 3))/(20 * x - 1))
fyacN("Sqrt(2)", "2 + 2*I", "3")
## [1] -4.267767-2.56066i
ratioOfQsprays
A couple of functions to query a ratioOfQsprays
are
available:
getNumerator(roq)
## 1/2*x^2 + 1/4*y.z
getDenominator(roq)
## x - 3/4*z + 1/4
numberOfVariables(roq)
## [1] 3
isConstant(roq)
## [1] FALSE
isConstant(roq / roq)
## [1] TRUE
isUnivariate(roq)
## [1] FALSE
isUnivariate(x1 / (x1^2 + 1))
## [1] TRUE
isPolynomial(roq)
## [1] FALSE
isPolynomial((x1^2 - x2^2) / (x1 - x2))
## [1] TRUE
ratioOfQsprays
As you have seen, the variables of roq
are denoted by
x
, y
, z
. This is the default way
of printing a ratioOfQsprays
which has no more than three
variables. If it has more than three variables, then they are denoted by
x1
, x2
, x3
, …:
<- qlone(4)
x4 / x4
roq ## [ 1/2*x1^2 + 1/4*x2.x3 ] %//% [ x1.x4 - 3/4*x3.x4 + 1/4*x4 ]
It is possible to control the way a ratioOfQsprays
is
printed. For example, let’s say you want to print roq
by
using a1
, a2
, a3
for the
variables and you want to change the symbol for the quotient bar:
showRatioOfQspraysOption(roq, "x") <- "a"
showRatioOfQspraysOption(roq, "quotientBar") <- " / "
roq## [ 1/2*a1^2 + 1/4*a2.a3 ] / [ a1 - 3/4*a3 + 1/4 ]
Now, if you perform an arithmetic operation between roq
at first position and an another ratioOfQsprays
,
these show options are passed to the result if possible:
+ (x1 + 1)/x2
roq ## [ 1/2*a1^2.a2 + a1^2 - 3/4*a1.a3 + 5/4*a1 + 1/4*a2^2.a3 - 3/4*a3 + 1/4 ] / [ a1.a2 - 3/4*a2.a3 + 1/4*a2 ]
If you perform an arithmetic operation between roq
and
an object coercible to a ratioOfQsprays
object but which is
not a ratioOfQsprays
object, such as a bigq
number or a qspray
object, the show options of
roq
are passed to the result, even if roq
is
not at the first position:
* roq
x1 ## [ 1/2*a1^3 + 1/4*a1.a2.a3 ] / [ a1 - 3/4*a3 + 1/4 ]
An obvious example of a situation in which it is not always possible to transfer the show options is when you use three letters for the variables, e.g.
showRatioOfQspraysOption(roq, "showQspray") <- showQsprayXYZ(c("A", "B", "C"))
roq## [ 1/2*A^2 + 1/4*B.C ] / [ A - 3/4*C + 1/4 ]
but then you add to roq
a ratioOfQsprays
containing the fourth variable:
+ x4/(x4 + 1)
roq ## [ 1/2*A1^2.A4 + 1/2*A1^2 + A1.A4 + 1/4*A2.A3.A4 + 1/4*A2.A3 - 3/4*A3.A4 + 1/4*A4 ] / [ A1.A4 + A1 - 3/4*A3.A4 - 3/4*A3 + 1/4*A4 + 1/4 ]
Obviously it is not possible to denote the resulting fraction of
polynomials with the letters A
, B
and
C
. The solution I adopted consists in taking the first of
these letters and to index it. The same method is used for the
qspray
polynomials.
ratioOfQsprays
Let’s take a ratioOfQsprays
fraction of polynomials:
<- function(x, y, z) {
f 2*x^2 + y*z) / (4*x - 3*z + 1)
(
}<- qlone(1); y <- qlone(2); z <- qlone(3)
x <- f(x, y, z) roq
You can differentiate it:
derivRatioOfQsprays(roq, 2) # derivative w.r.t. y
## [ 1/4*z ] %//% [ x - 3/4*z + 1/4 ]
You can permute its variables:
swapVariables(roq, 2, 3) == f(x, z, y)
## [1] TRUE
You can perform some polynomial changes of its variables:
changeVariables(roq, list(x+1, y^2, x+y+z)) == f(x+1, y^2, x+y+z)
## [1] TRUE