AbstractAlgebra.jl generic code makes use of a standardised set of functions which it expects to be implemented for all fields. Here we document this interface. All libraries which want to make use of the generic capabilities of AbstractAlgebra.jl must supply all of the required functionality for their fields.
Most fields must supply two types:
- a type for the parent object (representing the field itself)
- a type for elements of that field
For example, the generic fraction field type in AbstractAlgebra.jl provides two
types in generic/GenericTypes.jl
:
Generic.FracField{T}
for the parent objectsGeneric.FracFieldElem{T}
for the actual fractions
The parent type must belong to Field
and the element type must belong
to FieldElem
. Of course, the types may belong to these abstract types
transitively.
For parameterised fields, we advise that the types of both the parent objects and element objects to be parameterised by the types of the elements of the base ring.
There can be variations on this theme: e.g. in some areas of mathematics there is a notion of a coefficient domain, in which case it may make sense to parameterise all types by the type of elements of this coefficient domain. But note that this may have implications for the ad hoc operators one might like to explicitly implement.
Because of its lack of multiple inheritance, Julia does not allow Julia Base
types to belong to FieldElem
. To allow us to work equally with
AbstractAlgebra and Julia types that represent elements of fields we define a
union type FieldElement
in src/julia/JuliaTypes
.
So far, in addition to FieldElem
the union type
FieldElement
includes the Julia types Rational
and AbstractFloat
.
Most of the generic code in AbstractAlgebra makes use of the union type
FieldElement
instead of FieldElem
so that the
generic functions also accept the Julia Base field types.
!!! note
One must be careful when defining ad hoc binary operations for field element
types. It is often necessary to define separate versions of the functions for
`FieldElem` then for each of the Julia types separately in
order to avoid ambiguity warnings.
Note that even though FieldElement
is a union type we still
have the following inclusion
FieldElement <: RingElement
In many cases, it is desirable to have only one object in the system to represent each field. This means that if the same field is constructed twice, elements of the two fields will be compatible as far as arithmetic is concerned.
In order to facilitate this, global caches of fields are stored in AbstractAlgebra.jl,
usually implemented using dictionaries. For example, the Generic.FracField
parent
objects are looked up in a dictionary FracDict
to see if they have been previously
defined.
Whether these global caches are provided or not, depends on both mathematical and algorithmic considerations. E.g. in the case of number fields, it isn't desirable to identify all number fields with the same defining polynomial, as they may be considered with distinct embeddings into one another. In other cases, identifying whether two fields are the same may be prohibitively expensive. Generally, it may only make sense algorithmically to identify two fields if they were constructed from identical data.
If a global cache is provided, it must be optionally possible to construct the parent
objects without caching. This is done by passing a boolean value cached
to the inner
constructor of the parent object. See generic/GenericTypes.jl
for examples of how to
construct and handle such caches.
In the following, we list all the functions that are required to be provided for fields in AbstractAlgebra.jl or by external libraries wanting to use AbstractAlgebra.jl.
We give this interface for fictitious types MyParent
for the type of the field parent
object R
and MyElem
for the type of the elements of the field.
!!! note
Generic functions in AbstractAlgebra.jl may not rely on the existence of
functions that are not documented here. If they do, those functions will only be
available for fields that implement that additional functionality, and should be
documented as such.
In the first place, all fields are rings and therefore any field type must implement all of the Ring interface. The functionality below is in addition to this basic functionality.
characteristic(R::MyParent)
Return the characteristic of the field. If the characteristic is not known, an exception is raised.
is_unit(f::MyElem)
Return true
if the given element is invertible, i.e. nonzero in the field.