9xdSq-LIMPS-FemTO-R1C / monovec.jl
9x25dillon's picture
Upload 19 files
60a3e1e verified
export MonomialVector
# Invariant: Always sorted and no zero vector
struct MonomialVector{C} <: AbstractVector{Monomial{C}}
vars::Vector{PolyVar{C}}
Z::Vector{Vector{Int}}
function MonomialVector{C}(vars::Vector{PolyVar{C}}, Z::Vector{Vector{Int}}) where {C}
@assert !C || issorted(vars, rev=true)
@assert all(z -> length(z) == length(vars), Z)
@assert issorted(Z, rev=true, lt=grlex)
new(vars, Z)
end
end
MonomialVector(vars::Vector{PolyVar{C}}, Z::Vector{Vector{Int}}) where {C} = MonomialVector{C}(vars, Z)
MonomialVector{C}() where {C} = MonomialVector{C}(PolyVar{C}[], Vector{Int}[])
# Generate canonical reperesentation by removing variables that are not used
function canonical(m::MonomialVector)
v = zeros(Bool, nvariables(m))
for z in m.Z
v = [v[i] || z[i] > 0 for i in eachindex(v)]
end
MonomialVector(_vars(m)[v], Vector{Int}[z[v] for z in m.Z])
end
function Base.hash(m::MonomialVector, u::UInt)
cm = canonical(m)
if length(cm.Z) == 0
hash([], u)
elseif length(cm.Z) == 1
hash(Monomial(_vars(cm), cm.Z[1]), u)
else
hash(_vars(cm), hash(cm.Z, hash(u)))
end
end
# /!\ vars not copied, do not mess with vars
Base.copy(m::MV) where {MV<:MonomialVector} = MV(m.vars, copy(m.Z))
function Base.getindex(x::MV, I) where {MV<:MonomialVector}
MV(x.vars, x.Z[sort(I)])
end
Base.getindex(x::MonomialVector, i::Integer) = Monomial(x.vars, x.Z[i])
Base.firstindex(x::MonomialVector) = firstindex(x.Z)
Base.lastindex(x::MonomialVector) = lastindex(x.Z)
Base.size(x::MonomialVector) = (length(x),)
Base.length(x::MonomialVector) = length(x.Z)
Base.isempty(x::MonomialVector) = length(x) == 0
Base.iterate(x::MonomialVector) = isempty(x) ? nothing : (x[1], 1)
function Base.iterate(x::MonomialVector, state::Int)
state < length(x) ? (x[state+1], state+1) : nothing
end
MultivariatePolynomials.extdegree(x::MonomialVector) = isempty(x) ? (0, 0) : extrema(sum.(x.Z))
MultivariatePolynomials.mindegree(x::MonomialVector) = isempty(x) ? 0 : minimum(sum.(x.Z))
MultivariatePolynomials.maxdegree(x::MonomialVector) = isempty(x) ? 0 : maximum(sum.(x.Z))
_vars(m::Union{Monomial, MonomialVector}) = m.vars
# Recognize arrays of monomials of this module
# [x, y] -> Vector{PolyVar}
# [x, x*y] -> Vector{Monomial}
# [1, x] -> Vector{Term{Int}}
const DMonoVecElemNonConstant{C} = Union{PolyVar{C}, Monomial{C}, Term{C}}
# [1] -> Vector{Int}
const DMonoVecElem{C} = Union{Int, DMonoVecElemNonConstant{C}}
const DMonoVec{C} = AbstractVector{<:DMonoVecElem{C}}
MP.emptymonovec(vars::AbstractVector{PolyVar{C}}) where {C} = MonomialVector{C}(vars, Vector{Int}[])
MP.emptymonovec(t::DMonoVecElemNonConstant) = emptymonovec(_vars(t))
MP.emptymonovec(::Type{<:DMonoVecElemNonConstant{C}}) where {C} = MonomialVector{C}()
function fillZfordeg!(Z, n, deg, ::Type{Val{true}}, filter::Function)
z = zeros(Int, n)
z[1] = deg
while true
if filter(z)
push!(Z, z)
z = copy(z)
end
if z[end] == deg
break
end
sum = 1
for j in (n-1):-1:1
if z[j] != 0
z[j] -= 1
z[j+1] += sum
break
else
sum += z[j+1]
z[j+1] = 0
end
end
end
end
function fillZrec!(Z, z, i, n, deg, filter::Function)
if deg == 0
if filter(z)
push!(Z, copy(z))
end
else
for i in i:i+n-1
z[i] += 1
fillZrec!(Z, z, i, n, deg-1, filter)
z[i] -= 1
end
end
end
function fillZfordeg!(Z, n, deg, ::Type{Val{false}}, filter::Function)
z = zeros(Int, deg * n - deg + 1)
fillZrec!(Z, z, 1, n, deg, filter)
end
# List exponents in decreasing Graded Lexicographic Order
function getZfordegs(n, degs::AbstractVector{Int}, ::Type{Val{C}}, filter::Function) where C
Z = Vector{Vector{Int}}()
for deg in sort(degs, rev=true)
fillZfordeg!(Z, n, deg, Val{C}, filter)
end
@assert issorted(Z, rev=true, lt=grlex)
Z
end
function MonomialVector(vars::Vector{PolyVar{true}}, degs::AbstractVector{Int}, filter::Function = x->true)
MonomialVector{true}(vars, getZfordegs(length(vars), degs, Val{true}, z -> filter(Monomial(vars, z))))
end
function getvarsforlength(vars::Vector{PolyVar{false}}, len::Int)
n = length(vars)
map(i -> vars[((i-1) % n) + 1], 1:len)
end
function MonomialVector(vars::Vector{PolyVar{false}}, degs::AbstractVector{Int}, filter::Function = x->true)
Z = getZfordegs(length(vars), degs, Val{false}, z -> filter(Monomial(getvarsforlength(vars, length(z)), z)))
v = isempty(Z) ? vars : getvarsforlength(vars, length(first(Z)))
MonomialVector{false}(v, Z)
end
MonomialVector(vars::Vector{<:PolyVar}, degs::Int, filter::Function = x->true) = MonomialVector(vars, [degs], filter)
MP.monomials(vars::AbstractVector{<:PolyVar}, args...) = MonomialVector(vars, args...)
MP.monomials(vars::Tuple{Vararg{PolyVar}}, args...) = monomials([vars...], args...)
#function MP.monomials(vars::TupOrVec{PolyVar{true}}, degs::AbstractVector{Int}, filter::Function = x->true)
# Z = getZfordegs(length(vars), degs, true, z -> filter(Monomial(vars, z)))
# [Monomial{true}(vars, z) for z in Z]
#end
#function MP.monomials(vars::TupOrVec{PolyVar{false}}, degs::AbstractVector{Int}, filter::Function = x->true)
# Z = getZfordegs(length(vars), degs, false, z -> filter(Monomial(vars, z)))
# v = isempty(Z) ? vars : getvarsforlength(vars, length(first(Z)))
# [Monomial{false}(v, z) for z in Z]
#end
#MP.monomials(vars::TupOrVec{PV}, degs::Int, filter::Function = x->true) where {PV<:PolyVar} = monomials(vars, [degs], filter)
function buildZvarsvec(::Type{PV}, X::DMonoVec) where {PV<:PolyVar}
varsvec = Vector{PV}[ (isa(x, DMonoVecElemNonConstant) ? _vars(x) : PolyVar[]) for x in X ]
allvars, maps = mergevars(varsvec)
nvars = length(allvars)
Z = [zeros(Int, nvars) for i in 1:length(X)]
offset = 0
for (i, x) in enumerate(X)
if isa(x, PolyVar)
@assert length(maps[i]) == 1
z = [1]
elseif isa(x, Monomial)
z = x.z
elseif isa(x, Term)
z = x.x.z
else
@assert isa(x, Int)
z = Int[]
end
Z[i][maps[i]] = z
end
allvars, Z
end
MP.sortmonovec(X::MonomialVector) = (1:length(X), X)
function _sortmonovec(X::DMonoVec{C}) where {C}
allvars, Z = buildZvarsvec(PolyVar{C}, X)
σ = sortperm(Z, rev=true, lt=grlex)
allvars, Z, σ
end
function _removedups!(Z::Vector{Vector{Int}}, σ::Vector{Int})
dups = findall(i -> Z[σ[i]] == Z[σ[i-1]], 2:length(σ))
deleteat!(σ, dups)
end
function MP.sortmonovec(X::DMonoVec{C}) where {C}
if isempty(X)
Int[], MonomialVector{C}()
else
allvars, Z, σ = _sortmonovec(X)
_removedups!(Z, σ)
σ, MonomialVector{C}(allvars, Z[σ])
end
end
function MonomialVector{C}(X::DMonoVec{C}) where C
allvars, Z = buildZvarsvec(PolyVar{C}, X)
sort!(Z, rev=true, lt=grlex)
dups = findall(i -> Z[i] == Z[i-1], 2:length(Z))
deleteat!(Z, dups)
MonomialVector{C}(allvars, Z)
end
function MonomialVector(X)
monovectype(X)(X)
end
MP.monovectype(X::Union{DMonoVecElemNonConstant{C}, Type{<:DMonoVecElemNonConstant{C}}, DMonoVec{C}, Type{<:DMonoVec{C}}}) where {C} = MonomialVector{C}
function MP.monovec(X::DMonoVec)
MonomialVector(X)
end
MP.monovec(a, mv::MonomialVector) = (a, mv)
function MP.mergemonovec(ms::Vector{MonomialVector{C}}) where {C}
m = length(ms)
I = ones(Int, length(ms))
L = length.(ms)
X = Vector{Monomial{C}}()
while any(I .<= L)
max = nothing
for i in 1:m
if I[i] <= L[i]
x = ms[i][I[i]]
if max === nothing || max < x
max = x
end
end
end
@assert max !== nothing
# to ensure that max is no more a union
max === nothing && return X
push!(X, max)
for i in 1:m
if I[i] <= L[i] && max == ms[i][I[i]]
I[i] += 1
end
end
end
# There is no duplicate by construction
return MonomialVector{C}(buildZvarsvec(PolyVar{C}, X)...)
end