diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 409f6226b..eb0b7b74b 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.7","generation_timestamp":"2024-12-08T03:44:44","documenter_version":"1.8.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.7","generation_timestamp":"2024-12-09T03:46:36","documenter_version":"1.8.0"}} \ No newline at end of file diff --git a/dev/about/index.html b/dev/about/index.html index ff7c2bf36..81220d366 100644 --- a/dev/about/index.html +++ b/dev/about/index.html @@ -1,2 +1,2 @@ -About Nemo · Nemo.jl

About Nemo

Nemo is a library for fast basic arithmetic in various commonly used rings, for the Julia programming language. Our aim is to provide a highly performant package covering

  • Commutative Algebra
  • Number Theory
  • Group Theory

Nemo consists of wrappers of specialised C/C++ libraries:

Nemo also uses AbstractAlgebra.jl to provide generic constructions over the basic rings provided by the above packages.

Why Julia?

Julia is a sophisticated, modern programming language which is designed to be both performant and flexible. It was written by mathematicians, for mathematicians.

The benefits of Julia include

  • Familiar imperative syntax
  • JIT compilation (provides near native performance, even for highly generic code)
  • REPL console (cuts down on development time)
  • Parametric types (allows for fast generic constructions over other data types)
  • Powerful metaprogramming facilities
  • Operator overloading
  • Multiple dispatch (dispatch on every argument of a function)
  • Efficient native C interface (little or no wrapper overhead)
  • Experimental C++ interface
  • Dynamic type inference
  • Built-in bignums
  • Able to be embedded in C programs
  • High performance collection types (dictionaries, iterators, arrays, etc.)
  • Jupyter support (for web based notebooks)

The main benefits for Nemo are the parametric type system and JIT compilation. The former allows us to model many mathematical types, e.g. generic polynomial rings over an arbitrary base ring. The latter speeds up the runtime performance, even of highly generic mathematical procedures.

+About Nemo · Nemo.jl

About Nemo

Nemo is a library for fast basic arithmetic in various commonly used rings, for the Julia programming language. Our aim is to provide a highly performant package covering

  • Commutative Algebra
  • Number Theory
  • Group Theory

Nemo consists of wrappers of specialised C/C++ libraries:

Nemo also uses AbstractAlgebra.jl to provide generic constructions over the basic rings provided by the above packages.

Why Julia?

Julia is a sophisticated, modern programming language which is designed to be both performant and flexible. It was written by mathematicians, for mathematicians.

The benefits of Julia include

  • Familiar imperative syntax
  • JIT compilation (provides near native performance, even for highly generic code)
  • REPL console (cuts down on development time)
  • Parametric types (allows for fast generic constructions over other data types)
  • Powerful metaprogramming facilities
  • Operator overloading
  • Multiple dispatch (dispatch on every argument of a function)
  • Efficient native C interface (little or no wrapper overhead)
  • Experimental C++ interface
  • Dynamic type inference
  • Built-in bignums
  • Able to be embedded in C programs
  • High performance collection types (dictionaries, iterators, arrays, etc.)
  • Jupyter support (for web based notebooks)

The main benefits for Nemo are the parametric type system and JIT compilation. The former allows us to model many mathematical types, e.g. generic polynomial rings over an arbitrary base ring. The latter speeds up the runtime performance, even of highly generic mathematical procedures.

diff --git a/dev/acb/index.html b/dev/acb/index.html index 430c0d273..3e48f99b9 100644 --- a/dev/acb/index.html +++ b/dev/acb/index.html @@ -201,4 +201,4 @@ 0 0 0 - 1 + 1 diff --git a/dev/algebraic/index.html b/dev/algebraic/index.html index 5ae8307d8..5231da684 100644 --- a/dev/algebraic/index.html +++ b/dev/algebraic/index.html @@ -118,4 +118,4 @@ Root 0.100000 of 10x - 1

Interface

Nemo.guessFunction
guess(R::QQBarField, x::AcbFieldElem, maxdeg::Int, maxbits::Int=0)
 guess(R::QQBarField, x::ArbFieldElem, maxdeg::Int, maxbits::Int=0)
 guess(R::QQBarField, x::ComplexFieldElem, maxdeg::Int, maxbits::Int=0)
-guess(R::QQBarField, x::RealFieldElem, maxdeg::Int, maxbits::Int=0)

Try to reconstruct an algebraic number from a given numerical enclosure x. The algorithm looks for candidates up to degree maxdeg and with coefficients up to size maxbits (which defaults to the precision of x if not given). Throws if no suitable algebraic number can be found.

Guessing typically requires high precision to succeed, and it does not make much sense to call this function with input precision smaller than $O(maxdeg \cdot maxbits)$. If this function succeeds, then the output is guaranteed to be contained in the enclosure x, but failure does not prove that such an algebraic number with the specified parameters does not exist.

This function does a single iteration with the target parameters. For best performance, one should invoke this function repeatedly with successively larger parameters when the size of the intended solution is unknown or may be much smaller than a worst-case bound.

source
+guess(R::QQBarField, x::RealFieldElem, maxdeg::Int, maxbits::Int=0)

Try to reconstruct an algebraic number from a given numerical enclosure x. The algorithm looks for candidates up to degree maxdeg and with coefficients up to size maxbits (which defaults to the precision of x if not given). Throws if no suitable algebraic number can be found.

Guessing typically requires high precision to succeed, and it does not make much sense to call this function with input precision smaller than $O(maxdeg \cdot maxbits)$. If this function succeeds, then the output is guaranteed to be contained in the enclosure x, but failure does not prove that such an algebraic number with the specified parameters does not exist.

This function does a single iteration with the target parameters. For best performance, one should invoke this function repeatedly with successively larger parameters when the size of the intended solution is unknown or may be much smaller than a worst-case bound.

source diff --git a/dev/arb/index.html b/dev/arb/index.html index 7b13232a3..364c5f645 100644 --- a/dev/arb/index.html +++ b/dev/arb/index.html @@ -241,4 +241,4 @@ a = rand(RR) b = rand(RR; randtype = :null_exact) c = rand(RR; randtype = :exact) -d = rand(RR; randtype = :special) +d = rand(RR; randtype = :special) diff --git a/dev/complex/index.html b/dev/complex/index.html index 4bb616b2e..9a0b4c55d 100644 --- a/dev/complex/index.html +++ b/dev/complex/index.html @@ -172,4 +172,4 @@ 0 0 0 - 1 + 1 diff --git a/dev/constructors/index.html b/dev/constructors/index.html index f88611975..efae2fb9c 100644 --- a/dev/constructors/index.html +++ b/dev/constructors/index.html @@ -9,4 +9,4 @@ x^3 + 3*x + 1 julia> g = R(12) -12

In this example, $R$ is the parent object and we use it to convert the Int value $12$ to an element of the polynomial ring $\mathbb{Z}[x]$.

List of parent object constructors

For convenience, we provide a list of all the parent object constructors in Nemo and explain what domains they represent.

MathematicsNemo constructor
$R = \mathbb{Z}$R = ZZ
$R = \mathbb{Q}$R = QQ
$R = \mathbb{F}_{p^n}$R, a = finite_field(p, n, "a")
$R = \mathbb{Z}/n\mathbb{Z}$R, = residue_ring(ZZ, n)
$S = R[x]$S, x = polynomial_ring(R, "x")
$S = R[x, y]$S, (x, y) = polynomial_ring(R, ["x", "y"])
$S = R[[x]]$ (to precision $n$)S, x = power_series_ring(R, n, "x")
$S = R((x))$ (to precision $n$)S, x = laurent_series_ring(R, n, "x")
$S = \mathrm{Frac}_R$S = fraction_field(R)
$S = R/(f)$S, = residue_ring(R, f)
$S = \mathrm{Mat}_{m\times n}(R)$S = matrix_space(R, m, n)
$S = \mathbb{Q}[x]/(f)$S, a = number_field(f, "a")
$S = \mathbb{Q}_p$ (to precision $N$)S = PadicField(p, n)
$S = \mathbb{R}$S = RealField()
$S = \mathbb{C}$S = ComplexField()
+12

In this example, $R$ is the parent object and we use it to convert the Int value $12$ to an element of the polynomial ring $\mathbb{Z}[x]$.

List of parent object constructors

For convenience, we provide a list of all the parent object constructors in Nemo and explain what domains they represent.

MathematicsNemo constructor
$R = \mathbb{Z}$R = ZZ
$R = \mathbb{Q}$R = QQ
$R = \mathbb{F}_{p^n}$R, a = finite_field(p, n, "a")
$R = \mathbb{Z}/n\mathbb{Z}$R, = residue_ring(ZZ, n)
$S = R[x]$S, x = polynomial_ring(R, "x")
$S = R[x, y]$S, (x, y) = polynomial_ring(R, ["x", "y"])
$S = R[[x]]$ (to precision $n$)S, x = power_series_ring(R, n, "x")
$S = R((x))$ (to precision $n$)S, x = laurent_series_ring(R, n, "x")
$S = \mathrm{Frac}_R$S = fraction_field(R)
$S = R/(f)$S, = residue_ring(R, f)
$S = \mathrm{Mat}_{m\times n}(R)$S = matrix_space(R, m, n)
$S = \mathbb{Q}[x]/(f)$S, a = number_field(f, "a")
$S = \mathbb{Q}_p$ (to precision $N$)S = PadicField(p, n)
$S = \mathbb{R}$S = RealField()
$S = \mathbb{C}$S = ComplexField()
diff --git a/dev/developer/conventions/index.html b/dev/developer/conventions/index.html index c624c209b..14ff0eaf4 100644 --- a/dev/developer/conventions/index.html +++ b/dev/developer/conventions/index.html @@ -1,2 +1,2 @@ -Conventions · Nemo.jl

Conventions

AbstractAlgebra and Nemo have adopted a number of conventions to help maintain a uniform codebase.

Code conventions

Function and type names

Names of types in Julia follow the convention of CamelCase where the first letter of each word is capitalised, e.g. Int64 and AbstractString.

Function/method names in Julia use all lowercase with underscores between the words, e.g. zip and jacobi_symbol.

We follow these conventions in Nemo with some exceptions:

  • When interfacing C libraries the types use the same spelling and capitalisation in Nemo as they do in C, e.g. the Flint library's ZZPolyRingElem remains uncapitalised in Nemo.

  • Types such as fpPolyRingElem which don't exist under that name on the C side also use the lowercase convention as they wrap an actual C type which must be split into more than one type on the Julia side. For example zzModPolyRingElem and fpPolyRingElem on the Julia side both represent Flint zzModPolyRingElem's on the C side.

  • Types of rings and fields, modules, maps, etc. are capitalised whether they correspond to a C type or not, e.g. fqPolyRepField for the type of an object representing the field that fqPolyRepFieldElem's belong to.

.

  • We omit an underscore if the first word of a method is "is" or "has", e.g. iseven.

  • Underscores are omitted if the method name is already well established without an underscore in Julia itself, e.g. setindex.

  • Constructors with the same name as a type use the same spelling and capitalisation as that type, e.g. ZZRingElem(1).

  • Functions for creating rings, fields, modules, maps, etc. (rather than the elements thereof) use CamelCase, e.g. polynomial_ring. We refer to these functions as parent constructors. Note that we do not follow the Julia convention here, e.g. polynomial_ring is a function and not a type constructor (in fact we often return a tuple consisting of a parent object and other objects such as generators with this type of function) yet we capitalise it.

  • We prefer words to not be abbreviated, e.g. denominator instead of den.

  • Exceptions always exist where the result would be offensive in any major spoken language (example omitted).

It is easy to find counterexamples to virtually all these rules. However we have been making efforts to remove the most egregious cases from our codebase over time. As perfect consistency is not possible, work on this has to at times take a back seat.

Use of ASCII characters

All code and printed output in Nemo should use ASCII characters only. This is because we have developers who are using versions of the WSL that cannot correctly display non-ASCII characters.

This extends to function and operator names, which saves people having to learn how to enter them to use the system.

Spacing and tabs

All function bodies and control blocks should be indented using spaces.

A survey of existing code shows 2, 3 or 4 space indenting commonly used in our files. Values outside this range should not be used.

When contributing to an existing file, follow the majority convention in that file. Consistency within a file is valued highly.

If you are new to Nemo development and do not already have a very strong preference, new files should be started with 3 space indenting. This maximises the likelihood that copy and paste between files will be straightforward, though modern editors ease this to some degree.

Function signatures in docstrings should have four spaces before them.

Where possible, line lengths should not exceed 80 characters.

We use a term/factor convention for spacing. This means that all (additive) terms have spaces before and after them, (multiplicative) factors usually do not.

In practice this means that +, -, =, ==, !=, <, >, <=, >= all have spaces before and after them. The operators *, /, ^ and unary minus do not.

As per English, commas are followed by a single space in expressions. This applies for example to function arguments and tuples.

We do not put spaces immediately inside or before parentheses.

Colons used for ranges do not have spaces before or after them.

Logical operators, &, |, &&, etc. usually have spaces before and after them.

Comments

Despite appearances to the contrary, we now prefer code comments explaining the algorithm as it proceeds.

The hash when used for a comment should always be followed by a space. Full sentences are preferred.

We do not generally use comments in Nemo for questions, complaints or proposals for future improvement. These are better off in a ticket on GitHub with a discussion that will be brought to the attention of all relevant parties.

Any (necessary) limitations of the implementation should be noted in docstrings.

Layout of files

In Nemo, all types are places in special files with the word "Types" in their name, e.g. FlintTypes.jl. This is because Julia must be aware of all types before they are used. Separation of types from implementations makes it easy to ensure this happens.

Most implementation files present functions in a particular order, which is as follows:

  • A header stating what the file is for, and if needed, any copyright notices

  • Functions applying to any "types" used in the file, e.g. parent_type, elem_type, base_ring, parent, check_parent.

  • Basic manipulation, including hashes, predicates, getters/setters, functions for creating special values (e.g. one, zero and the like), deepcopy_internal. These are usually fairly short functions, often a single line.

  • Indexing (getindex, setindex), iteration, views.

  • String I/O (expressify and file access, etc.)

  • Arithmetic operations, usually in multiple sections, such as unary operations, binary operations, ad hoc binary operations (e.g. multiplication of a complex object by a scalar), comparisons, ad hoc comparisons, division, etc.

  • More complex functionality separated into sections based on functionality provided, e.g. gcd, interpolation, special functions, solving, etc.

  • Functions for mapping between different types, coercion, changing base ring, etc.

  • Unsafe operators, e.g. mul!, add!, etc.

  • Random generation

  • Promotion rules

  • Parent object call overload (e.g. for implementing R(2) where R is an object representing a ring or field, etc.)

  • Additional constructors, e.g. matrix, which might be used instead of a parent object to construct elements.

  • Parent object constructors, e.g. polynomial_ring, etc.

The exact order within the file is less important than generally following something like the above. This aids in finding functions in a file since all files are more or less set out the same way.

For an example to follow, see the src/Poly.jl and src/generic/Poly.jl files in AbstractAlgebra which form the oldest and most canonical example.

Headings for sections should be 80 characters wide and formed of hashes in the style that can be seen in each Nemo file.

+Conventions · Nemo.jl

Conventions

AbstractAlgebra and Nemo have adopted a number of conventions to help maintain a uniform codebase.

Code conventions

Function and type names

Names of types in Julia follow the convention of CamelCase where the first letter of each word is capitalised, e.g. Int64 and AbstractString.

Function/method names in Julia use all lowercase with underscores between the words, e.g. zip and jacobi_symbol.

We follow these conventions in Nemo with some exceptions:

  • When interfacing C libraries the types use the same spelling and capitalisation in Nemo as they do in C, e.g. the Flint library's ZZPolyRingElem remains uncapitalised in Nemo.

  • Types such as fpPolyRingElem which don't exist under that name on the C side also use the lowercase convention as they wrap an actual C type which must be split into more than one type on the Julia side. For example zzModPolyRingElem and fpPolyRingElem on the Julia side both represent Flint zzModPolyRingElem's on the C side.

  • Types of rings and fields, modules, maps, etc. are capitalised whether they correspond to a C type or not, e.g. fqPolyRepField for the type of an object representing the field that fqPolyRepFieldElem's belong to.

.

  • We omit an underscore if the first word of a method is "is" or "has", e.g. iseven.

  • Underscores are omitted if the method name is already well established without an underscore in Julia itself, e.g. setindex.

  • Constructors with the same name as a type use the same spelling and capitalisation as that type, e.g. ZZRingElem(1).

  • Functions for creating rings, fields, modules, maps, etc. (rather than the elements thereof) use CamelCase, e.g. polynomial_ring. We refer to these functions as parent constructors. Note that we do not follow the Julia convention here, e.g. polynomial_ring is a function and not a type constructor (in fact we often return a tuple consisting of a parent object and other objects such as generators with this type of function) yet we capitalise it.

  • We prefer words to not be abbreviated, e.g. denominator instead of den.

  • Exceptions always exist where the result would be offensive in any major spoken language (example omitted).

It is easy to find counterexamples to virtually all these rules. However we have been making efforts to remove the most egregious cases from our codebase over time. As perfect consistency is not possible, work on this has to at times take a back seat.

Use of ASCII characters

All code and printed output in Nemo should use ASCII characters only. This is because we have developers who are using versions of the WSL that cannot correctly display non-ASCII characters.

This extends to function and operator names, which saves people having to learn how to enter them to use the system.

Spacing and tabs

All function bodies and control blocks should be indented using spaces.

A survey of existing code shows 2, 3 or 4 space indenting commonly used in our files. Values outside this range should not be used.

When contributing to an existing file, follow the majority convention in that file. Consistency within a file is valued highly.

If you are new to Nemo development and do not already have a very strong preference, new files should be started with 3 space indenting. This maximises the likelihood that copy and paste between files will be straightforward, though modern editors ease this to some degree.

Function signatures in docstrings should have four spaces before them.

Where possible, line lengths should not exceed 80 characters.

We use a term/factor convention for spacing. This means that all (additive) terms have spaces before and after them, (multiplicative) factors usually do not.

In practice this means that +, -, =, ==, !=, <, >, <=, >= all have spaces before and after them. The operators *, /, ^ and unary minus do not.

As per English, commas are followed by a single space in expressions. This applies for example to function arguments and tuples.

We do not put spaces immediately inside or before parentheses.

Colons used for ranges do not have spaces before or after them.

Logical operators, &, |, &&, etc. usually have spaces before and after them.

Comments

Despite appearances to the contrary, we now prefer code comments explaining the algorithm as it proceeds.

The hash when used for a comment should always be followed by a space. Full sentences are preferred.

We do not generally use comments in Nemo for questions, complaints or proposals for future improvement. These are better off in a ticket on GitHub with a discussion that will be brought to the attention of all relevant parties.

Any (necessary) limitations of the implementation should be noted in docstrings.

Layout of files

In Nemo, all types are places in special files with the word "Types" in their name, e.g. FlintTypes.jl. This is because Julia must be aware of all types before they are used. Separation of types from implementations makes it easy to ensure this happens.

Most implementation files present functions in a particular order, which is as follows:

  • A header stating what the file is for, and if needed, any copyright notices

  • Functions applying to any "types" used in the file, e.g. parent_type, elem_type, base_ring, parent, check_parent.

  • Basic manipulation, including hashes, predicates, getters/setters, functions for creating special values (e.g. one, zero and the like), deepcopy_internal. These are usually fairly short functions, often a single line.

  • Indexing (getindex, setindex), iteration, views.

  • String I/O (expressify and file access, etc.)

  • Arithmetic operations, usually in multiple sections, such as unary operations, binary operations, ad hoc binary operations (e.g. multiplication of a complex object by a scalar), comparisons, ad hoc comparisons, division, etc.

  • More complex functionality separated into sections based on functionality provided, e.g. gcd, interpolation, special functions, solving, etc.

  • Functions for mapping between different types, coercion, changing base ring, etc.

  • Unsafe operators, e.g. mul!, add!, etc.

  • Random generation

  • Promotion rules

  • Parent object call overload (e.g. for implementing R(2) where R is an object representing a ring or field, etc.)

  • Additional constructors, e.g. matrix, which might be used instead of a parent object to construct elements.

  • Parent object constructors, e.g. polynomial_ring, etc.

The exact order within the file is less important than generally following something like the above. This aids in finding functions in a file since all files are more or less set out the same way.

For an example to follow, see the src/Poly.jl and src/generic/Poly.jl files in AbstractAlgebra which form the oldest and most canonical example.

Headings for sections should be 80 characters wide and formed of hashes in the style that can be seen in each Nemo file.

diff --git a/dev/developer/interfaces/index.html b/dev/developer/interfaces/index.html index 974bb2607..cadca21f6 100644 --- a/dev/developer/interfaces/index.html +++ b/dev/developer/interfaces/index.html @@ -1,2 +1,2 @@ -Interfaces · Nemo.jl

Interfaces

Functionality for Generic and Abstract Types

As previously mentioned, Nemo provides various generic types, e.g. Poly{T} for generic univariate polynomials and Mat{T} for generic matrices over a base ring. These and other polynomial and matrix types belong in turn to abstract types or unions thereof, e.g. PolyRingElem{T} is an abstract type representing all univariate polynomial types and MatrixElem{T} is a union of all Nemo matrix types.

When implementing generic functionality, one should usually implement it for the abstract types and unions thereof, since the new functionality will then work for all types of the specified kind, instead of just the generic types.

In order for this to work in practice, such implementations can only use functions in the relevant official interface. These are the functions required to be implemented by all types of that kind. For example, matrix implementations make heavy use of add! and mul! to accumulate entries, but they cannot make use of functions such as subeq! as it is not part of the official interface.

In addition to implementations for abstract types and their unions, one may also like to provide specialised implementations for the generic types e.g. Poly{T} and Mat{T} as one would for other specialised types. The generic types are based on Julia arrays internally, and so it makes perfect sense to implement lower level functionality for these types specifically, as this may lead to performance gains. Such specialised implementations can make use of any functions provided for the generic types, whether in the interface or not.

For convenience we list the most important abstract types and their unions for which one should usually prefer to write generic implementations.

  • PolyRingElem{T} : all univariate polynomial types
  • MPolyRingElem{T} : all multivariate polynomial types (see note below)
  • MatrixElem{T} : union of all matrix types including matrix algebras
  • MatElem{T} : all matrix types not including matrix algebras
  • AbsPowerSeriesRingElem{T} : all abstract series types
  • RelPowerSeriesRingElem{T} : all relative series types
  • LaurentSeriesElem{T} : union of all Laurent series over rings and fields
  • PuiseuxSeriesElem{T} : union of all Puiseux series over rings and fields
  • FPModule{T} : all finitely presented modules over a Euclidean domain
  • FPModuleElem{T} : all elems of fin. presented modules over a Euc. domain
  • FracElem{T} : all fractions
  • ResElem{T} : all elements of a residue ring
  • ResFieldElem{T} : all elements of a residue field
  • Map{D, C} : all maps (see Maps developer docs for a description)

N.B: inside the Generic submodule of AbstractAlgebra some abstract types Blah are only accessible by writing AbstractAlgebra.Blah. The unions are directly accessible. There may be generic types and abstract types with the same name, so this is more than just a convention.

Note that multivariate polynomials tend to require very specialised implementations depending heavily on implementation details of the specific multivariate type. Therefore it is rare to write implementations for the abstract type MPolyRingElem{T}. Instead, implementations tend to be done for each concrete multivariate type separately.

Generic interfaces

As mentioned above, the generic implementations in Nemo depend on carefully written interfaces for each of the abstract types provided by the system.

These interfaces are spelled out in the AbstractAlgebra documentation. Note that a generic implementation may depend on functions in both the required and optional interfaces as the optional functions are all implemented with generic fallbacks in terms of the required functions.

For convenience we provide here a list of interfaces that can be relied on in generic implementations, along with a description.

  • Ring : all commutative rings in the system
  • Field : all fields in the system
  • NCRing : all rings in the system (not necessarily commutative)
  • Euclidean Ring : Euclidean rings (see notes below)
  • Univariate Polynomial Ring : all dense univariate polynomials
  • Multivariate Polynomial Ring : all sparse distributed multivariate polys.
  • Series Ring : all series, relative and absolute
  • Residue Ring : all quotients of gcd domains with gcdx by a principal ideal
  • Fraction Field : all fractions over a gcd domain with gcdx
  • Module : all finitely presented modules over a Euclidean domain
  • Matrix : all matrices over a commutative ring
  • Map : all (set) maps in the system

Although we allow Z/nZ in our definition of Euclidean ring, much of the functionality in Nemo can be expected to misbehave (impossible inverses, etc.) when working with Euclidean rings that are not domains. In some cases the algorithms just don't exist, and in other cases we simply haven't implemented the required functionality to support all Euclidean rings for which computations can be done.

Whether a ring is a Euclidean domain or not cannot be encoded in the type. Thus there is no abstract type for Euclidean domains or their elements. Instead, generic functions rely on the existence of certain functions such as gcdx to implement functionality for Euclidean domains.

There is also currently no way to define a Euclidean function for a given ring (which is known to be Euclidean) and have the system recognise the ring as such. This kind of Euclidean interface may be provided in a future version of Nemo.

Julia interfaces we support

Many Julia interfaces rely on being able to create zero and one elements given the type only. As we use the parent/element model (see developer notes on this topic) we cannot support all Julia interfaces fully.

We do however partially implement some Julia interfaces.

  • Iteration : iterators are currently provided for multivariate polynomials to iterate over the coefficients, terms and monomials. Nemo matrices can also be iterated over. Iteration proceeds down each column in turn. One can also iterate over all permutations and partitions. Finally, all finite field types can be iterated over.

  • Views : because C libraries cannot be expected to implement the full range of Julia view types, views of matrices in Nemo can only be constructed for submatrices consisting of contiguous blocks in the original matrix.

  • map and similar : we implement the map and similar interfaces with the caveat that we generally use parent objects where Julia would use types. See the specific documentation for the module of interest to see details.

  • zero and one : these are implemented for parent types, which is not what Julia typically expects. Exceptions include the Flint ZZRingElem and QQFieldElem types, as their parents are not parameterised, which makes it possible to implement these functions for the types as well as the parents.

  • rand : we have a Nemo specific rand interface, which passes the tail of a given rand invocation to the rand function for the base ring, e.g. to create random matrix elements or polynomial coefficients and so on. In addition to this custom rand interface, we also support much of the Julia rand interface, with the usual caveat that we use parent objects instead of types where necessary.

  • serialisation : unfortunately this is currently NOT implemented by Nemo, but we would certainly like to see that done in the future. It's not automatic because of the C objects that underly many of our constructions.

  • Number : Nemo number types do NOT belong to Julia's Number hierarchy, as we must make all our ring element types belong to our RingElem abstract type. To make some Julia Number types cooperate with Nemo, we define the unions RingElement and FieldElement which include some Julia types, such as BigInt and Rational{BigInt}, etc. Note that fixed precision integer types cannot be expected to be well-behaved when they overflow. We recommend using Nemo integer types if one wants good performance for small machine word sized integers, but no overflow when the integer becomes large (Nemo integers are based on Flint's multiprecision ZZRingElem type).

  • hash : we implement hash functions for all major element types in Nemo.

  • getindex/setindex!/typed_hvcat : we implement these to access elements of Nemo matrices, however see the note below on row major representation. In addition, we allow creation of matrices using the notation R[a b; c d] etc. This is done by overloading typed_hvcat for the parent object R instead of a type as Julia would normally expect. This produces a Nemo matrix rather than a Julia one. Note that when passed a type, Julia's typed_hvcat can only construct Julia matrices for Nemo types such as ZZRingElem and QQFieldElem where elements can be constructed from types alone.

Many other Julia interfaces are either not yet implemented or only very partially implemented.

Column major vs row major matrices

Whereas Julia uses column major representation for its matrices, Nemo follows the convention of the C libraries it wraps and uses row major representation. Although Julia 2-D arrays are used internally in Nemo's generic matrix type, the interface from the perspective of the user is still the Nemo row major convention, not the Julia column major convention.

In row major representation, some row operations may be able to be performed more cheaply than similar column operations. In column major representation the converse is true. This may mean that some Julia matrix implementations may perform more slowly if naively ported to Nemo matrices, unless suitably modified.

+Interfaces · Nemo.jl

Interfaces

Functionality for Generic and Abstract Types

As previously mentioned, Nemo provides various generic types, e.g. Poly{T} for generic univariate polynomials and Mat{T} for generic matrices over a base ring. These and other polynomial and matrix types belong in turn to abstract types or unions thereof, e.g. PolyRingElem{T} is an abstract type representing all univariate polynomial types and MatrixElem{T} is a union of all Nemo matrix types.

When implementing generic functionality, one should usually implement it for the abstract types and unions thereof, since the new functionality will then work for all types of the specified kind, instead of just the generic types.

In order for this to work in practice, such implementations can only use functions in the relevant official interface. These are the functions required to be implemented by all types of that kind. For example, matrix implementations make heavy use of add! and mul! to accumulate entries, but they cannot make use of functions such as subeq! as it is not part of the official interface.

In addition to implementations for abstract types and their unions, one may also like to provide specialised implementations for the generic types e.g. Poly{T} and Mat{T} as one would for other specialised types. The generic types are based on Julia arrays internally, and so it makes perfect sense to implement lower level functionality for these types specifically, as this may lead to performance gains. Such specialised implementations can make use of any functions provided for the generic types, whether in the interface or not.

For convenience we list the most important abstract types and their unions for which one should usually prefer to write generic implementations.

  • PolyRingElem{T} : all univariate polynomial types
  • MPolyRingElem{T} : all multivariate polynomial types (see note below)
  • MatrixElem{T} : union of all matrix types including matrix algebras
  • MatElem{T} : all matrix types not including matrix algebras
  • AbsPowerSeriesRingElem{T} : all abstract series types
  • RelPowerSeriesRingElem{T} : all relative series types
  • LaurentSeriesElem{T} : union of all Laurent series over rings and fields
  • PuiseuxSeriesElem{T} : union of all Puiseux series over rings and fields
  • FPModule{T} : all finitely presented modules over a Euclidean domain
  • FPModuleElem{T} : all elems of fin. presented modules over a Euc. domain
  • FracElem{T} : all fractions
  • ResElem{T} : all elements of a residue ring
  • ResFieldElem{T} : all elements of a residue field
  • Map{D, C} : all maps (see Maps developer docs for a description)

N.B: inside the Generic submodule of AbstractAlgebra some abstract types Blah are only accessible by writing AbstractAlgebra.Blah. The unions are directly accessible. There may be generic types and abstract types with the same name, so this is more than just a convention.

Note that multivariate polynomials tend to require very specialised implementations depending heavily on implementation details of the specific multivariate type. Therefore it is rare to write implementations for the abstract type MPolyRingElem{T}. Instead, implementations tend to be done for each concrete multivariate type separately.

Generic interfaces

As mentioned above, the generic implementations in Nemo depend on carefully written interfaces for each of the abstract types provided by the system.

These interfaces are spelled out in the AbstractAlgebra documentation. Note that a generic implementation may depend on functions in both the required and optional interfaces as the optional functions are all implemented with generic fallbacks in terms of the required functions.

For convenience we provide here a list of interfaces that can be relied on in generic implementations, along with a description.

  • Ring : all commutative rings in the system
  • Field : all fields in the system
  • NCRing : all rings in the system (not necessarily commutative)
  • Euclidean Ring : Euclidean rings (see notes below)
  • Univariate Polynomial Ring : all dense univariate polynomials
  • Multivariate Polynomial Ring : all sparse distributed multivariate polys.
  • Series Ring : all series, relative and absolute
  • Residue Ring : all quotients of gcd domains with gcdx by a principal ideal
  • Fraction Field : all fractions over a gcd domain with gcdx
  • Module : all finitely presented modules over a Euclidean domain
  • Matrix : all matrices over a commutative ring
  • Map : all (set) maps in the system

Although we allow Z/nZ in our definition of Euclidean ring, much of the functionality in Nemo can be expected to misbehave (impossible inverses, etc.) when working with Euclidean rings that are not domains. In some cases the algorithms just don't exist, and in other cases we simply haven't implemented the required functionality to support all Euclidean rings for which computations can be done.

Whether a ring is a Euclidean domain or not cannot be encoded in the type. Thus there is no abstract type for Euclidean domains or their elements. Instead, generic functions rely on the existence of certain functions such as gcdx to implement functionality for Euclidean domains.

There is also currently no way to define a Euclidean function for a given ring (which is known to be Euclidean) and have the system recognise the ring as such. This kind of Euclidean interface may be provided in a future version of Nemo.

Julia interfaces we support

Many Julia interfaces rely on being able to create zero and one elements given the type only. As we use the parent/element model (see developer notes on this topic) we cannot support all Julia interfaces fully.

We do however partially implement some Julia interfaces.

  • Iteration : iterators are currently provided for multivariate polynomials to iterate over the coefficients, terms and monomials. Nemo matrices can also be iterated over. Iteration proceeds down each column in turn. One can also iterate over all permutations and partitions. Finally, all finite field types can be iterated over.

  • Views : because C libraries cannot be expected to implement the full range of Julia view types, views of matrices in Nemo can only be constructed for submatrices consisting of contiguous blocks in the original matrix.

  • map and similar : we implement the map and similar interfaces with the caveat that we generally use parent objects where Julia would use types. See the specific documentation for the module of interest to see details.

  • zero and one : these are implemented for parent types, which is not what Julia typically expects. Exceptions include the Flint ZZRingElem and QQFieldElem types, as their parents are not parameterised, which makes it possible to implement these functions for the types as well as the parents.

  • rand : we have a Nemo specific rand interface, which passes the tail of a given rand invocation to the rand function for the base ring, e.g. to create random matrix elements or polynomial coefficients and so on. In addition to this custom rand interface, we also support much of the Julia rand interface, with the usual caveat that we use parent objects instead of types where necessary.

  • serialisation : unfortunately this is currently NOT implemented by Nemo, but we would certainly like to see that done in the future. It's not automatic because of the C objects that underly many of our constructions.

  • Number : Nemo number types do NOT belong to Julia's Number hierarchy, as we must make all our ring element types belong to our RingElem abstract type. To make some Julia Number types cooperate with Nemo, we define the unions RingElement and FieldElement which include some Julia types, such as BigInt and Rational{BigInt}, etc. Note that fixed precision integer types cannot be expected to be well-behaved when they overflow. We recommend using Nemo integer types if one wants good performance for small machine word sized integers, but no overflow when the integer becomes large (Nemo integers are based on Flint's multiprecision ZZRingElem type).

  • hash : we implement hash functions for all major element types in Nemo.

  • getindex/setindex!/typed_hvcat : we implement these to access elements of Nemo matrices, however see the note below on row major representation. In addition, we allow creation of matrices using the notation R[a b; c d] etc. This is done by overloading typed_hvcat for the parent object R instead of a type as Julia would normally expect. This produces a Nemo matrix rather than a Julia one. Note that when passed a type, Julia's typed_hvcat can only construct Julia matrices for Nemo types such as ZZRingElem and QQFieldElem where elements can be constructed from types alone.

Many other Julia interfaces are either not yet implemented or only very partially implemented.

Column major vs row major matrices

Whereas Julia uses column major representation for its matrices, Nemo follows the convention of the C libraries it wraps and uses row major representation. Although Julia 2-D arrays are used internally in Nemo's generic matrix type, the interface from the perspective of the user is still the Nemo row major convention, not the Julia column major convention.

In row major representation, some row operations may be able to be performed more cheaply than similar column operations. In column major representation the converse is true. This may mean that some Julia matrix implementations may perform more slowly if naively ported to Nemo matrices, unless suitably modified.

diff --git a/dev/developer/introduction/index.html b/dev/developer/introduction/index.html index be6cdfc24..d16981ada 100644 --- a/dev/developer/introduction/index.html +++ b/dev/developer/introduction/index.html @@ -4,4 +4,4 @@ git push --all

However, if you are working on a much larger project it is highly recommended that you frequently pull from the official master branch and rebase your new branch on top of any changes that have been made there:

git checkout master
 git pull
 git checkout mynewbranch
-git rebase master

Note that rebasing will try to rewrite each of your commits over the top of the branch you are rebasing on (master in this case). This process will have many steps if there are many commits and lots of conflicts. Simply follow the instructions until the process is finished.

The longer you leave it before rebasing on master the longer the rebase process will take. It can eventually become overwhelming as it is not replaying the latest state of your repository over master, but each commit that you made in order. You may have completely forgotten what those older commits were about, so this can become very difficult if not done regularly.

Once you have pushed your changes to your GitHub account, go to the official project GitHub page and you should see your branch mentioned near the top of the page. Open a pull request.

Someone will review your code and suggest changes they'd like made. Simply add more commits to your branch and push again. They will automatically get added to your pull request.

Note that we don't accept code without tests and documentation. We use Documenter.jl for our documentation, in Markdown format. See our existing code for examples of docstrings above functions in the source code and look in the docs/src directory to see how these docstrings are merged into our online documentation.

Development list

All developers of AbstractAlgebra and Nemo are welcome to write to our development list to ask questions and discuss development:

https://groups.google.com/g/nemo-devel

Reporting bugs

Bugs should be reported by opening an issue (ticket) on the official GitHub page for the relevant project. Please state the Julia version being used, the machine you are using and the version of AbstractAlgebra/Nemo you are using. The version can be found in the Project.toml file at the top level of the source tree.

Development roadmap

AbstractAlgebra has a special roadmap ticket which lists the most important tickets that have been opened. If you want to contribute something high value this is the place to start:

https://github.com/Nemocas/AbstractAlgebra.jl/issues/492

This ticket is updated every so often.

Binaries

Binaries of C libraries for Nemo are currently made in a separate repository:

https://github.com/JuliaPackaging/Yggdrasil

If code is added to any of the C libraries used by Nemo, this jll package must be updated first and the version updated in Nemo.jl before the new functionality can be used. Ask the core developers for help with this as various other tasks must be completed at the same time.

Relationship to Oscar

Nemo and AbstractAlgebra are heavily used by the Oscar computer algebra system being developed in Germany by a number of universities involved in a large project known as TRR 195, funded by the DFG.

Oscar is the number one customer for Nemo. Many bugs in Nemo are found and fixed by Oscar developers and most of the key Nemo developers are part of the Oscar project.

See the Oscar website for further details:

https://www.oscar-system.org/

+git rebase master

Note that rebasing will try to rewrite each of your commits over the top of the branch you are rebasing on (master in this case). This process will have many steps if there are many commits and lots of conflicts. Simply follow the instructions until the process is finished.

The longer you leave it before rebasing on master the longer the rebase process will take. It can eventually become overwhelming as it is not replaying the latest state of your repository over master, but each commit that you made in order. You may have completely forgotten what those older commits were about, so this can become very difficult if not done regularly.

Once you have pushed your changes to your GitHub account, go to the official project GitHub page and you should see your branch mentioned near the top of the page. Open a pull request.

Someone will review your code and suggest changes they'd like made. Simply add more commits to your branch and push again. They will automatically get added to your pull request.

Note that we don't accept code without tests and documentation. We use Documenter.jl for our documentation, in Markdown format. See our existing code for examples of docstrings above functions in the source code and look in the docs/src directory to see how these docstrings are merged into our online documentation.

Development list

All developers of AbstractAlgebra and Nemo are welcome to write to our development list to ask questions and discuss development:

https://groups.google.com/g/nemo-devel

Reporting bugs

Bugs should be reported by opening an issue (ticket) on the official GitHub page for the relevant project. Please state the Julia version being used, the machine you are using and the version of AbstractAlgebra/Nemo you are using. The version can be found in the Project.toml file at the top level of the source tree.

Development roadmap

AbstractAlgebra has a special roadmap ticket which lists the most important tickets that have been opened. If you want to contribute something high value this is the place to start:

https://github.com/Nemocas/AbstractAlgebra.jl/issues/492

This ticket is updated every so often.

Binaries

Binaries of C libraries for Nemo are currently made in a separate repository:

https://github.com/JuliaPackaging/Yggdrasil

If code is added to any of the C libraries used by Nemo, this jll package must be updated first and the version updated in Nemo.jl before the new functionality can be used. Ask the core developers for help with this as various other tasks must be completed at the same time.

Relationship to Oscar

Nemo and AbstractAlgebra are heavily used by the Oscar computer algebra system being developed in Germany by a number of universities involved in a large project known as TRR 195, funded by the DFG.

Oscar is the number one customer for Nemo. Many bugs in Nemo are found and fixed by Oscar developers and most of the key Nemo developers are part of the Oscar project.

See the Oscar website for further details:

https://www.oscar-system.org/

diff --git a/dev/developer/parents/index.html b/dev/developer/parents/index.html index 79ebcf7b4..68f6eddb3 100644 --- a/dev/developer/parents/index.html +++ b/dev/developer/parents/index.html @@ -1,2 +1,2 @@ -Parent objects · Nemo.jl

Parent objects

The use of parent objects in Nemo

The parent/element model

As for other major computer algebra projects such as Sage and Magma, Nemo uses the parent/element model to manage its mathematical objects.

As explained in the appendix to the AbstractAlgebra documentation, the standard type/object model used in most programming languages is insufficient for much of mathematics which often requires mathematical structures parameterised by other objects.

For example a quotient ring by an ideal would be parameterised by the ideal. The ideal is an object in the system and not a type and so parameterised types are not sufficient to represent such quotient rings.

This means that each mathematical "domain" in the system (set, group, ring, field, module, etc.) must be represented by an object in the system, rather than a type. Such objects are called parent objects.

Just as one would write typeof(a) to get the type of an object a in an object/type system of a standard programming language, we write parent(a) to return the parent of the object a.

When talked about with reference to a parent in this way, the object a is referred to as an element of the parent. Thus the system is divided into elements and parents. For example a polynomial would be an element of a polynomial ring, the latter being the parent of the former.

Naturally the parent/element system leads to some issues in a programming language not built around this model. We discuss some of these issues below.

Types in the parent/element model

As all elements and parents in Nemo are objects, those objects have types which we refer to as the element type and parent type respectively.

For example, Flint integers have type ZZRingElem and the parent object they all belong to, ZZ has type ZZRing.

More complex parents and elements are parameterised. For example, generic univariate polynomials over a base ring R are parameterised by R. The base ring of a ring S can be obtained by the call base_ring(S).

We have found it extremely useful to parameterise the type of both the parent and element objects of such a ring by the type of the elements of the base ring. Thus for example, a generic polynomial with Flint integer coefficients would have type Poly{ZZRingElem}.

In practice Flint already implements univariate polynomials over Flint integers, and these have type ZZPolyRingElem. But both ZZPolyRingElem and the generic polynomials Poly{ZZRingElem} belong to the abstract type PolyRingElem{ZZRingElem} making it possible to write functions for all univariate polynomials over Flint integers.

Given a specific element type or parent type it is possible to compute one from the other with the functions elem_type and parent_type. For example parent_type(ZZPolyRingElem) returns ZZPolyRing and elem_type(ZZPolyRing) returns ZZPolyRingElem. Similarly parent_type(Generic.Poly{ZZRingElem}) returns Generic.PolyRing{ZZRingElem} and so on.

These functions are especially useful when writing type assertions or constructing arrays of elements insides function where only the parent object was passed.

Other functions for computing types

Sometimes one needs to know the type of a polynomial or matrix one would obtain if it were constructed over a given ring or with coefficients/entries of a given element type.

This is especially important in generic code where it may not even be known which Julia package is being used. The user may be expecting an AbstractAlgebra object, a Nemo object or even some other kind of object to be constructed, depending on which package they are using.

The function for returning the correct type for a dense matrix is dense_matrix_type to which one can pass either a base ring or an element type. For example, if AbstractAlgebra is being used, dense_matrix_type(ZZ) will return Mat{BigInt} whereas if Nemo is being used it will return ZZMatrix.

We also have dense_poly_type for univariate polynomials, abs_series_type for absolute series and rel_series_type for relative series.

In theory such functions should exist for all major object types, however they have in most cases not been implemented yet.

Functions for creating objects of a similar type

A slightly more consistent interface for creating objects of a type that is suitable for the package currently in use is the similar interface.

For example, given a matrix M one can create one with the same dimensions but over a different ring R by calling similar(M, R). Likewise one can create one over the same ring with different dimensions r x c by calling similar(M, r, c).

The similar system is sophisticated enough to know that there is no native type provided by Flint/Antic for matrices and polynomials over a number field. The system knows that in such cases it must create a generic matrix or polynomial over the given number field.

A great deal of thought went into the design of the similar system so that developers would not be required to implement similar for every pair of types in the package.

Again this interface should exist for all major Nemo domains, but the functionality is still being implemented in some cases.

Changing base rings and map

Given a polynomial, matrix or other composite object over a base ring, it is often convenient to create a similar object but with all the entries or coefficients coerced into a different ring.

For this purpose the function change_base_ring is provided.

Similarly it may be useful to create the matrix or polynomial that results by applying a given map/function/lambda to each of the entries or coefficients.

For this purpose Julia's map function is overloaded. There are also functions specific to polynomials and matrices called map_coefficients and map_entries respectively, which essentially do the same thing.

Note that the implementation of such functions must make use of the functions discussed above to ensure that a matrix/polynomial of the right type is output.

Parent checking

When applying binary operations to a pair of elements of a given ring, it is useful to check that they are in fact elements of the same ring. This is not possible by checking the types alone. For example elements of $Z/7Z$ and $Z/3Z$ would have the same type but different parents (one parameterised by the integer 7, the other by the integer 3).

In order to perform such a check in a function one uses check_parent(a, b) where a and b are the objects one wishes to assert must have the same parent. If not, an exception is raised by check_parent.

Parent object constructors

Various functions are provided for constructing parent objects. For example a polynomial ring is constructed by calling a polynomial_ring function. Such functions are called parent object constructors.

In general parent object constructors are intended for the user and should only be used with great care in library code. There are a number of reasons for this.

One issue is that parent objects are allowed to be arbitrarily large, and they are frequently cached by the system. They can also perform arbitrary precomputations for the ring/field/module etc. that is being constructed. This can lead to excessive memory usage. It can also have odd effects when a behavior affecting change is applied to a cached parent and thus has effects in places where one might not expect it.

Secondly, those parent caches are global objects. This is problematic for any future attempts to parallelise library code. And if the caches are not regularly emptied, in the worst case memory usage can balloon excessively.

To deal with this, most parent object constructors take a cached keyword which specifies whether the parent object should be cached which library code should generally set to false. That said it is better overall to simply eschew the use of parent object constructors in library code and instead let parents be passed in via arguments (directly or indirectly, e.g. the parent or base ring of some argument might be a suitable parent for some return values or intermediate objects).

One may also use, where applicable, functions such as similar, zero, zero_matrix, identity_matrix, change_base_ring, map, etc. for constructing polynomials and matrices directly without a parent object.

However even when using these functions in library code, it is important to remember to pass cached=false so that the cache is not filled up by calls to the library code. This creates an additional problem, namely that if one uses polynomial say, to construct two polynomials over the same base ring, they will not be compatible in the sense that they will have different parents.

Forcing the creation of full parent objects into as few bottlenecks as possible will make it much easier for developers to remove problems associated with such calls when they arise in future.

+Parent objects · Nemo.jl

Parent objects

The use of parent objects in Nemo

The parent/element model

As for other major computer algebra projects such as Sage and Magma, Nemo uses the parent/element model to manage its mathematical objects.

As explained in the appendix to the AbstractAlgebra documentation, the standard type/object model used in most programming languages is insufficient for much of mathematics which often requires mathematical structures parameterised by other objects.

For example a quotient ring by an ideal would be parameterised by the ideal. The ideal is an object in the system and not a type and so parameterised types are not sufficient to represent such quotient rings.

This means that each mathematical "domain" in the system (set, group, ring, field, module, etc.) must be represented by an object in the system, rather than a type. Such objects are called parent objects.

Just as one would write typeof(a) to get the type of an object a in an object/type system of a standard programming language, we write parent(a) to return the parent of the object a.

When talked about with reference to a parent in this way, the object a is referred to as an element of the parent. Thus the system is divided into elements and parents. For example a polynomial would be an element of a polynomial ring, the latter being the parent of the former.

Naturally the parent/element system leads to some issues in a programming language not built around this model. We discuss some of these issues below.

Types in the parent/element model

As all elements and parents in Nemo are objects, those objects have types which we refer to as the element type and parent type respectively.

For example, Flint integers have type ZZRingElem and the parent object they all belong to, ZZ has type ZZRing.

More complex parents and elements are parameterised. For example, generic univariate polynomials over a base ring R are parameterised by R. The base ring of a ring S can be obtained by the call base_ring(S).

We have found it extremely useful to parameterise the type of both the parent and element objects of such a ring by the type of the elements of the base ring. Thus for example, a generic polynomial with Flint integer coefficients would have type Poly{ZZRingElem}.

In practice Flint already implements univariate polynomials over Flint integers, and these have type ZZPolyRingElem. But both ZZPolyRingElem and the generic polynomials Poly{ZZRingElem} belong to the abstract type PolyRingElem{ZZRingElem} making it possible to write functions for all univariate polynomials over Flint integers.

Given a specific element type or parent type it is possible to compute one from the other with the functions elem_type and parent_type. For example parent_type(ZZPolyRingElem) returns ZZPolyRing and elem_type(ZZPolyRing) returns ZZPolyRingElem. Similarly parent_type(Generic.Poly{ZZRingElem}) returns Generic.PolyRing{ZZRingElem} and so on.

These functions are especially useful when writing type assertions or constructing arrays of elements insides function where only the parent object was passed.

Other functions for computing types

Sometimes one needs to know the type of a polynomial or matrix one would obtain if it were constructed over a given ring or with coefficients/entries of a given element type.

This is especially important in generic code where it may not even be known which Julia package is being used. The user may be expecting an AbstractAlgebra object, a Nemo object or even some other kind of object to be constructed, depending on which package they are using.

The function for returning the correct type for a dense matrix is dense_matrix_type to which one can pass either a base ring or an element type. For example, if AbstractAlgebra is being used, dense_matrix_type(ZZ) will return Mat{BigInt} whereas if Nemo is being used it will return ZZMatrix.

We also have dense_poly_type for univariate polynomials, abs_series_type for absolute series and rel_series_type for relative series.

In theory such functions should exist for all major object types, however they have in most cases not been implemented yet.

Functions for creating objects of a similar type

A slightly more consistent interface for creating objects of a type that is suitable for the package currently in use is the similar interface.

For example, given a matrix M one can create one with the same dimensions but over a different ring R by calling similar(M, R). Likewise one can create one over the same ring with different dimensions r x c by calling similar(M, r, c).

The similar system is sophisticated enough to know that there is no native type provided by Flint/Antic for matrices and polynomials over a number field. The system knows that in such cases it must create a generic matrix or polynomial over the given number field.

A great deal of thought went into the design of the similar system so that developers would not be required to implement similar for every pair of types in the package.

Again this interface should exist for all major Nemo domains, but the functionality is still being implemented in some cases.

Changing base rings and map

Given a polynomial, matrix or other composite object over a base ring, it is often convenient to create a similar object but with all the entries or coefficients coerced into a different ring.

For this purpose the function change_base_ring is provided.

Similarly it may be useful to create the matrix or polynomial that results by applying a given map/function/lambda to each of the entries or coefficients.

For this purpose Julia's map function is overloaded. There are also functions specific to polynomials and matrices called map_coefficients and map_entries respectively, which essentially do the same thing.

Note that the implementation of such functions must make use of the functions discussed above to ensure that a matrix/polynomial of the right type is output.

Parent checking

When applying binary operations to a pair of elements of a given ring, it is useful to check that they are in fact elements of the same ring. This is not possible by checking the types alone. For example elements of $Z/7Z$ and $Z/3Z$ would have the same type but different parents (one parameterised by the integer 7, the other by the integer 3).

In order to perform such a check in a function one uses check_parent(a, b) where a and b are the objects one wishes to assert must have the same parent. If not, an exception is raised by check_parent.

Parent object constructors

Various functions are provided for constructing parent objects. For example a polynomial ring is constructed by calling a polynomial_ring function. Such functions are called parent object constructors.

In general parent object constructors are intended for the user and should only be used with great care in library code. There are a number of reasons for this.

One issue is that parent objects are allowed to be arbitrarily large, and they are frequently cached by the system. They can also perform arbitrary precomputations for the ring/field/module etc. that is being constructed. This can lead to excessive memory usage. It can also have odd effects when a behavior affecting change is applied to a cached parent and thus has effects in places where one might not expect it.

Secondly, those parent caches are global objects. This is problematic for any future attempts to parallelise library code. And if the caches are not regularly emptied, in the worst case memory usage can balloon excessively.

To deal with this, most parent object constructors take a cached keyword which specifies whether the parent object should be cached which library code should generally set to false. That said it is better overall to simply eschew the use of parent object constructors in library code and instead let parents be passed in via arguments (directly or indirectly, e.g. the parent or base ring of some argument might be a suitable parent for some return values or intermediate objects).

One may also use, where applicable, functions such as similar, zero, zero_matrix, identity_matrix, change_base_ring, map, etc. for constructing polynomials and matrices directly without a parent object.

However even when using these functions in library code, it is important to remember to pass cached=false so that the cache is not filled up by calls to the library code. This creates an additional problem, namely that if one uses polynomial say, to construct two polynomials over the same base ring, they will not be compatible in the sense that they will have different parents.

Forcing the creation of full parent objects into as few bottlenecks as possible will make it much easier for developers to remove problems associated with such calls when they arise in future.

diff --git a/dev/developer/topics/index.html b/dev/developer/topics/index.html index ea9d1aed1..a6a77e83e 100644 --- a/dev/developer/topics/index.html +++ b/dev/developer/topics/index.html @@ -3,4 +3,4 @@ for k = 1:ncols(X) A[i, j] = addmul_delayed_reduction!(A[i, j], x[i, k], y[k, j], C) end -A[i, j] = reduce!(A[i, j])

Here C is a temporary element of the same type as the other inputs which is used internally in addmul_delayed_reduction! if needed.

Notice the final call to reduce! to reduce the accumulated value after the accumulation loop has finished.

Note that mul_red! is never called directly but is called inside the generic implementation of addmul_delayed_reduction! for rings that support delayed reduction. That generic code falls back to a call to addmul! which in turn falls back to mul! and add! where delayed reduction or addmul! are not available.

+A[i, j] = reduce!(A[i, j])

Here C is a temporary element of the same type as the other inputs which is used internally in addmul_delayed_reduction! if needed.

Notice the final call to reduce! to reduce the accumulated value after the accumulation loop has finished.

Note that mul_red! is never called directly but is called inside the generic implementation of addmul_delayed_reduction! for rings that support delayed reduction. That generic code falls back to a call to addmul! which in turn falls back to mul! and add! where delayed reduction or addmul! are not available.

diff --git a/dev/developer/typesystem/index.html b/dev/developer/typesystem/index.html index e2cc4601e..8d3ddc33a 100644 --- a/dev/developer/typesystem/index.html +++ b/dev/developer/typesystem/index.html @@ -1,2 +1,2 @@ -The type system · Nemo.jl

The type system

Use of Julia types in Nemo

Concrete and abstract types

Julia does not provide a traditional class/inheritance approach to programming. Instead, the basic unit of its object oriented approach is the type definition (struct and mutable struct) and inheritance exists only on the function side of the language rather than data side. Julia provides a rich system of abstract types and unions on the data side and multimethods on the function side to effect this.

For example Julia's Number type is an abstract type containing all concrete types that behave like numbers, e.g. Int64, Float64, and so on.

Abstract types can also belong to other abstract types, forming a tree of abstract types.

In Nemo the most important abstract types are Ring and Field, with the latter belonging to the former so that all fields are rings, and the abstract types RingElem and FieldElem for the objects that represent elements of rings and fields, again with the latter abstract type belonging to the former.

Because this hierarchy of abstract types must form a tree, Julia is strictly speaking single inheritance, as each concrete and abstract type can belong to at most one other abstract type. For example, one could not have a diamond of abstract types with ExactField belonging to both Field and ExactRing.

Recovering aspects of multiple inheritance in Nemo

Various possibilities exist to get around the limitation that abstract types must form a 'tree' in Nemo and AbstractAlgebra.

One such possibility is union types. If a function should accept one of a number of concrete or abstract types that can't all be made to belong to a single abstract type due to this limitation then one can use a union type.

For example, Nemo defines RingElement to be a union of RingElem and all the Julia standard types which behave like ring elements, e.g. all Integer types and types of rationals with Integer components.

Other union types are defined in src/AbstractAlgebra.jl in AbstractAlgebra.

A second feature we make use of in Nemo is parameterised types. Each concrete and abstract type can take one or more parameters. These parameter can be any other type, either concrete or abstract. For example, in Julia Rational{T} is for rationals with numerator and denominator of type T.

A great deal of control over parameterised types is possible, e.g. one can restrict the type parameter T using a where clause, e.g. to write a function that accepts all rational types with integer components of the same type one can use the type Rational{T} where T <: Integer.

Nemo makes use of such parameterised types for generic ring constructions such as generic polynomial rings and matrices over a given base ring. The type of the elements of the base ring is substituted for the parameter T in any concrete instantiation of the types Poly{T} and Mat{T}, which are defined in AbstractAlgebra in src/generic/GenericTypes.jl.

The totality of all univariate polynomial types, including those of generic Poly{T} types and those coming from C libraries (such as ZZPolyRingElem), is represented by the abstract type PolyRingElem{T} which in turn belongs to RingElem, both defined in AbstractAlgebra in src/AbstractTypes.jl.

Similarly, the totality of all matrix types, including explicit C types like ZZMatrix and the generic Mat{T} types is given by the abstract type MatElem{T}, again defined in AbstractAlgebra in src/AbstractTypes.jl.

This hierarchy of types allows one to write functions at any level, e.g. for all univariate polynomial types, just those with a given base type T, or for a specific concrete type corresponding to just one kind of univariate polynomial.

A third possibility to get around the single inheritance limitation of Julia is type traits. There is currently no explicit compiler/language support for traits, however various implementations exist that make use of type parameters in tricky ways. This allows one to add 'traits' to types, so long as those traits can be expressed as types. In this way, types can have multiple 'properties' at the same time, instead of belonging to just a single abstract type.

Nemo does not currently use type traits, though the map types in Nemo do make use of a custom analogue of this.

Note that unlike class based systems that dispatch on the type of a (sometimes implicit) this or self parameter, Julia methods dispatch on the type of all arguments. This is a natural fit for mathematics where all sorts of ad hoc left and right operations may be required.

Encapsulation, maps and runtime flags

One limitation of the Julia approach is that the type of an object cannot be changed at runtime. For example one might like to insist that a given ring is in fact a field. There are three standard ways to handle this in Julia.

The first approach is to encapsulate the object in another object which does have the desired type. The second approach is to map the object to a different one of the required type (e.g. by applying a morphism). The third approach is to introduce data fields in the original type which can be changed at runtime, unlike its type. All three approaches come with downsides.

Encapsulation can be time consuming for the developer as methods which applied to the original object do not automatically apply to the encapsulated object. One can write methods which do, but this is not automatic.

Application of a map may come with a performance penalty and may be difficult for the user to navigate. Moreover, mutation of the resulting object does not result in mutation of the original object.

The third option of adding runtime data fields essentially takes one back to writing a (possibly bug ridden) interpreter. It relies on the developer implementing outer methods that make use of hand written control statements to determine which of a range of inner methods should be applied to the object. This misses the benefits of one of the main defining features of Julia, namely its multimethod system and can also make introspection more difficult.

Nemo does not apply any of these three approaches widely at present, though information which can only be known at runtime such as whether a ring is Euclidean will eventually have to be encoded using one of these three methods.

Nemo's custom map types

It makes sense that map types in Nemo should be parameterised by the element types of both the domain and codomain of the map, and of course all maps in the system should somehow belong to an abstract type Map.

This leads one to consider a two parameter system of types Map{D, C} where D and C are the domain and codomain types respectively.

One may also wish to implement various types of map, e.g. linear maps (where the map contains a matrix representing the map) or functional maps (where the map is implemented by a Julia function) and so on. Notionally one imagines doing this with a hierarchy of two parameter abstract types all ultimately belonging to Map{D, C} as the root of the tree.

This approach begins to break down when constructions from homological algebra begin to be applied to maps. In such cases, the maps themselves are the object of study and functions may be applied to maps to produce other maps.

The simplest such function is composition. In a system where composition of maps always results in a map of the same type, no problem arises with the straightforward approach outlined above.

However, for various reasons (including performance) it may not be desirable or even possible to construct a composition of two given maps using the same representation as the original maps. This means that the result of composing two maps of the same type may be a map of a different type, e.g. in the worst case a general composition type.

This problem makes many homological and category theoretic operations on maps difficult or impossible to implement.

Other operations which may be desirable to implement are caching of maps (e.g. where the map is extremely time consuming to compute, such as discrete logarithms) and attaching category theoretic information to maps. Such operations can be effected by encapsulating existing maps in objects containing the extra information, e.g. a cache or a category. However all the methods that applied to the original map objects now no longer apply to the encapsulated objects.

To work around these limitations Nemo implements a four parameter Map type, Map{D, C, T, U}.

The first two parameters are the domain and codomain types as discussed above.

The parameter T is a "map class" which is itself an abstract type existing in a hierarchy of abstract types. This parameter is best thought of as a trait, independent of the hierarchy of abstract types belonging to Map, giving additional flexibility to the map types in the system.

For example, T may be set to LinearMap or FunctionalMap. This may be useful if one wishes to distinguish maps in other ways, e.g. whether they are homomorphisms, isomorphisms, maps with section or retraction etc. As usual, offering traits partially gets around the single inheritance problem.

The final parameter U is used to allow maps of a given type U to be composed and still result in a map of type U, even though the concrete type of the composition is different to that of the original maps. Methods can be written for all maps of type U by matching this parameter, rather than matching on the concrete type U of the original maps.

For example, two maps with concrete type MyRingHomomorphism would belong to Map{D, C, T, MyRingHomomorphism} as would any composition of such maps, even if the concrete type of the composition was not a MyRingHomomorphism.

Naturally four parameter types are rather unwieldy and so various helper functions are provided to compute four parameter map types. In the first instance one still has the type Map{D, C} which will give the union of all map types whose first two parameters are D and C, and where the remaining two parameters are arbitrary.

However one can also pass a map class or a concrete type U to a Map function to compute the class of all maps of the given map class or type.

For example, to write a function which accepts all maps of "type" MyRingHomomorphism, including all compositions of such maps, one inserts Map(MyRingHomomorphism) in place of the type, e.g.

function myfun(f::Map(MyRingHomomorphism))

Note the parentheses here, rather than curly braces; it's a function to compute a type! Now the function myfun will accept any map type whose fourth parameter U is set to MyRingHomomorphism.

This four parameter system is flexible, but may need to be expanded in the future. For example it may be useful to have more than one trait T. This could be achieved either by making T a tuple of traits or by introducing a parameterised MapTrait type which can be placed at that location. Naturally the Map functions for computing the four parameter types will have to be similarly expanded to make it easier for the user.

The map type system is currently considered experimental and our observation so far is that it is not intuitive for developers.

Type hierarchy diagram

The most important abstract types in the system are the element types. Their hierarchy is shown in the following diagram.

alt text

Most of the element types have a corresponding parent abstract type. These are shown in the following diagram.

alt text

+The type system · Nemo.jl

The type system

Use of Julia types in Nemo

Concrete and abstract types

Julia does not provide a traditional class/inheritance approach to programming. Instead, the basic unit of its object oriented approach is the type definition (struct and mutable struct) and inheritance exists only on the function side of the language rather than data side. Julia provides a rich system of abstract types and unions on the data side and multimethods on the function side to effect this.

For example Julia's Number type is an abstract type containing all concrete types that behave like numbers, e.g. Int64, Float64, and so on.

Abstract types can also belong to other abstract types, forming a tree of abstract types.

In Nemo the most important abstract types are Ring and Field, with the latter belonging to the former so that all fields are rings, and the abstract types RingElem and FieldElem for the objects that represent elements of rings and fields, again with the latter abstract type belonging to the former.

Because this hierarchy of abstract types must form a tree, Julia is strictly speaking single inheritance, as each concrete and abstract type can belong to at most one other abstract type. For example, one could not have a diamond of abstract types with ExactField belonging to both Field and ExactRing.

Recovering aspects of multiple inheritance in Nemo

Various possibilities exist to get around the limitation that abstract types must form a 'tree' in Nemo and AbstractAlgebra.

One such possibility is union types. If a function should accept one of a number of concrete or abstract types that can't all be made to belong to a single abstract type due to this limitation then one can use a union type.

For example, Nemo defines RingElement to be a union of RingElem and all the Julia standard types which behave like ring elements, e.g. all Integer types and types of rationals with Integer components.

Other union types are defined in src/AbstractAlgebra.jl in AbstractAlgebra.

A second feature we make use of in Nemo is parameterised types. Each concrete and abstract type can take one or more parameters. These parameter can be any other type, either concrete or abstract. For example, in Julia Rational{T} is for rationals with numerator and denominator of type T.

A great deal of control over parameterised types is possible, e.g. one can restrict the type parameter T using a where clause, e.g. to write a function that accepts all rational types with integer components of the same type one can use the type Rational{T} where T <: Integer.

Nemo makes use of such parameterised types for generic ring constructions such as generic polynomial rings and matrices over a given base ring. The type of the elements of the base ring is substituted for the parameter T in any concrete instantiation of the types Poly{T} and Mat{T}, which are defined in AbstractAlgebra in src/generic/GenericTypes.jl.

The totality of all univariate polynomial types, including those of generic Poly{T} types and those coming from C libraries (such as ZZPolyRingElem), is represented by the abstract type PolyRingElem{T} which in turn belongs to RingElem, both defined in AbstractAlgebra in src/AbstractTypes.jl.

Similarly, the totality of all matrix types, including explicit C types like ZZMatrix and the generic Mat{T} types is given by the abstract type MatElem{T}, again defined in AbstractAlgebra in src/AbstractTypes.jl.

This hierarchy of types allows one to write functions at any level, e.g. for all univariate polynomial types, just those with a given base type T, or for a specific concrete type corresponding to just one kind of univariate polynomial.

A third possibility to get around the single inheritance limitation of Julia is type traits. There is currently no explicit compiler/language support for traits, however various implementations exist that make use of type parameters in tricky ways. This allows one to add 'traits' to types, so long as those traits can be expressed as types. In this way, types can have multiple 'properties' at the same time, instead of belonging to just a single abstract type.

Nemo does not currently use type traits, though the map types in Nemo do make use of a custom analogue of this.

Note that unlike class based systems that dispatch on the type of a (sometimes implicit) this or self parameter, Julia methods dispatch on the type of all arguments. This is a natural fit for mathematics where all sorts of ad hoc left and right operations may be required.

Encapsulation, maps and runtime flags

One limitation of the Julia approach is that the type of an object cannot be changed at runtime. For example one might like to insist that a given ring is in fact a field. There are three standard ways to handle this in Julia.

The first approach is to encapsulate the object in another object which does have the desired type. The second approach is to map the object to a different one of the required type (e.g. by applying a morphism). The third approach is to introduce data fields in the original type which can be changed at runtime, unlike its type. All three approaches come with downsides.

Encapsulation can be time consuming for the developer as methods which applied to the original object do not automatically apply to the encapsulated object. One can write methods which do, but this is not automatic.

Application of a map may come with a performance penalty and may be difficult for the user to navigate. Moreover, mutation of the resulting object does not result in mutation of the original object.

The third option of adding runtime data fields essentially takes one back to writing a (possibly bug ridden) interpreter. It relies on the developer implementing outer methods that make use of hand written control statements to determine which of a range of inner methods should be applied to the object. This misses the benefits of one of the main defining features of Julia, namely its multimethod system and can also make introspection more difficult.

Nemo does not apply any of these three approaches widely at present, though information which can only be known at runtime such as whether a ring is Euclidean will eventually have to be encoded using one of these three methods.

Nemo's custom map types

It makes sense that map types in Nemo should be parameterised by the element types of both the domain and codomain of the map, and of course all maps in the system should somehow belong to an abstract type Map.

This leads one to consider a two parameter system of types Map{D, C} where D and C are the domain and codomain types respectively.

One may also wish to implement various types of map, e.g. linear maps (where the map contains a matrix representing the map) or functional maps (where the map is implemented by a Julia function) and so on. Notionally one imagines doing this with a hierarchy of two parameter abstract types all ultimately belonging to Map{D, C} as the root of the tree.

This approach begins to break down when constructions from homological algebra begin to be applied to maps. In such cases, the maps themselves are the object of study and functions may be applied to maps to produce other maps.

The simplest such function is composition. In a system where composition of maps always results in a map of the same type, no problem arises with the straightforward approach outlined above.

However, for various reasons (including performance) it may not be desirable or even possible to construct a composition of two given maps using the same representation as the original maps. This means that the result of composing two maps of the same type may be a map of a different type, e.g. in the worst case a general composition type.

This problem makes many homological and category theoretic operations on maps difficult or impossible to implement.

Other operations which may be desirable to implement are caching of maps (e.g. where the map is extremely time consuming to compute, such as discrete logarithms) and attaching category theoretic information to maps. Such operations can be effected by encapsulating existing maps in objects containing the extra information, e.g. a cache or a category. However all the methods that applied to the original map objects now no longer apply to the encapsulated objects.

To work around these limitations Nemo implements a four parameter Map type, Map{D, C, T, U}.

The first two parameters are the domain and codomain types as discussed above.

The parameter T is a "map class" which is itself an abstract type existing in a hierarchy of abstract types. This parameter is best thought of as a trait, independent of the hierarchy of abstract types belonging to Map, giving additional flexibility to the map types in the system.

For example, T may be set to LinearMap or FunctionalMap. This may be useful if one wishes to distinguish maps in other ways, e.g. whether they are homomorphisms, isomorphisms, maps with section or retraction etc. As usual, offering traits partially gets around the single inheritance problem.

The final parameter U is used to allow maps of a given type U to be composed and still result in a map of type U, even though the concrete type of the composition is different to that of the original maps. Methods can be written for all maps of type U by matching this parameter, rather than matching on the concrete type U of the original maps.

For example, two maps with concrete type MyRingHomomorphism would belong to Map{D, C, T, MyRingHomomorphism} as would any composition of such maps, even if the concrete type of the composition was not a MyRingHomomorphism.

Naturally four parameter types are rather unwieldy and so various helper functions are provided to compute four parameter map types. In the first instance one still has the type Map{D, C} which will give the union of all map types whose first two parameters are D and C, and where the remaining two parameters are arbitrary.

However one can also pass a map class or a concrete type U to a Map function to compute the class of all maps of the given map class or type.

For example, to write a function which accepts all maps of "type" MyRingHomomorphism, including all compositions of such maps, one inserts Map(MyRingHomomorphism) in place of the type, e.g.

function myfun(f::Map(MyRingHomomorphism))

Note the parentheses here, rather than curly braces; it's a function to compute a type! Now the function myfun will accept any map type whose fourth parameter U is set to MyRingHomomorphism.

This four parameter system is flexible, but may need to be expanded in the future. For example it may be useful to have more than one trait T. This could be achieved either by making T a tuple of traits or by introducing a parameterised MapTrait type which can be placed at that location. Naturally the Map functions for computing the four parameter types will have to be similarly expanded to make it easier for the user.

The map type system is currently considered experimental and our observation so far is that it is not intuitive for developers.

Type hierarchy diagram

The most important abstract types in the system are the element types. Their hierarchy is shown in the following diagram.

alt text

Most of the element types have a corresponding parent abstract type. These are shown in the following diagram.

alt text

diff --git a/dev/exact/index.html b/dev/exact/index.html index 10db0e771..94725d0ce 100644 --- a/dev/exact/index.html +++ b/dev/exact/index.html @@ -173,4 +173,4 @@ julia> sin(3*a) == 4 * sin(a) * sin(C(pi)//3 - a) * sin(C(pi)//3 + a) ERROR: Unable to perform operation (failed deciding truth of a predicate): isequal [...]

A possible workaround is to fall back on a numerical comparison:

julia> abs(cos(a) + cos(2*a) + cos(3*a) - (sin(7*a//2)//(2*sin(a//2)) - C(1)//2)) <= C(10)^-100
-true

Of course, this is not a rigorous proof that the numbers are equal, and CalciumField is overkill here; it would be far more efficient to use ArbField directly to check that the numbers are approximately equal.

Interface

Nemo.const_piMethod
const_pi(C::CalciumField)

Return the constant $\pi$ as an element of C.

source
Nemo.const_eulerMethod
const_euler(C::CalciumField)

Return Euler's constant $\gamma$ as an element of C.

source
Nemo.oneiMethod
onei(C::CalciumField)

Return the imaginary unit $i$ as an element of C.

source
Base.sqrtMethod
Base.sqrt(a::CalciumFieldElem; check::Bool=true)

Return the principal square root of a.

source
Base.expMethod
exp(a::CalciumFieldElem)

Return the exponential function of a.

source
Base.logMethod
log(a::CalciumFieldElem)

Return the natural logarithm of a.

source
Nemo.powMethod
pow(a::CalciumFieldElem, b::Int; form::Symbol=:default)

Return a raised to the integer power b. The optional form argument allows specifying the representation. In :default form, this is equivalent to a ^ b, which may create a new extension number $a^b$ if the exponent b is too large (as determined by the parent option :pow_limit or :prec_limit depending on the case). In :arithmetic form, the exponentiation is performed arithmetically in the field of a, regardless of the size of the exponent b.

source
Base.sinMethod
sin(a::CalciumFieldElem; form::Symbol=:default)

Return the sine of a. The optional form argument allows specifying the representation. In :default form, the result is determined by the :trig_form option of the parent object. In :exponential form, the value is represented using complex exponentials. In :tangent form, the value is represented using tangents. In :direct form, the value is represented directly using a sine or cosine.

source
Base.cosMethod
cos(a::CalciumFieldElem; form::Symbol=:default)

Return the cosine of a. The optional form argument allows specifying the representation. In :default form, the result is determined by the :trig_form option of the parent object. In :exponential form, the value is represented using complex exponentials. In :tangent form, the value is represented using tangents. In :direct form, the value is represented directly using a sine or cosine.

source
Base.tanMethod
tan(a::CalciumFieldElem; form::Symbol=:default)

Return the tangent of a. The optional form argument allows specifying the representation. In :default form, the result is determined by the :trig_form option of the parent object. In :exponential form, the value is represented using complex exponentials. In :direct or :tangent form, the value is represented directly using tangents. In :sine_cosine form, the value is represented using sines or cosines.

source
Base.atanMethod
atan(a::CalciumFieldElem; form::Symbol=:default)

Return the inverse tangent of a. The optional form argument allows specifying the representation. In :default form, the result is determined by the :trig_form option of the parent object. In :logarithm form, the value is represented using complex logarithms. In :direct or :arctangent form, the value is represented directly using arctangents.

source
Base.asinMethod
asin(a::CalciumFieldElem; form::Symbol=:default)

Return the inverse sine of a. The optional form argument allows specifying the representation. In :default form, the result is determined by the :trig_form option of the parent object. In :logarithm form, the value is represented using complex logarithms. In :direct form, the value is represented directly using an inverse sine or cosine.

source
Base.acosMethod
acos(a::CalciumFieldElem; form::Symbol=:default)

Return the inverse cosine of a. The optional form argument allows specifying the representation. In :default form, the result is determined by the :trig_form option of the parent object. In :logarithm form, the value is represented using complex logarithms. In :direct form, the value is represented directly using an inverse sine or cosine.

source
Nemo.gammaMethod
gamma(a::CalciumFieldElem)

Return the gamma function of a.

source
Nemo.erfMethod
erf(a::CalciumFieldElem)

Return the error function of a.

source
Nemo.erfiMethod
erfi(a::CalciumFieldElem)

Return the imaginary error function of a.

source
Nemo.erfcMethod
erfc(a::CalciumFieldElem)

Return the complementary error function of a.

source

Rewriting and simplification

Nemo.complex_normal_formMethod
complex_normal_form(a::CalciumFieldElem, deep::Bool=true)

Returns the input rewritten using standardizing transformations over the complex numbers:

  • Elementary functions are rewritten in terms of exponentials, roots and logarithms.

  • Complex parts are rewritten using logarithms, square roots, and (deep) complex conjugates.

  • Algebraic numbers are rewritten in terms of cyclotomic fields where applicable.

If deep is set, the rewriting is applied recursively to the tower of extension numbers; otherwise, the rewriting is only applied to the top-level extension numbers.

The result is not a normal form in the strong sense (the same number can have many possible representations even after applying this transformation), but this transformation can nevertheless be a useful heuristic for simplification.

source
+true

Of course, this is not a rigorous proof that the numbers are equal, and CalciumField is overkill here; it would be far more efficient to use ArbField directly to check that the numbers are approximately equal.

Interface

Nemo.const_piMethod
const_pi(C::CalciumField)

Return the constant $\pi$ as an element of C.

source
Nemo.const_eulerMethod
const_euler(C::CalciumField)

Return Euler's constant $\gamma$ as an element of C.

source
Nemo.oneiMethod
onei(C::CalciumField)

Return the imaginary unit $i$ as an element of C.

source
Base.sqrtMethod
Base.sqrt(a::CalciumFieldElem; check::Bool=true)

Return the principal square root of a.

source
Base.expMethod
exp(a::CalciumFieldElem)

Return the exponential function of a.

source
Base.logMethod
log(a::CalciumFieldElem)

Return the natural logarithm of a.

source
Nemo.powMethod
pow(a::CalciumFieldElem, b::Int; form::Symbol=:default)

Return a raised to the integer power b. The optional form argument allows specifying the representation. In :default form, this is equivalent to a ^ b, which may create a new extension number $a^b$ if the exponent b is too large (as determined by the parent option :pow_limit or :prec_limit depending on the case). In :arithmetic form, the exponentiation is performed arithmetically in the field of a, regardless of the size of the exponent b.

source
Base.sinMethod
sin(a::CalciumFieldElem; form::Symbol=:default)

Return the sine of a. The optional form argument allows specifying the representation. In :default form, the result is determined by the :trig_form option of the parent object. In :exponential form, the value is represented using complex exponentials. In :tangent form, the value is represented using tangents. In :direct form, the value is represented directly using a sine or cosine.

source
Base.cosMethod
cos(a::CalciumFieldElem; form::Symbol=:default)

Return the cosine of a. The optional form argument allows specifying the representation. In :default form, the result is determined by the :trig_form option of the parent object. In :exponential form, the value is represented using complex exponentials. In :tangent form, the value is represented using tangents. In :direct form, the value is represented directly using a sine or cosine.

source
Base.tanMethod
tan(a::CalciumFieldElem; form::Symbol=:default)

Return the tangent of a. The optional form argument allows specifying the representation. In :default form, the result is determined by the :trig_form option of the parent object. In :exponential form, the value is represented using complex exponentials. In :direct or :tangent form, the value is represented directly using tangents. In :sine_cosine form, the value is represented using sines or cosines.

source
Base.atanMethod
atan(a::CalciumFieldElem; form::Symbol=:default)

Return the inverse tangent of a. The optional form argument allows specifying the representation. In :default form, the result is determined by the :trig_form option of the parent object. In :logarithm form, the value is represented using complex logarithms. In :direct or :arctangent form, the value is represented directly using arctangents.

source
Base.asinMethod
asin(a::CalciumFieldElem; form::Symbol=:default)

Return the inverse sine of a. The optional form argument allows specifying the representation. In :default form, the result is determined by the :trig_form option of the parent object. In :logarithm form, the value is represented using complex logarithms. In :direct form, the value is represented directly using an inverse sine or cosine.

source
Base.acosMethod
acos(a::CalciumFieldElem; form::Symbol=:default)

Return the inverse cosine of a. The optional form argument allows specifying the representation. In :default form, the result is determined by the :trig_form option of the parent object. In :logarithm form, the value is represented using complex logarithms. In :direct form, the value is represented directly using an inverse sine or cosine.

source
Nemo.gammaMethod
gamma(a::CalciumFieldElem)

Return the gamma function of a.

source
Nemo.erfMethod
erf(a::CalciumFieldElem)

Return the error function of a.

source
Nemo.erfiMethod
erfi(a::CalciumFieldElem)

Return the imaginary error function of a.

source
Nemo.erfcMethod
erfc(a::CalciumFieldElem)

Return the complementary error function of a.

source

Rewriting and simplification

Nemo.complex_normal_formMethod
complex_normal_form(a::CalciumFieldElem, deep::Bool=true)

Returns the input rewritten using standardizing transformations over the complex numbers:

  • Elementary functions are rewritten in terms of exponentials, roots and logarithms.

  • Complex parts are rewritten using logarithms, square roots, and (deep) complex conjugates.

  • Algebraic numbers are rewritten in terms of cyclotomic fields where applicable.

If deep is set, the rewriting is applied recursively to the tower of extension numbers; otherwise, the rewriting is only applied to the top-level extension numbers.

The result is not a normal form in the strong sense (the same number can have many possible representations even after applying this transformation), but this transformation can nevertheless be a useful heuristic for simplification.

source
diff --git a/dev/factor/index.html b/dev/factor/index.html index cb6e613f8..d4749a947 100644 --- a/dev/factor/index.html +++ b/dev/factor/index.html @@ -18,4 +18,4 @@ true julia> fac[229] -3

Basic functionality

Objects of type Fac are iterable, that is, if a is an object of type Fac, then for (p, e) in a will iterate through all pairs (p, e), where p is a factor and e the corresponding exponent.

Base.inMethod
in(a, b::Fac)

Test whether $a$ is a factor of $b$.

source
Base.getindexMethod
getindex(a::Fac, b) -> Int

If $b$ is a factor of $a$, the corresponding exponent is returned. Otherwise an error is thrown.

source
Base.lengthMethod
length(a::Fac) -> Int

Return the number of factors of $a$, not including the unit.

source
AbstractAlgebra.unitMethod
unit(a::Fac{T}) -> T

Return the unit of the factorization.

source
+3

Basic functionality

Objects of type Fac are iterable, that is, if a is an object of type Fac, then for (p, e) in a will iterate through all pairs (p, e), where p is a factor and e the corresponding exponent.

Base.inMethod
in(a, b::Fac)

Test whether $a$ is a factor of $b$.

source
Base.getindexMethod
getindex(a::Fac, b) -> Int

If $b$ is a factor of $a$, the corresponding exponent is returned. Otherwise an error is thrown.

source
Base.lengthMethod
length(a::Fac) -> Int

Return the number of factors of $a$, not including the unit.

source
AbstractAlgebra.unitMethod
unit(a::Fac{T}) -> T

Return the unit of the factorization.

source
diff --git a/dev/ff_embedding/index.html b/dev/ff_embedding/index.html index e243f9efe..3d420a4d2 100644 --- a/dev/ff_embedding/index.html +++ b/dev/ff_embedding/index.html @@ -31,4 +31,4 @@ x7 julia> t = k7(y) -x7 +x7 diff --git a/dev/finitefield/index.html b/dev/finitefield/index.html index 1c6204fd3..9c4b8a59a 100644 --- a/dev/finitefield/index.html +++ b/dev/finitefield/index.html @@ -44,4 +44,4 @@ y^3 + y^2 + y + 2source

Element functionality

AbstractAlgebra.genMethod
gen(L::FqField)

Return a $K$-algebra generator a of the finite field $L$, where $K$ is the base field of $L$. The element a satisfies defining_polyomial(a) == 0.

Note that this is in general not a multiplicative generator and can be zero, if $L/K$ is an extension of degree one.

source
AbstractAlgebra.is_genMethod
is_gen(a::FqFieldElem)

Return true if the given finite field element is the generator of the finite field over its base field, otherwise return false.

source
LinearAlgebra.trMethod
tr(x::FqFieldElem)

Return the trace of $x$. This is an element of the base field.

source
Nemo.absolute_trMethod
absolute_tr(x::FqFieldElem)

Return the absolute trace of $x$. This is an element of the prime field.

source
LinearAlgebra.normMethod
norm(x::FqFieldElem)

Return the norm of $x$. This is an element of the base field.

source
Nemo.absolute_normMethod
absolute_norm(x::FqFieldElem)

Return the absolute norm of $x$. This is an element of the prime field.

source
AbstractAlgebra.liftMethod
lift(R::FqPolyRing, a::FqFieldElem) -> FqPolyRingElem

Given a polynomial ring over the base field of the parent of a, return a lift such that parent(a)(lift(R, a)) == a is true.

source
AbstractAlgebra.liftMethod
lift(::ZZRing, x::FqFieldElem) -> ZZRingElem

Given an element $x$ of a prime field $\mathbf{F}_p$, return a preimage under the canonical map $\mathbf{Z} \to \mathbf{F}_p$.

Examples

julia> K = GF(19);
 
 julia> lift(ZZ, K(3))
-3
source
+3source diff --git a/dev/fraction/index.html b/dev/fraction/index.html index 52c9bf93e..97ba4d799 100644 --- a/dev/fraction/index.html +++ b/dev/fraction/index.html @@ -33,4 +33,4 @@ julia> c = dedekind_sum(-120, ZZ(1305)) -575//522source
Nemo.simplest_betweenMethod
  simplest_between(l::QQFieldElem, r::QQFieldElem)

Return the simplest fraction in the closed interval $[l, r]$. A canonical fraction $a_1 / b_1$ is defined to be simpler than $a_2 / b_2$ if and only if $b_1 < b_2$ or $b_1 = b_2$ and $a_1 < a_2$.

Examples

julia> simplest_between(QQ(1//10), QQ(3//10))
-1//4
source
+1//4source diff --git a/dev/gfp/index.html b/dev/gfp/index.html index 4ff2ed5bd..6dbd8628f 100644 --- a/dev/gfp/index.html +++ b/dev/gfp/index.html @@ -10,4 +10,4 @@ 3 julia> b = order(F) -3 +3 diff --git a/dev/index.html b/dev/index.html index 9cbef44dd..f30f925d3 100644 --- a/dev/index.html +++ b/dev/index.html @@ -65,4 +65,4 @@ julia> @time divexact((u*exp(x*u)), (exp(u)-1)); 0.412813 seconds (667.49 k allocations: 33.966 MiB, 90.26% compilation time)

Building dependencies from source

Nemo depends on the FLINT C library which is installed using binaries by default. With Julia version >= 1.3, the use of this binary can be overridden by putting the following into the file ~/.julia/artifacts/Overrides.toml:

[e134572f-a0d5-539d-bddf-3cad8db41a82]
-FLINT = "/prefix/for/libflint"

Experimental threading support for flint

Enabling a threaded version of flint can be done by setting the environment variable NEMO_THREADED=1. To set the actual number of threads, use Nemo.flint_set_num_threads($numberofthreads).

+FLINT = "/prefix/for/libflint"

Experimental threading support for flint

Enabling a threaded version of flint can be done by setting the environment variable NEMO_THREADED=1. To set the actual number of threads, use Nemo.flint_set_num_threads($numberofthreads).

diff --git a/dev/integer/index.html b/dev/integer/index.html index 2355a151e..14572b21a 100644 --- a/dev/integer/index.html +++ b/dev/integer/index.html @@ -190,4 +190,4 @@ 4//5 + 3//5*im julia> abs2(a//b) -1 +1 diff --git a/dev/matrix/index.html b/dev/matrix/index.html index 2302475f6..5f1fa691a 100644 --- a/dev/matrix/index.html +++ b/dev/matrix/index.html @@ -236,4 +236,4 @@ julia> eigenvalues_with_multiplicities(A) 1-element Vector{Tuple{ComplexFieldElem, Int64}}: - (2.0000000000000000000, 3) + (2.0000000000000000000, 3) diff --git a/dev/misc/index.html b/dev/misc/index.html index f5336e7be..271d261e1 100644 --- a/dev/misc/index.html +++ b/dev/misc/index.html @@ -13,4 +13,4 @@ end f(n) = x^n -end

Alternatively, one can disable precompilation by adding __precompile__(false) inside A. Note that this might have other unwanted side effects.

+end

Alternatively, one can disable precompilation by adding __precompile__(false) inside A. Note that this might have other unwanted side effects.

diff --git a/dev/mpolynomial/index.html b/dev/mpolynomial/index.html index 36ea88d7a..31e254022 100644 --- a/dev/mpolynomial/index.html +++ b/dev/mpolynomial/index.html @@ -1,2 +1,2 @@ -Multivariate polynomials · Nemo.jl

Multivariate polynomials

Introduction

Nemo allow the creation of sparse, distributed multivariate polynomials over any computable ring $R$. There are two different kinds of implementation: a generic one for the case where no specific implementation exists (provided by AbstractAlgebra.jl), and efficient implementations of polynomials over numerous specific rings, usually provided by C/C++ libraries.

The following table shows each of the polynomial types available in Nemo, the base ring $R$, and the Julia/Nemo types for that kind of polynomial (the type information is mainly of concern to developers).

Base ringLibraryElement typeParent type
Generic ring $R$AbstractAlgebra.jlGeneric.MPoly{T}Generic.MPolyRing{T}
$\mathbb{Z}$FlintZZMPolyRingElemZZMPolyRing
$\mathbb{Z}/n\mathbb{Z}$ (small $n$)FlintzzModMPolyRingElemzzModMPolyRing
$\mathbb{Q}$FlintQQMPolyRingElemQQMPolyRing
$\mathbb{Z}/p\mathbb{Z}$ (small prime $p$)FlintfpMPolyRingElemfpMPolyRing
$\mathbb{F}_{p^n}$ (small $p$)FlintfqPolyRepMPolyRingElemfqPolyRepMPolyRing

The string representation of the variables and the base ring $R$ of a generic polynomial is stored in its parent object.

All polynomial element types belong to the abstract type MPolyRingElem and all of the polynomial ring types belong to the abstract type MPolyRing. This enables one to write generic functions that can accept any Nemo multivariate polynomial type.

Polynomial functionality

All multivariate polynomial types in Nemo provide the multivariate polynomial functionality described by AbstractAlgebra:

https://nemocas.github.io/AbstractAlgebra.jl/stable/mpolynomial

Generic multivariate polynomials are also available.

We describe here only functions that are in addition to that guaranteed by AbstractAlgebra.jl, for specific coefficient rings.

+Multivariate polynomials · Nemo.jl

Multivariate polynomials

Introduction

Nemo allow the creation of sparse, distributed multivariate polynomials over any computable ring $R$. There are two different kinds of implementation: a generic one for the case where no specific implementation exists (provided by AbstractAlgebra.jl), and efficient implementations of polynomials over numerous specific rings, usually provided by C/C++ libraries.

The following table shows each of the polynomial types available in Nemo, the base ring $R$, and the Julia/Nemo types for that kind of polynomial (the type information is mainly of concern to developers).

Base ringLibraryElement typeParent type
Generic ring $R$AbstractAlgebra.jlGeneric.MPoly{T}Generic.MPolyRing{T}
$\mathbb{Z}$FlintZZMPolyRingElemZZMPolyRing
$\mathbb{Z}/n\mathbb{Z}$ (small $n$)FlintzzModMPolyRingElemzzModMPolyRing
$\mathbb{Q}$FlintQQMPolyRingElemQQMPolyRing
$\mathbb{Z}/p\mathbb{Z}$ (small prime $p$)FlintfpMPolyRingElemfpMPolyRing
$\mathbb{F}_{p^n}$ (small $p$)FlintfqPolyRepMPolyRingElemfqPolyRepMPolyRing

The string representation of the variables and the base ring $R$ of a generic polynomial is stored in its parent object.

All polynomial element types belong to the abstract type MPolyRingElem and all of the polynomial ring types belong to the abstract type MPolyRing. This enables one to write generic functions that can accept any Nemo multivariate polynomial type.

Polynomial functionality

All multivariate polynomial types in Nemo provide the multivariate polynomial functionality described by AbstractAlgebra:

https://nemocas.github.io/AbstractAlgebra.jl/stable/mpolynomial

Generic multivariate polynomials are also available.

We describe here only functions that are in addition to that guaranteed by AbstractAlgebra.jl, for specific coefficient rings.

diff --git a/dev/numberfield/index.html b/dev/numberfield/index.html index 286986a7a..5ab40fa54 100644 --- a/dev/numberfield/index.html +++ b/dev/numberfield/index.html @@ -84,4 +84,4 @@ 113 julia> f = tr(c) --15 +-15 diff --git a/dev/padic/index.html b/dev/padic/index.html index 754bb71aa..ecf95d636 100644 --- a/dev/padic/index.html +++ b/dev/padic/index.html @@ -104,4 +104,4 @@ O(7^30) julia> f = teichmuller(b) -2*7^0 + 4*7^1 + 6*7^2 + O(7^3) +2*7^0 + 4*7^1 + 6*7^2 + O(7^3) diff --git a/dev/polynomial/index.html b/dev/polynomial/index.html index 5d6eb114e..3a32c2d4e 100644 --- a/dev/polynomial/index.html +++ b/dev/polynomial/index.html @@ -155,4 +155,4 @@ -29211840*x^29 + 128406630*x^28 + 24647168*x^27 - 73279080*x^26 + 13865712*x^25 - 25499225*x^24 + 21288960*x^23 + 18643272*x^22 - 12830688*x^21 - 4219488*x^20 - 7109760*x^19 + 10661420*x^18 + 2727432*x^17 - 6905934*x^16 + 987136*x^15 + 1217160*x^14 + 401856*x^13 - 577738*x^12 - 370944*x^11 + 534612*x^10 - 115920*x^9 - 113643*x^8 + 84480*x^7 - 16744*x^6 - 6048*x^5 + 4830*x^4 - 1472*x^3 + 252*x^2 - 24*x + 1 julia> o = cyclotomic(10, 1 + x + x^2) -x^8 + 4*x^7 + 9*x^6 + 13*x^5 + 14*x^4 + 11*x^3 + 6*x^2 + 2*x + 1 +x^8 + 4*x^7 + 9*x^6 + 13*x^5 + 14*x^4 + 11*x^3 + 6*x^2 + 2*x + 1 diff --git a/dev/puiseux/index.html b/dev/puiseux/index.html index 71cf1003e..7b38aac8d 100644 --- a/dev/puiseux/index.html +++ b/dev/puiseux/index.html @@ -9,4 +9,4 @@ 1 + z + 3*z^2 + O(z^5) julia> k = eta_qexp(z) -z^(1//24) - z^(25//24) + O(z^(31//24)) +z^(1//24) - z^(25//24) + O(z^(31//24)) diff --git a/dev/qadic/index.html b/dev/qadic/index.html index 757ec2de9..0e8e55c64 100644 --- a/dev/qadic/index.html +++ b/dev/qadic/index.html @@ -85,4 +85,4 @@ 2*7^0 + 4*7^1 + 6*7^2 + O(7^3) julia> g = frobenius(a, 2) -7^0 + 7^1 + 2*7^2 + O(7^3) +7^0 + 7^1 + 2*7^2 + O(7^3) diff --git a/dev/rational/index.html b/dev/rational/index.html index f26e47662..cce87b1e7 100644 --- a/dev/rational/index.html +++ b/dev/rational/index.html @@ -1,2 +1,2 @@ -Rationals · Nemo.jl

Rationals

Nemo provides much functionality for the rational numbers. See the section on Fraction Fields where all the basic functionality is documented, along with the extra functionality only available for the rational numbers themselves.

+Rationals · Nemo.jl

Rationals

Nemo provides much functionality for the rational numbers. See the section on Fraction Fields where all the basic functionality is documented, along with the extra functionality only available for the rational numbers themselves.

diff --git a/dev/real/index.html b/dev/real/index.html index 0eae5103c..1b3abdc5b 100644 --- a/dev/real/index.html +++ b/dev/real/index.html @@ -222,4 +222,4 @@ 8717442233//2774848045source

Random generation

Base.randMethod
rand(r::RealField; randtype::Symbol=:urandom)

Return a random element in given Arb field.

The randtype default is :urandom which return an ArbFieldElem contained in $[0,1]$.

The rest of the methods return non-uniformly distributed values in order to exercise corner cases. The option :randtest will return a finite number, and :randtest_exact the same but with a zero radius. The option :randtest_precise return an ArbFieldElem with a radius around $2^{-\mathrm{prec}}$ the magnitude of the midpoint, while :randtest_wide return a radius that might be big relative to its midpoint. The :randtest_special-option might return a midpoint and radius whose values are NaN or inf.

source
rand([rng=Random.default_rng(),] G::SymmetricGroup)

Return a random permutation from G.

source

Examples

a = rand(RR)
 b = rand(RR; randtype = :null_exact)
 c = rand(RR; randtype = :exact)
-d = rand(RR; randtype = :special)
+d = rand(RR; randtype = :special) diff --git a/dev/residue/index.html b/dev/residue/index.html index eefffe7a4..cd29015e6 100644 --- a/dev/residue/index.html +++ b/dev/residue/index.html @@ -2,4 +2,4 @@ Residue rings · Nemo.jl

Residue rings

Nemo allows the creation of residue rings of the form $R/(a)$ for an element $a$ of a ring $R$.

We don't require $(a)$ to be a prime or maximal ideal. Instead, we allow the creation of the residue ring $R/(a)$ for any nonzero $a$ and simply raise an exception if an impossible inverse is encountered during computations involving elements of $R/(a)$. Of course, a GCD function must be available for the base ring $R$.

There is a generic implementation of residue rings of this form in AbstractAlgebra.jl, which accepts any ring $R$ as base ring.

The associated types of parent object and elements for each kind of residue rings in Nemo are given in the following table.

Base ringLibraryElement typeParent type
Generic ring $R$AbstractAlgebra.jlEuclideanRingResidueRingElem{T}EuclideanRingResidueRing{T}
$\mathbb{Z}$ (Int modulus)FlintzzModRingElemzzModRing
$\mathbb{Z}$ (ZZ modulus)FlintZZModRingElemZZModRing

The modulus $a$ of a residue ring is stored in its parent object.

All residue element types belong to the abstract type ResElem and all the residue ring parent object types belong to the abstract type ResidueRing. This enables one to write generic functions that accept any Nemo residue type.

Residue functionality

All the residue rings in Nemo provide the functionality described in AbstractAlgebra for residue rings:

https://nemocas.github.io/AbstractAlgebra.jl/stable/residue

In addition, generic residue rings are available.

We describe Nemo specific residue ring functionality below.

GCD

Base.gcdxMethod
gcdx(a::zzModRingElem, b::zzModRingElem)

Compute the extended gcd with the Euclidean structure inherited from $\mathbb{Z}$.

source
Base.gcdxMethod
gcdx(a::ZZModRingElem, b::ZZModRingElem)

Compute the extended gcd with the Euclidean structure inherited from $\mathbb{Z}$.

source

Examples

julia> R, = residue_ring(ZZ, 123456789012345678949);
 
 julia> g, s, t = gcdx(R(123), R(456))
-(1, 123456789012345678928, 41152263004115226322)
+(1, 123456789012345678928, 41152263004115226322) diff --git a/dev/series/index.html b/dev/series/index.html index 461630058..4589a7463 100644 --- a/dev/series/index.html +++ b/dev/series/index.html @@ -24,4 +24,4 @@ z + 2*z^2 + 29//6*z^3 - z^4 + O(z^5) julia> m = atanh(b) -z + 2*z^2 + 16//3*z^3 + 2*z^4 + O(z^5) +z + 2*z^2 + 16//3*z^3 + 2*z^4 + O(z^5) diff --git a/dev/types/index.html b/dev/types/index.html index 4fcad94b0..a33b9c861 100644 --- a/dev/types/index.html +++ b/dev/types/index.html @@ -1,2 +1,2 @@ -Types in Nemo · Nemo.jl

Types in Nemo

Nemo is fully compatible with AbstractAlgebra.jl, but specialises implementations of various commonly used rings with a highly optimised C implementation, provided by the C libraries wrapped by Nemo.

Below, we give a list of all of the specialised types available in Nemo that implement rings using a specialised C library. The types of elements of the respective rings and other mathematical structures are given, and in parentheses we list the types of the parent objects of the given rings and structures.

  • Flint

    • ZZRingElem (ZZRing)
    • QQFieldElem (QQField)
    • zzModRingElem (zzModRing)
    • ZZModRingElem (ZZModRing`)
    • fqPolyRepFieldElem (fqPolyRepField)
    • fpFieldElem (fpField)
    • FpFieldElem (FpField)
    • FqPolyRepFieldElem (FqPolyRepField)
    • PadicFieldElem (PadicField)
    • QadicFieldElem (QadicField)
    • ZZPolyRingElem (ZZPolyRing)
    • QQPolyRingElem (QQPolyRing)
    • zzModPolyRingElem (zzModPolyRing)
    • ZZModPolyRingElem (ZZModPolyRing)
    • FqPolyRepPolyRingElem (FqPolyRepPolyRing)
    • fqPolyRepPolyRingElem (fqPolyRepPolyRing)
    • ZZMPolyRingElem (ZZMPolyRing)
    • QQMPolyRingElem (QQMPolyRing)
    • zzModMPolyRingElem (zzModMPolyRing)
    • fqPolyRepMPolyRingElem (fqPolyRepMPolyRing`)
    • fpPolyRingElem (fpPolyRing)
    • FpPolyRingElem (FpPolyRing)
    • ZZRelPowerSeriesRingElem (ZZRelPowerSeriesRing)
    • ZZAbsPowerSeriesRingElem (ZZAbsPowerSeriesRing)
    • QQRelPowerSeriesRingElem (QQRelPowerSeriesRing)
    • QQAbsPowerSeriesRingElem (QQAbsPowerSeriesRing)
    • ZZModRelPowerSeriesRingElem (ZZModRelPowerSeriesRing)
    • ZZModAbsPowerSeriesRingElem (ZZModAbsPowerSeriesRing)
    • zzModRelPowerSeriesRingElem (zzModRelPowerSeriesRing)
    • zzModAbsPowerSeriesRingElem (zzModAbsPowerSeriesRing)
    • fpRelPowerSeriesRingElem (fpRelPowerSeriesRing)
    • fpAbsPowerSeriesRingElem (fpAbsPowerSeriesRing)
    • FpRelPowerSeriesRingElem (FpRelPowerSeriesRing)
    • FpAbsPowerSeriesRingElem (FpAbsPowerSeriesRing)
    • fqPolyRepRelPowerSeriesRingElem (fqPolyRepRelPowerSeriesRing)
    • fqPolyRepAbsPowerSeriesRingElem (fqPolyRepAbsPowerSeriesRing)
    • FqPolyRepRelPowerSeriesRingElem (FqPolyRepRelPowerSeriesRing)
    • FqPolyRepAbsPowerSeriesRingElem (FqPolyRepAbsPowerSeriesRing)
    • ZZMatrix (ZZMatrixSpace)
    • QQMatrix (QQMatrixSpace)
    • zzModMatrix (zzModMatrixSpace)
    • ZZModMatrix (ZZModMatrixSpace`)
    • fqPolyRepMatrix (fqPolyRepMatrixSpace)
    • FqPolyRepMatrix (FqPolyRepMatrixSpace)
    • fpMatrix (fpMatrixSpace)
    • perm (SymmetricGroup)
  • Antic

    • AbsSimpleNumFieldElem (AbsSimpleNumField)
  • Arb

    • ArbFieldElem (ArbField)
    • AcbFieldElem (AcbField)
    • ArbPolyRingElem (ArbPolyRing)
    • AcbPolyRingElem (AcbPolyRing)
    • ArbMatrix (ArbMatrixSpace)
    • AcbMatrix (AcbMatrixSpace)
  • Calcium

    • QQBarFieldElem (QQBarField)
    • CalciumFieldElem (CalciumField)
+Types in Nemo · Nemo.jl

Types in Nemo

Nemo is fully compatible with AbstractAlgebra.jl, but specialises implementations of various commonly used rings with a highly optimised C implementation, provided by the C libraries wrapped by Nemo.

Below, we give a list of all of the specialised types available in Nemo that implement rings using a specialised C library. The types of elements of the respective rings and other mathematical structures are given, and in parentheses we list the types of the parent objects of the given rings and structures.

  • Flint

    • ZZRingElem (ZZRing)
    • QQFieldElem (QQField)
    • zzModRingElem (zzModRing)
    • ZZModRingElem (ZZModRing`)
    • fqPolyRepFieldElem (fqPolyRepField)
    • fpFieldElem (fpField)
    • FpFieldElem (FpField)
    • FqPolyRepFieldElem (FqPolyRepField)
    • PadicFieldElem (PadicField)
    • QadicFieldElem (QadicField)
    • ZZPolyRingElem (ZZPolyRing)
    • QQPolyRingElem (QQPolyRing)
    • zzModPolyRingElem (zzModPolyRing)
    • ZZModPolyRingElem (ZZModPolyRing)
    • FqPolyRepPolyRingElem (FqPolyRepPolyRing)
    • fqPolyRepPolyRingElem (fqPolyRepPolyRing)
    • ZZMPolyRingElem (ZZMPolyRing)
    • QQMPolyRingElem (QQMPolyRing)
    • zzModMPolyRingElem (zzModMPolyRing)
    • fqPolyRepMPolyRingElem (fqPolyRepMPolyRing`)
    • fpPolyRingElem (fpPolyRing)
    • FpPolyRingElem (FpPolyRing)
    • ZZRelPowerSeriesRingElem (ZZRelPowerSeriesRing)
    • ZZAbsPowerSeriesRingElem (ZZAbsPowerSeriesRing)
    • QQRelPowerSeriesRingElem (QQRelPowerSeriesRing)
    • QQAbsPowerSeriesRingElem (QQAbsPowerSeriesRing)
    • ZZModRelPowerSeriesRingElem (ZZModRelPowerSeriesRing)
    • ZZModAbsPowerSeriesRingElem (ZZModAbsPowerSeriesRing)
    • zzModRelPowerSeriesRingElem (zzModRelPowerSeriesRing)
    • zzModAbsPowerSeriesRingElem (zzModAbsPowerSeriesRing)
    • fpRelPowerSeriesRingElem (fpRelPowerSeriesRing)
    • fpAbsPowerSeriesRingElem (fpAbsPowerSeriesRing)
    • FpRelPowerSeriesRingElem (FpRelPowerSeriesRing)
    • FpAbsPowerSeriesRingElem (FpAbsPowerSeriesRing)
    • fqPolyRepRelPowerSeriesRingElem (fqPolyRepRelPowerSeriesRing)
    • fqPolyRepAbsPowerSeriesRingElem (fqPolyRepAbsPowerSeriesRing)
    • FqPolyRepRelPowerSeriesRingElem (FqPolyRepRelPowerSeriesRing)
    • FqPolyRepAbsPowerSeriesRingElem (FqPolyRepAbsPowerSeriesRing)
    • ZZMatrix (ZZMatrixSpace)
    • QQMatrix (QQMatrixSpace)
    • zzModMatrix (zzModMatrixSpace)
    • ZZModMatrix (ZZModMatrixSpace`)
    • fqPolyRepMatrix (fqPolyRepMatrixSpace)
    • FqPolyRepMatrix (FqPolyRepMatrixSpace)
    • fpMatrix (fpMatrixSpace)
    • perm (SymmetricGroup)
  • Antic

    • AbsSimpleNumFieldElem (AbsSimpleNumField)
  • Arb

    • ArbFieldElem (ArbField)
    • AcbFieldElem (AcbField)
    • ArbPolyRingElem (ArbPolyRing)
    • AcbPolyRingElem (AcbPolyRing)
    • ArbMatrix (ArbMatrixSpace)
    • AcbMatrix (AcbMatrixSpace)
  • Calcium

    • QQBarFieldElem (QQBarField)
    • CalciumFieldElem (CalciumField)