Rings with Operators: Category, Factory and Wrapper¶
Module with all structures for defining rings with operators.
Let \(\sigma: R \rightarrow R\) be an additive homomorphism, i.e., for all elements \(r,s \in R\), the map satisfies \(\sigma(r+s) = \sigma(r) + \sigma(s)\). We define the ring \(R\) with operator \(\sigma\) as the pair \((R, \sigma)\).
Similarly, if we have a set of additive maps \(\sigma_1,\ldots,\sigma_n : R \rightarrow R\). Then we define the ring \(R\) with operators \((\sigma_1,\ldots,\sigma_n)\) as the tuple \((R, (\sigma_1,\ldots,\sigma_n))\).
This module provides the framework to define this type of rings with as many operators as the user wants and we also provide a Wrapper class so we can extend existing ring structures that already exist in SageMath.
The factory RingWithOperator()
allows the creation of these rings with operators and will determine
automatically in which specified category a ring will belong. For example, we can create the differential
ring \((\mathbb{Q}[x], \partial_x)\) or the difference ring \((\mathbb{Q}[x], x \mapsto x + 1)\) with the
following code:
sage: from dalgebra import *
sage: dQx = RingWithOperators(QQ[x], lambda p : p.derivative())
sage: sQx = RingWithOperators(QQ[x], lambda p : QQ[x](p)(x=QQ[x].gens()[0] + 1))
Once the rings are created, we can create elements within the ring and apply the corresponding operator:
sage: x = dQx(x)
sage: x.operation()
1
sage: x = sQx(x)
sage: x.operation()
x + 1
We can also create the same ring with both operators together:
sage: dsQx = RingWithOperators(QQ[x], lambda p : p.derivative(), lambda p : QQ[x](p)(x=QQ[x].gens()[0] + 1))
sage: x = dsQx(x)
sage: x.operation(operation=0)
1
sage: x.operation(operation=1)
x + 1
However, these operators have no structure by themselves: SageMath is not able to distinguish the type of the operators if they are defined using lambda expressions or callables. This can be seen by the fact that the factory can not detect the equality on two identical rings:
sage: dQx is RingWithOperators(QQ[x], lambda p : p.derivative())
False
To avoid this behavior, we can set the types by providing an optional list called types
whose elements are
strings with values:
homomorphism
: the operator is interpret as a homomorphism/shift/difference operator.derivation
: the operator is considered as a derivation.skew
: the operator is considered as a skew-derivation.none
: the operator will only be considered as an additive Map without further structure.
We can see that, when setting this value, the ring is detected to be equal:
sage: dQx = RingWithOperators(QQ[x], lambda p : p.derivative(), types=["derivation"])
sage: dQx is RingWithOperators(QQ[x], lambda p : p.derivative(), types=["derivation"])
True
sage: # Since we have one variable, the built-in `diff` also work
sage: dQx is RingWithOperators(QQ[x], diff, types=["derivation"])
True
sage: # We can also use elements in the derivation module
sage: dQx is RingWithOperators(QQ[x], QQ[x].derivation_module().gens()[0], types=["derivation"])
True
Also, we can detect this equality when adding operators sequentially instead of at once:
sage: dsQx = RingWithOperators(QQ[x],
....: lambda p : p.derivative(),
....: lambda p : QQ[x](p)(x=QQ[x].gens()[0] + 1),
....: types = ["derivation", "homomorphism"]
....: )
sage: dsQx is RingWithOperators(dQx, lambda p : QQ[x](p)(x=QQ[x].gens()[0] + 1), types=["homomorphism"])
True
For specific types of operators as derivations or homomorphism, there are other functions where the types
argument can be skipped
taking the corresponding value by default:
sage: dQx is DifferentialRing(QQ[x], lambda p : p.derivative())
True
sage: dsQx is DifferenceRing(DifferentialRing(QQ[x], lambda p : p.derivative()), lambda p : QQ[x](p)(x=QQ[x].gens()[0] + 1))
True
We can also have more complexes structures with different types of operators:
sage: R.<x,y> = QQ[] # x is the usual variable, y is an exponential
sage: dx, dy = R.derivation_module().gens(); d = dx + y*dy
sage: DR = DifferentialRing(R, d)
sage: # We add a special homomorphism where the two generators are squared but QQ is fixed
sage: DSR = DifferenceRing(DR, R.Hom(R)([x^2, y^2]))
sage: DSR.noperators()
2
sage: DSR.operator_types()
('derivation', 'homomorphism')
We can see that these operator do not commute:
sage: x = DSR(x); y = DSR(y)
sage: x.difference().derivative()
2*x
sage: x.derivative().difference()
1
sage: y.difference().derivative()
2*y^2
sage: y.derivative().difference()
y^2
Finally, this module also allows the definition of skew-derivations for any ring. This requires the use of derivation modules with twist (see sage.rings.derivations):
sage: R.<x,y> = QQ[]
sage: s = R.Hom(R)([x-y, x+y])
sage: td = R.derivation_module(twist=s)(x-y)
sage: tR = RingWithOperators(R, s, td, types=["homomorphism", "skew"])
sage: x,y = tR.gens()
sage: (x*y).skew() == x.skew()*y + x.shift()*y.skew()
True
sage: (x*y).skew() == x.skew()*y.shift() + x*y.skew()
True
AUTHORS:
Antonio Jimenez-Pastor (GitHub)
- class dalgebra.ring_w_operator.RingsWithOperators(s=None)¶
Bases:
sage.categories.category.Category
Category for representing rings with operators.
Let \(\sigma: R \rightarrow R\) be an additive homomorphism, i.e., for all elements \(r,s \in R\), the map satisfies \(\sigma(r+s) = \sigma(r) + \sigma(s)\). We define the ring \(R\) with operator \(\sigma\) as the pair \((R, \sigma)\).
Similarly, if we have a set of additive maps \(\sigma_1,\ldots,\sigma_n : R \rightarrow R\). Then we define the ring \(R\) with operators \((\sigma_1,\ldots,\sigma_n)\) as the tuple \((R, (\sigma_1,\ldots,\sigma_n))\).
This category defines the basic methods for these rings and their elements
- class ElementMethods¶
Bases:
object
- derivative(derivation=None, times=1)¶
Apply a derivation to
self
a given amount of times.This method applies repeatedly a derivation defined in the parent of
self
. Seederivative()
for further information.
- difference(difference=None, times=1)¶
Apply a difference to
self
a given amount of times.This method applies repeatedly a difference defined in the parent of
self
. Seedifference()
for further information.
- is_constant(operation=0)¶
Method to check whether an element is a constant with respect to one operator.
INPUT:
operation
: index defining the operation we want to check.
OUTPUT:
A boolean value with
True
is the element is a constant (seeconstant_ring()
for further information on what is a constant depending on the type of operator).REMARK: this method do not require the implementation on
constant_ring()
on its parent structure.EXAMPLES:
sage: from dalgebra import * sage: R = DifferentialRing(QQ[x], diff) sage: p = R(3) sage: p.is_constant() True sage: p = R(x^3 - 3*x + 1) sage: p.is_constant() False
Some interesting constants may arise unexpectedly when adding other derivations:
sage: R.<x,y> = QQ[] sage: dx, dy = R.derivation_module().gens(); d = y*dx - x*dy sage: dR = DifferentialRing(R, d) sage: x,y = dR.gens() sage: x.is_constant() False sage: y.is_constant() False sage: (x^2 + y^2).is_constant() True
- operation(operation=None, times=1)¶
Apply an operation to
self
a given amount of times.This method applies repeatedly an operation defined in the parent of
self
. Seeoperation()
for further information.
- shift(shift=None, times=1)¶
Alias for
difference()
.
- class MorphismMethods¶
Bases:
object
- class ParentMethods¶
Bases:
object
- all_operators_commute(points=10, *args, **kwds)¶
Method to check whether all operators of the ring commute.
This method is not deterministic (meaning that it may return
True
even when the two operators do not fully commute) but it tries to check in a fix number of random elements if the two operators actually commute.It also try to see if the operators commute in the generators of the ring.
See
operators_commute()
for further informationINPUT:
points
: number of random points to be selected.args
: arguments to be passed to therandom_element
method.kwds
: arguments to be passed to therandom_element
method.
OUTPUT:
True
if all the tests indicates the operators commute,False
otherwise.EXAMPLES:
sage: from dalgebra import * sage: R.<x> = QQ[]; d = diff; s = R.Hom(R)(x+1) sage: dsR = DifferenceRing(DifferentialRing(R, d), s) sage: dsR.all_operators_commute() True sage: R.<x,y> = QQ[] sage: dx,dy = R.derivation_module().gens(); d = dx + y*dy sage: s = R.Hom(R)([x + 1, y^2]) sage: dsR = DifferenceRing(DifferentialRing(R, d), s) sage: dsR.all_operators_commute() False
- constant_ring()¶
Method to obtain the constant ring of a given operation.
The meaning of a ring of constants depends on the type of operator that we are considering:
“homomorphism”: the elements that are fixed by the operator.
“derivation”: the elements that goes to zero with the operator.
“skew”: the elements that goes to zero with the operator.
“none”: it makes no sense to talk about constant for these operators.
- derivations()¶
Method to filter the derivations out of a ring with operators.
Derivations are a particular type of operators. With this method we provide a similar interface as with the generic operators but just with derivation.
Similarly, this class offers access to homomorphisms and skew derivations.
When no derivation is declared for a ring, an empty tuple is returned.
- derivative(element, derivation=None)¶
Method to apply a derivation over an element.
This method applies a derivation over a given element in the same way an operator is applied by the method
operation()
.
- difference(element, difference=None)¶
Method to apply a difference over an element.
This method applies a difference over a given element in the same way an operator is applied by the method
operation()
.
- differences()¶
Method to filter the differences out of a ring with operators.
Differences are a particular type of operators. With this method we provide a similar interface as with the generic operators but just with difference.
Similarly, this class offers access to derivations and skew derivations.
When no difference is declared for a ring, an empty tuple is returned.
- has_derivations()¶
Method to know if there are derivations defined over the ring.
- has_differences()¶
Method to know if there are differences defined over the ring.
- has_skews()¶
Method to know if there are skew-derivations defined over the ring.
- is_difference()¶
Method to check whether a ring is difference, i.e, all operators are homomorphisms.
- is_differential()¶
Method to check whether a ring is differential, i.e, all operators are derivations.
- is_skew()¶
Method to check whether a ring is skewed, i.e, all operators are skew-derivations.
- nderivations()¶
Method to get the number of derivations defined over a ring
- ndifferences()¶
Method to get the number of differences defined over a ring
- noperators()¶
Method to get the number of operators defined over a ring
- nskews()¶
Method to get the number of skew-derivations defined over a ring
- operation(element, operator=None)¶
Method to apply an operator over an element.
This method takes an element of
self
and applies one of the operators defined overself
over such element. This operator is given by its index, hence raising aIndexError
if the index is not in the valid range.INPUT:
element
: an element over the operator of this ring will be applied.operator
(\(0\) by default) the index of the operator that will be applied.
OUTPUT:
If the index is incorrect, an
IndexError
is raised. Otherwise this method returns \(f(x)\) where \(x\) is theelement
and \(f\) is the operator defined byoperator
.EXAMPLES:
sage: from dalgebra import * sage: dQx = RingWithOperators(QQ[x], lambda p : p.derivative()) sage: sQx = RingWithOperators(QQ[x], lambda p : p(x=QQ[x].gens()[0] + 1)) sage: sdQx = RingWithOperators(QQ[x], lambda p : p(x=QQ[x].gens()[0] + 1), lambda p : p.derivative()) sage: p = QQ[x](x^3 - 3*x^2 + 3*x - 1) sage: dQx.operation(p) 3*x^2 - 6*x + 3 sage: sQx.operation(p) x^3 sage: sdQx.operation(p) Traceback (most recent call last): ... IndexError: An index for the operation must be provided when having several operations sage: sdQx.operation(p, 0) x^3 sage: sdQx.operation(p, 1) 3*x^2 - 6*x + 3 sage: sdQx.operation(p, 2) Traceback (most recent call last): ... IndexError: ... index out of range
- operator_ring()¶
Method to get the operator ring of
self
.When we consider a ring with operators, we can always consider a new (usually non-commutative) ring where we extend
self
polynomially with all the operators and its elements represent new operators created from the operators defined overself
.This method return this new structure.
- operator_types()¶
Method to get the types of the operators.
The only condition for \(\sigma: R \rightarrow R\) to be a valid operator is that it is an additive homomorphism. However, the behavior of \(\sigma\) with respect to the multiplication of \(R\) categorize \(\sigma\) into several possibilities:
“none”: no condition is known over this method. This will disallow some extension operations.
“homomorphism”: the map \(\sigma\) is an homomorphism, i.e., for all \(r, s \in R\) it satisfies \(\sigma(rs) = \sigma(r)\sigma(s)\).
“derivative”: the map \(\sigma\) satisfies Leibniz rule, i.e., for all \(r, s \in R\) it satisfies \(\sigma(rs) = \sigma(r)s + r\sigma(s)\).
“skew”: the map \(\sigma\) satisfies the skew-Leibniz rule, i.e., there is an homomorphism \(\delta\) such for all \(r, s \in R\) it satisfies \(\sigma(rs) = \sigma(r)s + \delta(r)\sigma(s)\).
This method returns a tuple (sorted as the output of
operators()
) with the types of each of the operators.
- operators()¶
Method to get the collection of operators that are defined over the ring.
These operators are maps from
self
toself
that compute the application of each operator over the elements ofself
.
- operators_commute(op1, op2, points=10, *args, **kwds)¶
Method to check whether two operators of the ring commute.
This method is not deterministic (meaning that it may return
True
even when the two operators do not fully commute) but it tries to check in a fix number of random elements if the two operators actually commute.It also try to see if the operators commute in the generators of the ring.
INPUT:
op1
: index of the first operator to check.op2
: index of the second operator to check.points
: number of random points to be selected.args
: arguments to be passed to therandom_element
method.kwds
: arguments to be passed to therandom_element
method.
OUTPUT:
True
if all the tests indicates the operators commute,False
otherwise.
- shift(element, shift=None)¶
Alias for
difference()
.
- skew(element, skew=None)¶
Method to apply a skew-derivation over an element.
This method applies a skew-derivation over a given element in the same way an operator is applied by the method
operation()
.
- skews()¶
Method to filter the skew-derivations out of a ring with operators.
Differences are a particular type of operators. With this method we provide a similar interface as with the generic operators but just with difference.
Similarly, this class offers access to homomorphisms and derivations.
When no skew-derivation is declared for a ring, an empty tuple is returned.
- super_categories()¶
- dalgebra.ring_w_operator.DifferentialRing(base, *operators)¶
Method that calls the
RingWithOperatorFactory
with types always as “derivation”.See documentation on
RingWithOperatorFactory
for further information.
- dalgebra.ring_w_operator.DifferenceRing(base, *operators)¶
Method that calls the
RingWithOperatorFactory
with types always as “homomorphism”.See documentation on
RingWithOperatorFactory
for further information.