Structures for systems of equation over rings with operators

File for the structures concerning differential systems

This file contains the structures and main algorithms to manipulate and study the solutions of differential systems expressed in terms of differential polynomials.

AUTHORS:

  • Antonio Jimenez-Pastor (2022-02-04): initial version

class dalgebra.rwo_polynomial.rwo_polynomial_system.RWOSystem(equations, parent=None, variables=None)

Bases: object

Class for representing a system over a ring with an operator.

This class allows the user to represent a system of equations over a ring with operators (see the category RingsWithOperators) as a list of infinite polynomials in one or several variables.

This class will offer a set of methods and properties to extract the main information of the system and also the main algorithms and methods to study or manipulate these systems such as elimination procedures, etc.

INPUT:

  • equations: list or tuple of operator polynomials (see RWOPolynomial). The system will be the one defined by \(eq = 0\) for all \(eq\) in the input equations.

  • parent: the common parent to transform the input. The final parent of all the elements will a common structure (if possible) that will be the pushout of all the parents of elements and this structure.

  • variables: list of names or infinite variables that will fix the variables of the system. If it is not given, we will consider all the differential variables as main variables.

algebraic_equations()

Method to get the equivalent algebraic equations.

Considering a differential polynomial algebraically means to separate semantically the relation of different derivatives of a differential variable. This is mainly useful once all differential operations are completed.

OUTPUT:

A tuple of polynomials in a common parent that represent the equations of self in a purely algebraic context.

EXAMPLES:

sage: from dalgebra import *
sage: R.<u> = DifferentialPolynomialRing(QQ)
sage: system = DifferentialSystem([u[1]-u[0]])
sage: system.algebraic_equations()
(-u_0 + u_1,)
sage: system.extend_by_operation([1]).algebraic_equations()
(-u_0 + u_1, -u_1 + u_2)

We can check that the parent of all the equations is the same:

sage: parents = [el.parent() for el in system.extend_by_operation([1]).algebraic_equations()]
sage: all(el == parents[0] for el in parents[1:])
True
sage: parents[0]
Multivariate Polynomial Ring in u_0, u_1, u_2 over Differential 
Ring [[Rational Field], (0,)]

The same can be checked for a multivariate differential polynomial:

sage: R.<u,v> = DifferentialPolynomialRing(QQ[x]); x = R.base().gens()[0]
sage: system = DifferentialSystem([x*u[0] + x^2*u[2] - (1-x)*v[0], v[1] - v[2] + u[1]])
sage: system.algebraic_equations()
((x - 1)*v_0 + x*u_0 + x^2*u_2, v_1 - v_2 + u_1)
sage: system.extend_by_operation([1,2]).algebraic_equations()
((x - 1)*v_0 + x*u_0 + x^2*u_2,
v_0 + (x - 1)*v_1 + u_0 + x*u_1 + 2*x*u_2 + x^2*u_3,
v_1 - v_2 + u_1,
v_2 - v_3 + u_2,
v_3 - v_4 + u_3)

And the parents are again the same for all those equations:

sage: parents = [el.parent() for el in system.extend_by_operation([1,2]).algebraic_equations()]
sage: all(el == parents[0] for el in parents[1:])
True
sage: parents[0]
Multivariate Polynomial Ring in v_0, v_1, v_2, v_3, v_4, u_0, u_1, u_2, u_3 over 
Differential Ring [[Univariate Polynomial Ring in x over Rational Field], (d/dx,)]

The output of this method depends actively in the set of active variables that defines the system:

sage: system_with_u = DifferentialSystem([x*u[0] + x^2*u[2] - (1-x)*v[0], v[1] - v[2] + u[1]], variables=[u])
sage: system_with_u.algebraic_equations()                                                                                                                                        
(x*u_0 + x^2*u_2 + (x - 1)*v_0, u_1 + v_1 - v_2)
sage: system_with_u.extend_by_operation([1,2]).algebraic_equations()
(x*u_0 + x^2*u_2 + (x - 1)*v_0,
u_0 + x*u_1 + 2*x*u_2 + x^2*u_3 + v_0 + (x - 1)*v_1,
u_1 + v_1 - v_2,
u_2 + v_2 - v_3,
u_3 + v_3 - v_4)

In this case, the parent prioritize the variables related with \(u_*\):

sage: system_with_u.algebraic_equations()[0].parent()
Multivariate Polynomial Ring in u_0, u_1, u_2 over Multivariate Polynomial 
Ring in v_0, v_1, v_2 over Differential Ring [[Univariate Polynomial Ring 
in x over Rational Field], (d/dx,)]
sage: parents = [el.parent() for el in system_with_u.extend_by_operation([1,2]).algebraic_equations()]
sage: all(el == parents[0] for el in parents[1:])
True
sage: parents[0]
Multivariate Polynomial Ring in u_0, u_1, u_2, u_3 over Multivariate Polynomial 
Ring in v_0, v_1, v_2, v_3, v_4 over Differential Ring [[Univariate Polynomial 
Ring in x over Rational Field], (d/dx,)]
algebraic_variables()

Method to retrieve the algebraic variables in the system.

This method computes the number of algebraic variables that appear on the system. This means, gathering each appearance of the differential variables and filter them by the variables of the system.

OUTPUT:

A tuple containing the algebraic variables appearing in the system (as differential polynomials)

EXAMPLES:

sage: from dalgebra import *
sage: R.<u> = DifferentialPolynomialRing(QQ)
sage: system = DifferentialSystem([u[1]-u[0]])
sage: system.algebraic_variables()
(u_0, u_1)
sage: R.<u,v> = DifferentialPolynomialRing(QQ[x]); x = R.base().gens()[0]
sage: system = DifferentialSystem([x*u[0] + x^2*u[2] - (1-x)*v[0], v[1] - v[2] + u[1]])
sage: system.algebraic_variables()
(v_0, v_1, v_2, u_0, u_1, u_2)
sage: system = DifferentialSystem([x*u[0] + x^2*u[2] - (1-x)*v[0], v[1] - v[2] + u[1]], variables = [u])
sage: system.algebraic_variables()
(u_0, u_1, u_2)
build_sp1 = <function RWOSystem.extend_by_operation>

alias for method extend_by_operation()

change_variables(variables)

Method that creates a new system with new set of main variables.

This method returns an equivalent system as self, i.e., with the same equations and with the same parent. Bu now we fix a different set of variables as main variables of the system.

INPUT:

  • variables: set of new variables for the system. See RWOSystem to see the format for this input.

OUTPUT:

A RWOSystem with the same equations but main variables given by variables.

EXAMPLES:

sage: from dalgebra import *
sage: bR = QQ[x]; x = bR('x')
sage: R.<u,v> = DifferentialPolynomialRing(bR)
sage: eq1 = u[0]*x - v[1]
sage: eq2 = u[1] - (x-1)*v[0]
sage: system = DifferentialSystem([eq1,eq2], variables=[u,v])
sage: system.change_variables(u)
System over [Ring of operator polynomials in (u, v) over Differential Ring 
[[Univariate Polynomial Ring in x over Rational Field], (d/dx,)]] with variables [(u_*,)]:
{
    x*u_0 - v_1 == 0
    u_1 + (-x + 1)*v_0 == 0
}
sage: system.change_variables([v])
System over [Ring of operator polynomials in (u, v) over Differential Ring 
[[Univariate Polynomial Ring in x over Rational Field], (d/dx,)]] with variables [(v_*,)]:
{
    x*u_0 - v_1 == 0
    u_1 + (-x + 1)*v_0 == 0
}
diff_resultant(verbose, *args, **kwds)

Method to compute the operator resultant of this system.

TODO: add explanation of resultant.

This method has the optional argument verbose which, when given, will print the logging output in the console (sys.stdout)

INPUT:

  • bound_L: bound for the values of Ls for method extend_by_operation().

  • operation: index for the operation for which we want to compute the resultant.

  • alg_res: ("auto" by default) method to compute the algebraic resultant once we extended a system to a valid system (see is_sp2()). The valid values are, currently, "dixon", "macaulay" and "iterative".

OUTPUT:

The resultant for this system.

TODO: add examples

equation(index, *apply)

Method to get an equation from the system.

This method allows to obtain one equation from this system. This means, obtain a polynomial that is equal to zero, assuming the polynomials in self._equations are all equal to zero.

This method allow to obtain the equations in self._equations but also the derived equations using the operation of the system.

INPUT:

  • index: the index for the equation desired.

  • apply: a collection of tuples \((a,n)\) indicating which operations to apply to the equation. In case there is only one operator, an integer means how many times to apply the only operator. Otherwise, an integer means the application of that operation once.

OUTPUT:

A polynomial with the \(i\)-th equation of the system. If apply was given, then we return the \(i\)-th equation after applying the operations as many times as requested.

EXAMPLES:

sage: from dalgebra import *
sage: R.<u,v> = DifferentialPolynomialRing(QQ[x]); x = R.base()(x)
sage: eq1 = u[0]*x - v[1]
sage: eq2 = u[1] - (x-1)*v[0]
sage: system = DifferentialSystem([eq1,eq2], variables=[u,v])
sage: system.equation(0)
x*u_0 - v_1
sage: system.equation(1)
u_1 + (-x + 1)*v_0

If the index given is not in range, we raise a IndexError:

sage: system.equation(2)
Traceback (most recent call last):
...
IndexError: tuple index out of range

And if we provide the apply information, we take the corresponding equation and apply all the given operations:

sage: system.equation(0,1) == eq1.derivative()
True
sage: system.equation(0,5) == eq1.derivative(times=5)
True

This is specially useful when having several operators:

sage: A = DifferenceRing(DifferentialRing(QQ["x"], diff), QQ["x"].Hom(QQ["x"])("x+1")); x = A("x")
sage: R.<u,v> = RWOPolynomialRing(A)
sage: eq1 = u[0,0]*x - v[1,0]
sage: eq2 = u[0,1] - (x-1)*v[0,0]
sage: system = RWOSystem([eq1,eq2])
sage: system.equation(0)
x*u_0_0 - v_1_0
sage: system.equation(1)
u_0_1 + (-x + 1)*v_0_0

And now, we can use the apply argument for each operator:

sage: system.equation(0, (0, 1)) # we apply the derivative
x*u_1_0 + u_0_0 - v_2_0
sage: system.equation(0, (1, 1)) # we apply the shift
(x + 1)*u_0_1 - v_1_1
sage: system.equation(0, (0, 2), (1, 3)) == system.equation(0).derivative(times=2).shift(times=3)
True
sage: system.equation(0,0,0,1,1,1) == system.equation(0, (0,2), (1,3))
True
equations(indices=None)

Method to get a list of equations to the system.

This method allows to obtain a list of equations from the system, i.e., a list of polynomials that are assumed to be equal to zero. This method can also be used to get equations from extended systems.

INPUT:

  • indices: collection of elements to be obtain. See method index() for further information. If the input is a slice, we convert it into a list. If the input is not a list or a tuple, we create a list with one element and try to get that element. If None is given, then we return all equations.

OUTPUT:

A list of RWOPolynomial with the requested equations from this system.

EXAMPLES:

sage: from dalgebra import *
sage: R.<u,v> = DifferentialPolynomialRing(QQ[x]); x = R.base()(x)
sage: eq1 = u[0]*x - v[1]
sage: eq2 = u[1] - (x-1)*v[0]
sage: system = DifferentialSystem([eq1,eq2], variables=[u,v])

If nothing is given, we return all the equations:

sage: system.equations()
(x*u_0 - v_1, u_1 + (-x + 1)*v_0)

If only an element is given, then we return that particular element:

sage: system.equations(1)
(u_1 + (-x + 1)*v_0,)

Otherwise, we return the tuple with the equations required. This can be also used to obtained equations after applying the operation (see equation()):

sage: system.equations([(0,0), (0,1), (1,3)])
(x*u_0 - v_1, x*u_1 + u_0 - v_2, u_4 + (-x + 1)*v_3 + (-3)*v_2)

This method also allows the use of slice to provide the indices for equations:

sage: system.equations(slice(None,None,-1)) # reversing the equations
(u_1 + (-x + 1)*v_0, x*u_0 - v_1)
extend_by_operation(Ls, operation=None)

Method that build an extended system that satisfies SP1.

The condition SP1 is defined in the paper doi:10.1016/j.laa.2013.01.016 (Section 3) in regard with a system of differential polynomial: let \(\mathcal{P} = \{f_1,\ldots,f_m\}\). We say that an extended set of differential polynomials \(SP \subset \partial(\mathcal{P})\) is SP1 for some \(L_1,\ldots,L_m \in \mathbb{N}\) if it can be written as:

\[PS = \left\{\partial^{k}(f_i) \mid k \in \{0,\ldots,L_i\}, i=1,\ldots,m\right\}\]

This method provides a way to build an extended system from self using the operator of the base ring that satisfies condition SP1 for a fixed set of values of \(L_1,\ldots,L_m\).

INPUT:

  • Ls: list or tuple of non-negative integers of length self.size().

  • operation: index of the operation with respect to which we want to extend the system.

OUTPUT:

Another RWOSystem extending self with the operation in the base ring that satisfies SP1 for the given list of \(L_i\).

EXAMPLES:

sage: from dalgebra import *
sage: R.<u> = DifferentialPolynomialRing(QQ)
sage: system = DifferentialSystem([u[1]-u[0]])
sage: system.extend_by_operation([0]).equations()
(u_1 - u_0,)
sage: system.extend_by_operation([1]).equations()
(u_1 - u_0, u_2 - u_1)
sage: system.extend_by_operation([5]).equations()
(u_1 - u_0, u_2 - u_1, u_3 - u_2, u_4 - u_3, u_5 - u_4, u_6 - u_5)
sage: R.<u,v> = DifferentialPolynomialRing(QQ[x]); x = R.base().gens()[0]
sage: system = DifferentialSystem([x*u[0] + x^2*u[2] - (1-x)*v[0], v[1] - v[2] + u[1]], variables = [u])
sage: system.extend_by_operation([0,0]).equations()
(x^2*u_2 + x*u_0 + (x - 1)*v_0, u_1 - v_2 + v_1)
sage: system.extend_by_operation([1,0]).equations()
(x^2*u_2 + x*u_0 + (x - 1)*v_0,
x^2*u_3 + 2*x*u_2 + x*u_1 + u_0 + (x - 1)*v_1 + v_0,
u_1 - v_2 + v_1)
sage: system.extend_by_operation([1,1]).equations()
(x^2*u_2 + x*u_0 + (x - 1)*v_0,
x^2*u_3 + 2*x*u_2 + x*u_1 + u_0 + (x - 1)*v_1 + v_0,
u_1 - v_2 + v_1,
u_2 - v_3 + v_2)
is_DifferenceSystem()

EXAMPLES:

sage: class Foo:
....:     def __init__(self, x):
....:         self._x = x
....:     @cached_method
....:     def f(self):
....:         return self._x^2
sage: a = Foo(2)
sage: print(a.f.cache)
None
sage: a.f()
4
sage: a.f.cache
4
is_DifferentialSystem()

EXAMPLES:

sage: class Foo:
....:     def __init__(self, x):
....:         self._x = x
....:     @cached_method
....:     def f(self):
....:         return self._x^2
sage: a = Foo(2)
sage: print(a.f.cache)
None
sage: a.f()
4
sage: a.f.cache
4
is_difference()
is_differential()
is_homogeneous()

This method checks whether the system is homogeneous in the indicated variables.

This method relies on the method algebraic_equations() and the method is_homogeneous() from the polynomial class in Sage.

is_linear(variables=None)

Method that checks whether a system is linear in its variables.

See method is_linear() for further information on how this is computed.

is_sp2()

Method that checks the condition SP2.

The condition SP2 is defined in the paper doi:10.1016/j.laa.2013.01.016 (Section 3) in regard with a system of differential polynomial: let \(\mathcal{P} = \{f_1,\ldots,f_m\}\) be a system of differentially algebraic equations in the differential variables \(\mathcal{U} = \{u_1,\ldots, u_n}\). We say that the system satisfies the condition SP2 if and only if the number of variables is valid to compute a resultant algebraically.

This quantity changing depending on whether the equations are homogeneous (same number of variables and equations) or not (one more equations than variables).

It is interesting to remark that the algebraic variables of a differential polynomial are the total amount of variables that appears in it before the differential relation. Namely, the result of method variables() provides the algebraic variables for a differential polynomial.

OUTPUT:

True if the system satisfies the condition SP2, False otherwise.

EXAMPLES:

sage: from dalgebra import *
sage: R.<u,v> = DifferentialPolynomialRing(QQ[x]); x = R.base().gens()[0]
sage: system = DifferentialSystem([x*u[0] + x^2*u[2] - (1-x)*v[0], v[1] - v[2] + u[1]], variables = [u])
sage: system.is_sp2()
False
sage: system.extend_by_operation([1,2]).is_sp2()
True

WARNING: for this method it is crucial to know that the result depends directly on the set variables for the system. Namely, having different set of active variables change the output of this method for the same differential system:

sage: same_system = DifferentialSystem([x*u[0] + x^2*u[2] - (1-x)*v[0], v[1] - v[2] + u[1]])
sage: system.is_sp2()
False
sage: system.extend_by_operation([1,2]).is_sp2()
True
maximal_linear_variables()

EXAMPLES:

sage: class Foo:
....:     def __init__(self, x):
....:         self._x = x
....:     @cached_method
....:     def f(self):
....:         return self._x^2
sage: a = Foo(2)
sage: print(a.f.cache)
None
sage: a.f()
4
sage: a.f.cache
4
order(gen=None, operation=- 1)

Method to return the order of the system.

The order of a system is defined as the maximal order of their equations. This method allows a generator to be given and then the order w.r.t. this variable will be computed. For further information, check order().

parameters

Initialize self. See help(type(self)) for accurate signature.

parent()
size()
subsystem(indices=None, variables=None)

Method that creates a subsystem for a given set of equations.

This method create a new RWOSystem with the given variables in indices (see equations() to see the format of this input) and setting as main variables those given in variables (see in RWOSystem the format for this input).

INPUT:

  • indices: list or tuple of indices to select the subsystem. (see equations() to see the format of this input).

  • variables: list of variables for the new system. If None is given, we use the variables of self.

OUTPUT:

A new RWOSystem with the new given equations and the variables stated in the input.

EXAMPLES:

sage: from dalgebra import *
sage: bR = QQ[x]; x = bR('x')
sage: R.<u,v> = DifferentialPolynomialRing(bR)
sage: eq1 = u[0]*x - v[1]
sage: eq2 = u[1] - (x-1)*v[0]
sage: system = DifferentialSystem([eq1,eq2], variables=[u,v])
sage: system.subsystem([(0,0), (0,1), (1,3)])
System over [Ring of operator polynomials in (u, v) over Differential Ring 
[[Univariate Polynomial Ring in x over Rational Field], (d/dx,)]] with variables [(u_*, v_*)]:
{
    x*u_0 - v_1 == 0
    x*u_1 + u_0 - v_2 == 0
    u_4 + (-x + 1)*v_3 + (-3)*v_2 == 0
}

This method is used when using the __getitem__ notation:

sage: system[::-1] # same system but with equations changed in order
System over [Ring of operator polynomials in (u, v) over Differential Ring 
[[Univariate Polynomial Ring in x over Rational Field], (d/dx,)]] with variables [(u_*, v_*)]:
{
    u_1 + (-x + 1)*v_0 == 0
    x*u_0 - v_1 == 0
}

Setting up the argument variables allows to change the variables considered for the system:

sage: system.subsystem(None, variables=[u])
System over [Ring of operator polynomials in (u, v) over Differential Ring 
[[Univariate Polynomial Ring in x over Rational Field], (d/dx,)]] with variables [(u_*,)]:
{
    x*u_0 - v_1 == 0
    u_1 + (-x + 1)*v_0 == 0
}
variables

Initialize self. See help(type(self)) for accurate signature.

class dalgebra.rwo_polynomial.rwo_polynomial_system.DifferentialSystem(equations, parent=None, variables=None)

Bases: dalgebra.rwo_polynomial.rwo_polynomial_system.RWOSystem

Class representing a differential system.

extend_by_derivation = <function RWOSystem.extend_by_operation>

new alias for extend_by_operation()

class dalgebra.rwo_polynomial.rwo_polynomial_system.DifferenceSystem(equations, parent=None, variables=None)

Bases: dalgebra.rwo_polynomial.rwo_polynomial_system.RWOSystem

Class representing a difference system.

extend_by_difference = <function RWOSystem.extend_by_operation>

new alias for extend_by_operation()