Learn R Programming

The ‘symbolicQspray’ package

Stéphane Laurent 2024-07-26

Multivariate polynomials with symbolic parameters.


These notes about the symbolicQspray package assume that the reader is a bit familiar with the qspray package and with the ratioOfQsprays package.

A symbolicQspray object represents a multivariate polynomial whose coefficients are fractions of polynomials with rational coefficients. Actually (see our discussion in the next section), a symbolicQspray object represents a multivariate polynomial with parameters. The parameters are the variables of the fractions of polynomials, and so they are symbolically represented.

To construct a symbolicQspray polynomial, use qlone (from the qspray package) to introduce the parameters and use Qlone to introduce the variables of the polynomial:

library(symbolicQspray)
f <- function(a1, a2, X1, X2, X3) {
  (a1/(a2^2+1)) * X1^2*X2  +  (a2+1) * X3  +  a1/a2
}
# parameters, the variables occurring in the coefficients:
a1 <- qlone(1)
a2 <- qlone(2)
# variables:
X1 <- Qlone(1)
X2 <- Qlone(2)
X3 <- Qlone(3)
# the 'symbolicQspray':
( Qspray <- f(a1, a2, X1, X2, X3) )
## { [ a1 ] %//% [ a2^2 + 1 ] } * X^2.Y  +  { [ a2 + 1 ] } * Z  +  { [ a1 ] %//% [ a2 ] }

The fractions of polynomials such as the first coefficient a1/(a2^2+1) in the above example are ratioOfQsprays objects, and the numerator and the denominator of a ratioOfQsprays are qspray objects.

Arithmetic on symbolicQspray objects is available:

Qspray^2
## { [ a1^2 ] %//% [ a2^4 + 2*a2^2 + 1 ] } * X^4.Y^2  +  { [ 2*a1.a2 + 2*a1 ] %//% [ a2^2 + 1 ] } * X^2.Y.Z  +  { [ 2*a1^2 ] %//% [ a2^3 + a2 ] } * X^2.Y  +  { [ a2^2 + 2*a2 + 1 ] } * Z^2  +  { [ 2*a1.a2 + 2*a1 ] %//% [ a2 ] } * Z  +  { [ a1^2 ] %//% [ a2^2 ] }
Qspray - Qspray
## 0
(Qspray - 1)^2
## { [ a1^2 ] %//% [ a2^4 + 2*a2^2 + 1 ] } * X^4.Y^2  +  { [ 2*a1.a2 + 2*a1 ] %//% [ a2^2 + 1 ] } * X^2.Y.Z  +  { [ 2*a1^2 - 2*a1.a2 ] %//% [ a2^3 + a2 ] } * X^2.Y  +  { [ a2^2 + 2*a2 + 1 ] } * Z^2  +  { [ 2*a1.a2 + 2*a1 - 2*a2^2 - 2*a2 ] %//% [ a2 ] } * Z  +  { [ a1^2 - 2*a1.a2 + a2^2 ] %//% [ a2^2 ] }
Qspray^2 - 2*Qspray + 1
## { [ a1^2 ] %//% [ a2^4 + 2*a2^2 + 1 ] } * X^4.Y^2  +  { [ 2*a1.a2 + 2*a1 ] %//% [ a2^2 + 1 ] } * X^2.Y.Z  +  { [ 2*a1^2 - 2*a1.a2 ] %//% [ a2^3 + a2 ] } * X^2.Y  +  { [ a2^2 + 2*a2 + 1 ] } * Z^2  +  { [ 2*a1.a2 + 2*a1 - 2*a2^2 - 2*a2 ] %//% [ a2 ] } * Z  +  { [ a1^2 - 2*a1.a2 + a2^2 ] %//% [ a2^2 ] }

Evaluating a symbolicQspray

Substituting the “exterior” variables (the variables occurring in the ratios of polynomials, also called the parameters - see below) yields a qspray object:

a <- c(2, "3/2")
( qspray <- evalSymbolicQspray(Qspray, a = a) )
## 8/13*X^2.Y + 5/2*Z + 4/3

Substituting the “main” variables yields a ratioOfQsprays object:

X <- c(4, 3, "2/5")
( ratioOfQsprays <- evalSymbolicQspray(Qspray, X = X) )
## [ a1.a2^2 + 48*a1.a2 + a1 + 2/5*a2^4 + 2/5*a2^3 + 2/5*a2^2 + 2/5*a2 ] %//% [ a2^3 + a2 ]

There is a discutable point here. A symbolicQspray object represents a polynomial with ratioOfQsprays coefficients. So one could consider that the polynomial variables X, Y and Z represent some indeterminate ratioOfQsprays fractions, and that it should be possible to replace them with ratioOfQsprays objects. However this is not allowed. We will discuss that, just after checking the consistency:

evalSymbolicQspray(Qspray, a = a, X = X)
## Big Rational ('bigq') :
## [1] 1243/39
evalQspray(qspray, X)
## Big Rational ('bigq') :
## [1] 1243/39
evalRatioOfQsprays(ratioOfQsprays, a)
## Big Rational ('bigq') :
## [1] 1243/39
a <- gmp::as.bigq(a); X <- gmp::as.bigq(X)
f(a[1], a[2], X[1], X[2], X[3])
## Big Rational ('bigq') :
## [1] 1243/39

Now let’s turn to our promised discussion. Why is replacing the values of the polynomial variables with some ratioOfQsprays objects not allowed?

Actually my motivation to do this package was inspired by the Jack polynomials. In the context of Jack polynomials, the variables X, Y and Z represent indeterminate numbers, and the coefficients are numbers depending on a parameter (the Jack parameter), and it turns out that they are fractions of polynomials of this parameter. So I consider that a symbolicQspray is not a polynomial on the field of fractions of polynomials: I consider it is a polynomial with rational coefficients depending on some parameters.

Also note that evaluating the ratioOfQsprays object evalSymbolicQspray(Qspray, X = X) at a would make no sense if we took some ratioOfQsprays objects for the values of X.

Querying a symbolicQspray

The package provides some functions to perform elementary queries on a symbolicQspray:

numberOfVariables(Qspray)
## [1] 3
numberOfParameters(Qspray)
## [1] 2
numberOfTerms(Qspray)
## [1] 3
getCoefficient(Qspray, c(2, 1)) # coefficient of X^2.Y
## [ a1 ] %//% [ a2^2 + 1 ]
getConstantTerm(Qspray)
## [ a1 ] %//% [ a2 ]
isUnivariate(Qspray)
## [1] FALSE
isConstant(Qspray)
## [1] FALSE

Transforming a symbolicQspray

You can differentiate a symbolicQspray polynomial:

derivSymbolicQspray(Qspray, 2) # derivative w.r.t. Y
## { [ a1 ] %//% [ a2^2 + 1 ] } * X^2

You can permute its variables:

swapVariables(Qspray, 2, 3) == f(a1, a2, X1, X3, X2)
## [1] TRUE

You can perform polynomial transformations of its variables:

changeVariables(Qspray, list(X1+1, X2^2, X1+X2+X3)) == 
  f(a1, a2, X1+1, X2^2, X1+X2+X3)
## [1] TRUE

You can also perform polynomial transformations of its parameters:

changeParameters(Qspray, list(a1^2, a2^2)) == f(a1^2, a2^2, X1, X2, X3)
## [1] TRUE

Showing a symbolicQspray

You can change the way a symbolicQspray is printed by using showSymbolicQsprayOption:

showSymbolicQsprayOption(Qspray, "a") <- "x"
showSymbolicQsprayOption(Qspray, "showMonomial") <- 
  showMonomialXYZ(c("A", "B", "C"))
showSymbolicQsprayOption(Qspray, "quotientBar") <- " / "
Qspray
## { [ x1 ] / [ x2^2 + 1 ] } * A^2.B  +  { [ x2 + 1 ] } * C  +  { [ x1 ] / [ x2 ] }

When this is possible, the result of an arithmetic operation between two symbolicQspray objects inherits the show options of the first operand:

set.seed(421)
( Q <- rSymbolicQspray() ) # a random symbolicQspray
## { [ -a1^2.a3^3 - 5/2*a1^2 - 5/2*a3 ] %//% [ a1^4.a3^3 - 3/2*a2^4 ] } * X^3.Y  +  { [ 5/3*a1^4.a3^4 - 1/3*a1^2.a2.a3^3 ] %//% [ a1^2 - a2^2.a3^3 ] } * Y^3
Qspray + Q
## { [ -x1^2.x3^3 - 5/2*x1^2 - 5/2*x3 ] / [ x1^4.x3^3 - 3/2*x2^4 ] } * A^3.B  +  { [ x1 ] / [ x2^2 + 1 ] } * A^2.B  +  { [ 5/3*x1^4.x3^4 - 1/3*x1^2.x2.x3^3 ] / [ x1^2 - x2^2.x3^3 ] } * B^3  +  { [ x2 + 1 ] } * C  +  { [ x1 ] / [ x2 ] }

This behavior is the same as the ones implemented in qspray and in ratioOfQsprays. You should be familiar with these two packages in order to use symbolicQspray.

Application: Jacobi polynomials

The Jacobi polynomials are univariate polynomials depending on two parameters that we will denote by alpha and beta. They are implemented in this package:

JP <- JacobiPolynomial(2)
isUnivariate(JP)
## [1] TRUE
numberOfParameters(JP)
## [1] 2
showSymbolicQsprayOption(JP, "showRatioOfQsprays") <-
  showRatioOfQspraysXYZ(c("alpha", "beta"))
JP
## { [ 1/8*alpha^2 + 1/4*alpha.beta + 7/8*alpha + 1/8*beta^2 + 7/8*beta + 3/2 ] } * X^2  +  { [ 1/4*alpha^2 + 3/4*alpha - 1/4*beta^2 - 3/4*beta ] } * X  +  { [ 1/8*alpha^2 - 1/4*alpha.beta - 1/8*alpha + 1/8*beta^2 - 1/8*beta - 1/2 ] }

The implementation constructs these polynomials by using the recurrence relation. This is a child game, one just has to copy the first two terms and this recurrence relation:

JacobiPolynomial <- function(n) {
  stopifnot(isPositiveInteger(n))
  if(n == 0) {
    Qone()
  } else if(n == 1) {
    alpha <- qlone(1)
    beta  <- qlone(2)
    X     <- Qlone(1)
    (alpha + 1) + (alpha + beta + 2) * (X - 1)/2
  } else {
    alpha <- qlone(1)
    beta  <- qlone(2)
    X     <- Qlone(1)
    a <- n + alpha
    b <- n + beta
    c <- a + b
    K <- 2 * n * (c - n) * (c - 2)
    lambda1 <- ((c - 1) * (c * (c - 2) * X + (a - b) * (c - 2*n))) / K
    lambda2 <- (2 * (a - 1) * (b - 1) * c) / K
    (lambda1 * JacobiPolynomial(n - 1) - lambda2 * JacobiPolynomial(n - 2))
  }
}

It is clearly visible from the recurrence relation that the coefficients of the Jacobi polynomials are indeed fractions of polynomials in alpha and beta. But they actually are polynomials in alpha and beta. Actually I don’t know, this is a conjecture I made because I observed this fact for some small values of n. We can check it with the function hasPolynomialCoefficientsOnly:

JP <- JacobiPolynomial(7)
hasPolynomialCoefficientsOnly(JP)
## [1] TRUE

Up to a factor, the Gegenbauer polynomials with parameter alpha coincide with the Jacobi polynomials with parameters alpha - 1/2 and alpha - 1/2. Let’s derive them from the Jacobi polynomials, as an exercise. The factor can be implemented as follows (see Wikipedia for its formula):

risingFactorial <- function(theta, n) {
  toMultiply <- c(theta, lapply(seq_len(n-1), function(i) theta + i))
  Reduce(`*`, toMultiply)
}
theFactor <- function(alpha, n) {
  risingFactorial(2*alpha, n) / risingFactorial((2*alpha + 1)/2, n)
}

Now let’s apply the formula given in the Wikipedia article:

GegenbauerPolynomial <- function(n) {
  alpha <- qlone(1)
  P <- changeParameters(
    JacobiPolynomial(n), list(alpha - "1/2", alpha - "1/2")
  )
  theFactor(alpha, n) * P
}

Let’s check that the recurrence relation given in the Wikipedia article is fulfilled:

n <- 5
alpha <- qlone(1)
X <- Qlone(1)
(n + 1) * GegenbauerPolynomial(n+1) == 
  2*(n + alpha) * X * GegenbauerPolynomial(n) - 
    (n + 2*alpha - 1) * GegenbauerPolynomial(n-1)
## [1] TRUE

Application to Jack polynomials

The symbolicQspray package is used in the jack package to compute the Jack polynomials with a symbolic Jack parameter. The Jack polynomials exactly fit to the polynomials represented by the symbolicQspray objects: their coefficients are fractions of polynomials by definition, of one variable: the Jack parameter.

Copy Link

Version

Install

install.packages('symbolicQspray')

Monthly Downloads

99

Version

1.1.0

License

GPL-3

Issues

Pull Requests

Stars

Forks

Maintainer

Stc3<a9>phane Laurent

Last Published

July 28th, 2024

Functions in symbolicQspray (1.1.0)

showSymbolicQsprayXYZ

Print a 'symbolicQspray' object
permuteVariables

Permute variables
numberOfParameters

Number of parameters
hasPolynomialCoefficientsOnly

Whether the coefficients of a 'symbolicQspray' polynomially depend on its parameters
numberOfTerms

Number of terms in a 'symbolicQspray' polynomial
showSymbolicQsprayOption<-

Set a show option to a 'symbolicQspray' object
showSymbolicQspray

Print a 'symbolicQspray' object
getCoefficient

Get a coefficient in a 'symbolicQspray' polynomial
symbolicQspray-unary

Unary operators for 'symbolicQspray objects
showSymbolicQsprayABCXYZ

Print a 'symbolicQspray' object
symbolicQspray_from_list

(internal) Make a 'symbolicQspray' object from a list
numberOfVariables

Number of variables of a 'symbolicQspray' polynomial
swapVariables

Swap variables
substituteVariables

Assign values to the variables of a 'symbolicQspray'
substituteParameters

Assign values to the parameters of a 'symbolicQspray'
rSymbolicQspray

Random 'symbolicQspray'
showSymbolicQsprayX1X2X3

Print a 'symbolicQspray' object
dSymbolicQspray

Partial differentiation
JacobiPolynomial

Jacobi polynomial
Qzero

The null 'symbolicQspray' polynomial
derivSymbolicQspray

Partial derivative
Qone

The unit 'symbolicQspray' polynomial
as.symbolicQspray

Coercion to a 'symbolicQspray' object
changeParameters

Change of parameters in a 'symbolicQspray' polynomial
Qlone

Polynomial variable
changeVariables

Change of variables in a 'symbolicQspray' polynomial
compactSymmetricQspray

Compact symmetric qspray
getConstantTerm

Get the constant term of a 'symbolicQspray' polynomial
isQone

Whether a 'symbolicQspray' polynomial is the unit polynomial
isQzero

Whether a 'symbolicQspray' polynomial is null
isUnivariate

Whether a 'symbolicQspray' polynomial is univariate
involvedVariables

Variables involved in a 'symbolicQspray' polynomial
isConstant

Whether a 'symbolicQspray' polynomial is constant
evalSymbolicQspray

Evaluation of a 'symbolicQspray' polynomial