Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ jobs:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v3
- name: MP
shell: julia --project=@. {0}
run: |
using Pkg
Pkg.add([
PackageSpec(name="DynamicPolynomials", rev="master"),
PackageSpec(name="TypedPolynomials", rev="master"),
PackageSpec(name="StarAlgebras", rev="bl/term"),
])

- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
#with:
Expand Down
26 changes: 14 additions & 12 deletions src/MultivariatePolynomials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,12 @@ polynomial of only one term.
"""
abstract type AbstractPolynomialLike{T} <: MA.AbstractMutable end

"""
AbstractTermLike{T}

Abstract type for a value that can act like a term. For instance, an `AbstractMonomial` is an `AbstractTermLike{Int}` since it can act as a term with coefficient `1`.
"""
abstract type AbstractTermLike{T} <: AbstractPolynomialLike{T} end

"""
AbstractMonomialLike

Abstract type for a value that can act like a monomial. For instance, an `AbstractVariable` is an `AbstractMonomialLike` since it can act as a monomial of one variable with degree `1`.
"""
abstract type AbstractMonomialLike <: AbstractTermLike{Int} end
abstract type AbstractMonomialLike <: AbstractPolynomialLike{Int} end

"""
AbstractVariable <: AbstractMonomialLike
Expand All @@ -46,11 +39,20 @@ Abstract type for a monomial, i.e. a product of variables elevated to a nonnegat
abstract type AbstractMonomial <: AbstractMonomialLike end

"""
AbstractTerm{T} <: AbstractTermLike{T}
AbstractTermLike{T}

Union type for values that can act like a term. This includes `AbstractMonomialLike`
(which acts as a term with coefficient `1`) and `SA.Term{T}`.
"""
const AbstractTermLike{T} = Union{AbstractMonomialLike,SA.Term{T}}

"""
AbstractTerm{T}

Abstract type for a term of coefficient type `T`, i.e. the product between a value of type `T` and a monomial.
Type alias for a term of coefficient type `T`, i.e. the product between a
value of type `T` and a monomial. This is [`StarAlgebras.Term{T}`](@ref).
"""
abstract type AbstractTerm{T} <: AbstractTermLike{T} end
const AbstractTerm{T} = SA.Term{T}

"""
AbstractPolynomial{T} <: AbstractPolynomialLike{T}
Expand All @@ -59,7 +61,7 @@ Abstract type for a polynomial of coefficient type `T`, i.e. a sum of `AbstractT
"""
abstract type AbstractPolynomial{T} <: AbstractPolynomialLike{T} end

const _APL{T} = AbstractPolynomialLike{T}
const _APL{T} = Union{AbstractPolynomialLike{T},SA.Term{T}}

include("zip.jl")
include("lazy_iterators.jl")
Expand Down
33 changes: 11 additions & 22 deletions src/comparison.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
Base.iszero(v::AbstractVariable) = false
Base.iszero(m::AbstractMonomial) = false
Base.iszero(t::AbstractTerm) = iszero(coefficient(t))
# SA.Term already has `iszero` defined in StarAlgebras
Base.iszero(t::AbstractPolynomial) = iszero(nterms(t))

Base.isone(v::AbstractVariable) = false
Base.isone(m::AbstractMonomial) = isconstant(m)
Base.isone(t::AbstractTerm) = isone(coefficient(t)) && isconstant(monomial(t))
# isone for SA.Term is defined in StarAlgebras using `isone(basis_element(t))`.
# For polynomials, `isone(monomial)` means `isconstant(monomial)` which should
# be equivalent to `isone(monomial)` as defined by the monomial type.
function Base.isone(p::AbstractPolynomial)
return isone(nterms(p)) && isone(first(terms(p)))
end
Expand Down Expand Up @@ -55,33 +57,20 @@ end
function Base.:(==)(v::AbstractVariable, mono::AbstractMonomial)
return isone(degree(mono)) && v == variable(mono)
end
function Base.:(==)(t::AbstractTerm, mono::AbstractMonomialLike)
function Base.:(==)(t::SA.Term, mono::AbstractMonomialLike)
return isone(coefficient(t)) && monomial(t) == mono
end
function Base.:(==)(mono::AbstractMonomialLike, t::AbstractTerm)
function Base.:(==)(mono::AbstractMonomialLike, t::SA.Term)
return isone(coefficient(t)) && mono == monomial(t)
end

function _compare_term(t1::AbstractTerm, t2::AbstractTerm, comp)
c1 = coefficient(t1)
c2 = coefficient(t2)
if iszero(c1)
iszero(c2)
else
comp(c1, c2) && comp(monomial(t1), monomial(t2))
end
end

Base.:(==)(t1::AbstractTerm, t2::AbstractTerm) = _compare_term(t1, t2, ==)
Base.:(==)(p::AbstractPolynomial, t::AbstractTerm) = right_term_eq(p, t)
Base.:(==)(t::AbstractTerm, p::AbstractPolynomial) = right_term_eq(p, t)
function Base.isequal(t1::AbstractTerm, t2::AbstractTerm)
return _compare_term(t1, t2, isequal)
end
function Base.isequal(p::AbstractPolynomial, t::AbstractTerm)
# ==, isequal for SA.Term × SA.Term are defined in StarAlgebras
Base.:(==)(p::AbstractPolynomial, t::SA.Term) = right_term_eq(p, t)
Base.:(==)(t::SA.Term, p::AbstractPolynomial) = right_term_eq(p, t)
function Base.isequal(p::AbstractPolynomial, t::SA.Term)
return right_term_eq(p, t; comp = isequal)
end
function Base.isequal(t::AbstractTerm, p::AbstractPolynomial)
function Base.isequal(t::SA.Term, p::AbstractPolynomial)
return right_term_eq(p, t; comp = isequal)
end

Expand Down
49 changes: 23 additions & 26 deletions src/conversion.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
function convert_constant end
Base.convert(::Type{P}, α) where {P<:_APL} = convert_constant(P, α)
function convert_constant(::Type{TT}, α) where {T,TT<:AbstractTerm{T}}
return term(convert(T, α), constant_monomial(TT))
Base.convert(::Type{P}, α) where {P<:AbstractPolynomialLike} = convert_constant(P, α)
function Base.convert(::Type{SA.Term{T,M}}, α) where {T,M}
return convert_constant(SA.Term{T,M}, α)
end
function convert_constant(::Type{SA.Term{T,M}}, α) where {T,M}
return term(convert(T, α), constant_monomial(SA.Term{T,M}))
end
function convert_constant(::Type{PT}, α) where {PT<:AbstractPolynomial}
return convert(PT, convert(term_type(PT), α))
Expand Down Expand Up @@ -35,40 +38,19 @@ end

function Base.convert(
::Type{M},
t::AbstractTerm,
t::SA.Term,
) where {M<:AbstractMonomialLike}
if isone(coefficient(t))
return convert(M, monomial(t))
else
throw(InexactError(:convert, M, t))
end
end
function Base.convert(
TT::Type{<:AbstractTerm{T}},
m::AbstractMonomialLike,
) where {T}
return convert(TT, term(one(T), convert(monomial_type(TT), m)))
end
function Base.convert(TT::Type{<:AbstractTerm{T}}, t::AbstractTerm) where {T}
return convert(
TT,
term(
convert(T, coefficient(t)),
convert(monomial_type(TT), monomial(t)),
),
)
end

# Base.convert(::Type{T}, t::T) where {T <: AbstractTerm} is ambiguous with above method.
# we need the following:
function Base.convert(::Type{TT}, t::TT) where {T,TT<:AbstractTerm{T}}
return t
end

function Base.convert(
::Type{T},
p::AbstractPolynomial,
) where {T<:AbstractTermLike}
) where {T<:AbstractMonomialLike}
if iszero(nterms(p))
convert(T, zero_term(p))
elseif isone(nterms(p))
Expand All @@ -77,6 +59,19 @@ function Base.convert(
throw(InexactError(:convert, T, p))
end
end
# Disambiguation: SA.Term{T,M} from AbstractPolynomial (more specific than the α catch-all)
function Base.convert(
::Type{SA.Term{T,M}},
p::AbstractPolynomial,
) where {T,M}
if iszero(nterms(p))
convert(SA.Term{T,M}, zero_term(p))
elseif isone(nterms(p))
convert(SA.Term{T,M}, leading_term(p))
else
throw(InexactError(:convert, SA.Term{T,M}, p))
end
end

MA.scaling(p::AbstractPolynomialLike{T}) where {T} = convert(T, p)
# Conversion polynomial -> constant
Expand All @@ -100,3 +95,5 @@ end

# Also covers, e.g., `convert(_APL, ::P)` where `P<:_APL`
Base.convert(::Type{PT}, p::PT) where {PT<:_APL} = p
# Disambiguation: identity conversion for AbstractPolynomialLike
Base.convert(::Type{PT}, p::PT) where {PT<:AbstractPolynomialLike} = p
135 changes: 60 additions & 75 deletions src/default_term.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
"""
struct Term{CoeffType,M<:AbstractMonomial} <: AbstractTerm{CoeffType}
coefficient::CoeffType
monomial::M
end
Term{CoeffType,M}

A representation of the multiplication between a `coefficient` and a `monomial`.
Type alias for [`StarAlgebras.Term{CoeffType,M}`](@ref).
A representation of the multiplication between a `coefficient` and a monomial.

!!! note
The `coefficient` does not need to be a `Number`. It can be for instance a
Expand All @@ -16,116 +14,103 @@ A representation of the multiplication between a `coefficient` and a `monomial`.
multiply each term of `p` with `m` but `term(p, m)` will create a term
with `p` as coefficient and `m` as monomial.
"""
struct Term{CoeffType,M<:AbstractMonomial} <: AbstractTerm{CoeffType}
coefficient::CoeffType
monomial::M
end
const Term{CoeffType,M} = SA.Term{CoeffType,M}

coefficient(t::Term) = t.coefficient
monomial(t::Term) = t.monomial
term_type(::Type{<:Term{C,M}}, ::Type{T}) where {C,M,T} = Term{T,M}
monomial_type(::Type{<:Term{C,M}}) where {C,M} = M
monomial_type(::Type{<:SA.Term{<:Any,M}}) where {M} = M

(t::Term)(s...) = substitute(Eval(), t, s)
(t::SA.Term)(s...) = substitute(Eval(), t, s)

function Base.convert(::Type{Term{T,M}}, m::AbstractMonomialLike) where {T,M}
return Term(one(T), convert(M, m))
end
function Base.convert(::Type{Term{T,M}}, t::AbstractTerm) where {T,M}
return Term{T,M}(convert(T, coefficient(t)), convert(M, monomial(t)))
end
function Base.convert(::Type{Term{T,M}}, t::Term{T,M}) where {T,M}
return t
# convert from AbstractMonomialLike (MP type in signature, not piracy)
function Base.convert(::Type{SA.Term{T,M}}, m::AbstractMonomialLike) where {T,M}
return SA.Term(one(T), convert(M, m))
end

function convert_constant(::Type{Term{C,M} where C}, α) where {M}
return convert(Term{typeof(α),M}, α)
end
# convert, promote_rule, ==, isequal, hash, copy, ^, ndims, broadcastable
# for SA.Term × SA.Term are defined in StarAlgebras (not here, to avoid piracy)

# promote_rule involving MP's AbstractMonomialLike (not piracy)
function Base.promote_rule(
::Type{Term{C,M1} where {C}},
::Type{SA.Term{C,M1} where {C}},
M2::Type{<:AbstractMonomialLike},
) where {M1}
return (Term{C,promote_type(M1, M2)} where {C})
return (SA.Term{C,promote_type(M1, M2)} where {C})
end
function Base.promote_rule(
M1::Type{<:AbstractMonomialLike},
::Type{Term{C,M2} where {C}},
::Type{SA.Term{C,M2} where {C}},
) where {M2}
return (Term{C,promote_type(M1, M2)} where {C})
return (SA.Term{C,promote_type(M1, M2)} where {C})
end
# promote_rule with UnionAll Term type (involves `where C` which is MP convention)
function Base.promote_rule(
::Type{Term{C,M1} where {C}},
::Type{Term{T,M2}},
::Type{SA.Term{C,M1} where {C}},
::Type{SA.Term{T,M2}},
) where {T,M1,M2}
return (Term{C,promote_type(M1, M2)} where {C})
return (SA.Term{C,promote_type(M1, M2)} where {C})
end
function Base.promote_rule(
::Type{Term{T,M2}},
::Type{Term{C,M1} where {C}},
::Type{SA.Term{T,M2}},
::Type{SA.Term{C,M1} where {C}},
) where {T,M1,M2}
return (Term{C,promote_type(M1, M2)} where {C})
return (SA.Term{C,promote_type(M1, M2)} where {C})
end
promote_rule_constant(::Type{T}, TT::Type{SA.Term{C,M} where C}) where {T,M} = Any

function convert_constant(::Type{SA.Term{C,M} where C}, α) where {M}
return convert(SA.Term{typeof(α),M}, α)
end
promote_rule_constant(::Type{T}, TT::Type{Term{C,M} where C}) where {T,M} = Any

combine(t1::Term, t2::Term) = combine(promote(t1, t2)...)
function combine(t1::T, t2::T) where {T<:Term}
return Term(t1.coefficient + t2.coefficient, t1.monomial)
combine(t1::SA.Term, t2::SA.Term) = combine(promote(t1, t2)...)
function combine(t1::T, t2::T) where {T<:SA.Term}
return SA.Term(coefficient(t1) + coefficient(t2), monomial(t1))
end
function MA.promote_operation(
::typeof(combine),
::Type{Term{S,M1}},
::Type{Term{T,M2}},
::Type{SA.Term{S,M1}},
::Type{SA.Term{T,M2}},
) where {S,T,M1,M2}
return Term{MA.promote_operation(+, S, T),promote_type(M1, M2)}
return SA.Term{MA.promote_operation(+, S, T),promote_type(M1, M2)}
end

function MA.mutability(::Type{Term{C,M}}) where {C,M}
if MA.mutability(C) isa MA.IsMutable && MA.mutability(M) isa MA.IsMutable
return MA.IsMutable()
else
return MA.IsNotMutable()
end
end
# MA.mutability, Base.copy, MA.mutable_copy, MA.operate_to!(*, Term, Term, Term),
# MA.operate!(*, Term, Term), and MA.operate!(one, Term) are defined in StarAlgebras.

# `Base.power_by_squaring` calls `Base.copy` and we want
# `t^1` to be a mutable copy of `t` so `copy` needs to be
# the same as `mutable_copy`.
Base.copy(t::Term) = MA.mutable_copy(t)
function MA.mutable_copy(t::Term)
return Term(
MA.copy_if_mutable(coefficient(t)),
MA.copy_if_mutable(monomial(t)),
)
# Broader MA operations involving MP's AbstractTermLike (not piracy)
function MA.operate_to!(
t::SA.Term,
::typeof(*),
t1::AbstractMonomialLike,
t2::AbstractTermLike,
)
MA.operate_to!(t.coefficient, *, coefficient(t1), coefficient(t2))
MA.operate_to!(t.basis_element, *, monomial(t1), monomial(t2))
return t
end

function MA.operate_to!(
t::Term,
t::SA.Term,
::typeof(*),
t1::AbstractTermLike,
t2::AbstractTermLike,
t2::AbstractMonomialLike,
)
MA.operate_to!(t.coefficient, *, coefficient(t1), coefficient(t2))
MA.operate_to!(t.monomial, *, monomial(t1), monomial(t2))
MA.operate_to!(t.basis_element, *, monomial(t1), monomial(t2))
return t
end

function MA.operate!(::typeof(*), t1::Term, t2::AbstractTermLike)
MA.operate!(*, t1.coefficient, coefficient(t2))
MA.operate!(*, t1.monomial, monomial(t2))
return t1
function MA.operate_to!(
t::SA.Term,
::typeof(*),
t1::AbstractMonomialLike,
t2::AbstractMonomialLike,
)
MA.operate_to!(t.coefficient, *, coefficient(t1), coefficient(t2))
MA.operate_to!(t.basis_element, *, monomial(t1), monomial(t2))
return t
end

# Needed to resolve ambiguity with
# MA.operate!(::typeof(*), ::AbstractMonomial, ::AbstractMonomialLike)
function MA.operate!(::typeof(*), t1::Term, t2::AbstractMonomialLike)
function MA.operate!(::typeof(*), t1::SA.Term, t2::AbstractMonomialLike)
MA.operate!(*, t1.coefficient, coefficient(t2))
MA.operate!(*, t1.monomial, monomial(t2))
MA.operate!(*, t1.basis_element, monomial(t2))
return t1
end

function MA.operate!(::typeof(one), t::Term)
MA.operate!(one, t.coefficient)
MA.operate!(constant_monomial, t.monomial)
return t
end
Loading
Loading