Nemo.jl

repository·master·Indexed 42 days ago

https://github.com/nemocas/nemo.jl

A high-performance computer algebra system for Julia focusing on commutative algebra, number theory, and group theory. Nemo bridges AbstractAlgebra.jl abstractions with FLINT-based implementations, providing tools for polynomial rings, matrix spaces, fraction fields, and specialized arithmetic such as fixed-precision real balls (ArbField), complex balls (AcbField), and the algebraic closure of the rational numbers (QQBarField).

Tokens
37.2K
Snippets
138
Records
178
Agent score
53%

What's inside Nemo

  1. Overview of Nemo

    master

    Nemo is a high-performance Julia library designed for fast basic arithmetic in various commonly used rings. It is primarily used for tasks in:

    • Commutative Algebra
    • Number Theory
    • Group Theory

    Nemo achieves its performance by providing wrappers for the specialized C library FLINT and leverages AbstractAlgebra.jl to provide generic mathematical constructions over the basic rings provided by the underlying packages.

  2. Overview of Nemo.jl

    master

    Nemo is a computer algebra package for the Julia programming language. It is designed to provide computational tools for commutative algebra, number theory, and group theory.

    Nemo achieves this by providing:

    1. Re-exports of AbstractAlgebra.jl: It provides generic implementations for polynomial rings, matrix spaces, fraction fields, residue rings, power series, finite fields, p-adics, integers, rationals, permutations and characters, and number fields.
    2. FLINT Wrappers: It includes wrappers for the FLINT library, which incorporates functionality from the Arb, Antic, and Calcium projects.
  3. Performance considerations for algebraic numbers

    master

    The default QQBarFieldElem uses minimal polynomials for canonical representation. While good for individual numbers, it is not optimized for fast field arithmetic.

    For high-performance calculations in $\overline{\mathbb{Q}}$, use CalciumField. Alternatively, if you are working within a specific subfield $\mathbb{Q}(a)$, construct a dedicated number field instead.

  4. Understand the parent/element model in Nemo

    master

    Nemo uses a parent/element model to manage mathematical objects. In this model, mathematical structures (like sets, groups, rings, fields, or modules) are represented by parent objects rather than just types. The objects belonging to these structures are called elements.

    For example, a polynomial is an element, and the polynomial ring it belongs to is its parent.

    To interact with this model:

    • Use parent(a) to retrieve the parent object of an element a.
    • Use base_ring(S) to obtain the base ring of a ring S.

    This model allows for parameterised structures, such as a quotient ring being parameterised by a specific ideal object.

    parent(a) # Returns the parent of element a
    base_ring(S) # Returns the base ring of ring S
  5. Working with Nemo Integers (ZZ)

    master

    The default integer type in Nemo is provided by FLINT. The integer ring is represented by the constant parent object ZZ.

    Elements of this ring are of type ZZRingElem. For compatibility with standard Julia code, Nemo defines IntegerUnion = Union{Integer, ZZRingElem}, allowing functions to accept both standard Julia integers and Nemo's specialized integer elements.

    Key types:

    • Parent: ZZRing (represented by ZZ)
    • Elements: ZZRingElem
    using Nemo
    a = ZZ(12)
    # a is a ZZRingElem
  6. Create residue rings in Nemo

    master

    Nemo allows you to create residue rings of the form $R/(a)$ for any nonzero element $a$ of a ring $R$. The library does not require $(a)$ to be a prime or maximal ideal; instead, it will raise an exception if an impossible inverse is encountered during computation.

    Nemo provides different implementations depending on the base ring:

    • Generic rings $R$: Uses AbstractAlgebra.jl implementations. The parent type is EuclideanRingResidueRing{T} and elements are EuclideanRingResidueRingElem{T}.
    • $\mathbb{Z}$ (Int modulus): Uses FLINT implementations. The parent type is zzModRing and elements are zzModRingElem.
    • $\mathbb{Z}$ (ZZ modulus): Uses FLINT implementations. The parent type is ZZModRing and elements are ZZModRingElem.

    All residue elements belong to the abstract type ResElem and all residue ring parent objects belong to the abstract type ResidueRing, allowing for generic programming.

    # Example: Creating a residue ring with a ZZ modulus
    R, = residue_ring(ZZ, 123456789012345678949)
  7. Understand the Nemo type system and Julia inheritance

    master

    Nemo leverages Julia's type system, which uses multimethods (dispatching on the types of all arguments) rather than class-based dispatch on a self or this parameter.

    Key concepts:

    • Abstract Types: Nemo uses a hierarchy of abstract types to group related mathematical objects. For example, Field is a subtype of Ring, and FieldElem is a subtype of RingElem.
    • Single Inheritance: Because Julia's abstract type hierarchy must form a tree, Nemo uses other techniques to achieve the flexibility of multiple inheritance.
    • Union Types: Used when a function must accept multiple types that do not share a single common abstract parent. For example, RingElement is a union of RingElem and standard Julia types like Integer.
    • Parameterized Types: Many Nemo types are parameterized to allow generic constructions. For example, Poly{T} and Mat{T} represent polynomials and matrices over a base ring of type T.
  8. Understand Nemo's generic interfaces

    master

    Nemo's generic implementations rely on specific interfaces defined for various mathematical structures. These interfaces are detailed in the AbstractAlgebra documentation. A generic implementation may depend on both required and optional functions (optional functions have generic fallbacks).

    Supported Interfaces

    • Ring: All commutative rings.
    • Field: All fields.
    • NCRing: All rings (not necessarily commutative).
    • Euclidean Ring: Euclidean rings. Note: Nemo algorithms may misbehave if the ring is not a domain (e.g., Z/nZ). There is currently no way to encode if a ring is a Euclidean domain via its type; instead, functions rely on the existence of gcdx.
    • Univariate Polynomial Ring: All dense univariate polynomials.
    • Multivariate Polynomial Ring: All sparse distributed multivariate polynomials.
    • 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.
  9. Understand Nemo's aliasing and mutation rules

    master

    Nemo follows specific rules regarding object mutation and aliasing to balance performance with correctness. Developers must distinguish between objects that are safe to mutate and those that are not.

    Key Rules for Developers:

    • Arithmetic Operations: Operations like +, -, *, ^, and unary minus always return a new object. These are safe to use as fresh copies.
    • deepcopy: Always returns a new object and is safe for use in unsafe functions.
    • Coercion R(a): If R is the parent of a, R(a) returns the original object a without copying. Do not use R(a) to create a copy for unsafe functions.
    • Other Functions: Most other functions may return the input object as the output. Therefore, their return values should not be assumed safe for use in unsafe/mutating functions.
    • Matrices vs. Other Objects:
      • Matrices are containers that may contain aliased elements.
      • Polynomials, Series, etc. are constructed from objects that must not alias one another, even in part.
    • Unsafe Operators: Operators like mul!, addmul!, zero!, and add! mutate their outputs. They should only be used if the output object is entirely under the caller's control (e.g., created specifically for accumulation).
  10. Handle immutable types when using unsafe operators

    master

    Because some Nemo objects are immutable, unsafe operators must be used with a specific assignment pattern to ensure correctness.

    When an unsafe operator is called on an immutable object, it cannot mutate the original; instead, it returns a new object representing the result. To ensure the variable holds the updated value, you must always assign the return value back to the original variable.

    Correct Pattern:

    a = add!(a, b)

    If a is mutable, the function modifies a in place and the assignment a = ... is effectively a no-op. If a is immutable, the assignment is required to update the variable a with the new object returned by the operator.

  11. Working with Gaussian Integers

    master

    Gaussian integers are provided by a pair of FLINT integers. You can access the ring and its fraction field using:

    • Nemo.GaussianIntegers(): The ring of Gaussian integers.
    • Nemo.GaussianRationals(): The field of Gaussian rationals.

    Elements can be constructed using ZZ(a, b) for $a + bi$ or by multiplying a ZZ element by im.

    ZZi = Nemo.GaussianIntegers()
    a = ZZi(3, 4) # 3 + 4*im
    b = ZZ(5)*im
    
    factor(a) # Returns prime factorization
  12. Understand Finite Field representations in Nemo

    master

    Nemo represents a finite field $K$ as a simple extension $K = k(\alpha) = k[x]/(f)$, where $k$ is the base field, $\alpha$ is the generator, and $f$ is the defining polynomial.

    There are two types of finite fields in Nemo:

    1. Absolute finite fields: The base field $k$ is a prime field $\mathbf{F}_p$.
    2. Relative finite fields: The base field $k$ is an arbitrary finite field.

    Note that field-theoretic properties such as basis, degree, or trace are defined relative to the base field. Methods prefixed with absolute_ return properties relative to the prime field $\mathbf{F}_p$.