Skip to content

Commit 4f56b8e

Browse files
authored
Merge pull request #52 from JuliaAlgebra/bl/mutable_arithmetics
Implement MutableArithmetics
2 parents 5e86595 + ab8db24 commit 4f56b8e

File tree

14 files changed

+354
-73
lines changed

14 files changed

+354
-73
lines changed

Project.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ repo = "https://github.com/JuliaAlgebra/DynamicPolynomials.jl.git"
44
version = "0.3.3"
55

66
[deps]
7+
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
8+
Future = "9fa8497b-333b-5362-9e8d-4d0656e87820"
79
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
810
MultivariatePolynomials = "102ac46a-7ee4-5c85-9060-abc95bfdeaa3"
11+
MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
912
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
1013
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1114
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1215

1316
[compat]
14-
MultivariatePolynomials = "~0.3.2"
17+
MultivariatePolynomials = "~0.3.3"
18+
MutableArithmetics = "0.1.1"
1519
Reexport = ">= 0.2"
1620
julia = "1"

src/DynamicPolynomials.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
module DynamicPolynomials
22

3+
import Future # For `copy!`
4+
35
using Reexport
46
@reexport using MultivariatePolynomials
57
const MP = MultivariatePolynomials
68

9+
import MutableArithmetics
10+
const MA = MutableArithmetics
11+
12+
using DataStructures
713

814
include("var.jl")
915
include("mono.jl")
1016
const DMonomialLike{C} = Union{Monomial{C}, PolyVar{C}}
17+
MA.mutability(::Type{<:Monomial}) = MA.IsMutable()
1118
include("term.jl")
19+
MA.mutability(::Type{Term{C, T}}) where {C, T} = MA.mutability(T)
1220
include("monovec.jl")
1321
include("poly.jl")
22+
MA.mutability(::Type{<:Polynomial}) = MA.IsMutable()
1423
const TermPoly{C, T} = Union{Term{C, T}, Polynomial{C, T}}
1524
const PolyType{C} = Union{Polynomial{C}, Term{C}, Monomial{C}, PolyVar{C}}
1625
MP.variable_union_type(::Union{PolyType{C}, Type{<:PolyType{C}}}) where {C} = PolyVar{C}
@@ -35,6 +44,7 @@ MP.variables(p::Union{PolyType, MonomialVector, AbstractArray{<:PolyType}}) = _v
3544
MP.nvariables(p::Union{PolyType, MonomialVector, AbstractArray{<:PolyType}}) = length(_vars(p))
3645
MP.similarvariable(p::Union{PolyType{C}, Type{<:PolyType{C}}}, ::Type{Val{V}}) where {C, V} = PolyVar{C}(string(V))
3746
MP.similarvariable(p::Union{PolyType{C}, Type{<:PolyType{C}}}, V::Symbol) where {C} = PolyVar{C}(string(V))
47+
3848
include("promote.jl")
3949

4050
include("operators.jl")

src/cmult.jl

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,38 @@ function Base.:(*)(x::PolyVar{true}, y::MonomialVector{true})
2323
w, updatez = multiplyvar(y.vars, x)
2424
MonomialVector{true}(w, updatez.(y.Z))
2525
end
26+
function multdivmono!(output_variables::Vector{PolyVar{true}},
27+
v::Vector{PolyVar{true}}, x::Monomial{true}, op)
28+
if v == x.vars
29+
updatez! = (output, z) -> @. output = op(z, x.z)
30+
else
31+
w, maps = mergevars([v, x.vars])
32+
n = length(v)
33+
resize!(output_variables, length(w))
34+
output_variables[maps[1]] = v[1:n]
35+
updatez! = (output, z) -> begin
36+
resize!(output, length(w))
37+
z[maps[1]] = z[1:n]
38+
I = maps[1]; i = 1; lI = length(I)
39+
J = maps[2]; j = 1; lJ = length(J)
40+
while i <= lI || j <= lJ
41+
if i > lI || (j <= lJ && J[j] < I[i])
42+
output[J[j]] = op(0, x.z[j])
43+
j += 1
44+
elseif j > lJ || (i <= lI && I[i] < J[j])
45+
output[I[i]] = op(z[I[i]], 0)
46+
i += 1
47+
else
48+
@assert I[i] == J[j]
49+
output[I[i]] = op(z[I[i]], x.z[j])
50+
i += 1
51+
j += 1
52+
end
53+
end
54+
end
55+
end
56+
return updatez!
57+
end
2658
function multdivmono(v::Vector{PolyVar{true}}, x::Monomial{true}, op)
2759
if v == x.vars
2860
# /!\ no copy done here for efficiency, do not mess up with vars
@@ -53,7 +85,17 @@ function multdivmono(v::Vector{PolyVar{true}}, x::Monomial{true}, op)
5385
end
5486
w, updatez
5587
end
56-
function MP.mapexponents(f, x::Monomial{true}, y::Monomial{true})
88+
function MP.mapexponents_to!(output::Monomial{true}, f::Function, x::Monomial{true}, y::Monomial{true})
89+
updatez! = multdivmono!(output.vars, x.vars, y, f)
90+
updatez!(output.z, x.z)
91+
return x
92+
end
93+
function MP.mapexponents!(f::Function, x::Monomial{true}, y::Monomial{true})
94+
updatez! = multdivmono!(x.vars, x.vars, y, f)
95+
updatez!(x.z, x.z)
96+
return x
97+
end
98+
function MP.mapexponents(f::Function, x::Monomial{true}, y::Monomial{true})
5799
w, updatez = multdivmono(x.vars, y, f)
58100
Monomial{true}(w, updatez(x.z))
59101
end

src/comp.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ Base.isless(x::PolyVar{C}, y::PolyVar{C}) where C = isless(y.id, x.id)
3030
# Comparison of Monomial
3131

3232
# graded lex ordering
33+
function samevars_grlex(x::Vector{Int}, y::Vector{Int})
34+
@assert length(x) == length(y)
35+
degx = sum(x)
36+
degy = sum(y)
37+
if degx != degy
38+
degx - degy
39+
else
40+
for i in eachindex(x)
41+
if x[i] != y[i]
42+
return x[i] - y[i]
43+
end
44+
end
45+
return 0
46+
end
47+
end
3348
function mycomp(x::Monomial{C}, y::Monomial{C}) where C
3449
degx = degree(x)
3550
degy = degree(y)

src/mono.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ end
3535
Monomial::Number) = convert(Monomial{true}, α)
3636

3737
Base.broadcastable(m::Monomial) = Ref(m)
38-
Base.copy(m::M) where {M<:Monomial} = M(m.vars, copy(m.z))
38+
MA.mutable_copy(m::M) where {M<:Monomial} = M(copy(m.vars), copy(m.z))
39+
Base.copy(m::Monomial) = MA.mutable_copy(m)
3940

4041
# Generate canonical reperesentation by removing variables that are not used
4142
function canonical(m::Monomial)

src/monovec.jl

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ function Base.hash(m::MonomialVector, u::UInt)
3535
end
3636
end
3737

38-
# /!\ vars not copied, do not mess with vars
39-
Base.copy(m::MV) where {MV<:MonomialVector} = MV(m.vars, copy(m.Z))
38+
# vars copied as it may be modifed by `_add_variables!`
39+
# `mutable_copy` recursively copies the vector or vector of integers.
40+
MA.mutable_copy(m::MV) where {MV<:MonomialVector} = MV(copy(m.vars), MA.mutable_copy(m.Z))
41+
Base.copy(m::MonomialVector) = MA.mutable_copy(m)
4042
function Base.getindex(x::MV, I) where {MV<:MonomialVector}
4143
MV(x.vars, x.Z[sort(I)])
4244
end
@@ -239,3 +241,16 @@ function MP.mergemonovec(ms::Vector{MonomialVector{C}}) where {C}
239241
# There is no duplicate by construction
240242
return MonomialVector{C}(buildZvarsvec(PolyVar{C}, X)...)
241243
end
244+
245+
function _add_variables!(monos::MonomialVector{C}, allvars::Vector{PolyVar{C}}, map) where C
246+
Future.copy!(monos.vars, allvars)
247+
if !isempty(monos.Z)
248+
tmp = similar(first(monos.Z))
249+
for z in monos.Z
250+
Future.copy!(tmp, z)
251+
resize!(z, length(allvars))
252+
fill!(z, 0)
253+
z[map] = tmp
254+
end
255+
end
256+
end

src/mult.jl

Lines changed: 71 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@ include("ncmult.jl")
3131

3232
MP.multconstant(α, x::Monomial) = Term(α, x)
3333
MP.mapcoefficientsnz(f::Function, p::Polynomial) = Polynomial(f.(p.a), p.x)
34+
function MP.mapcoefficientsnz_to!(output::Polynomial, f::Function, t::MP.AbstractTermLike)
35+
MP.mapcoefficientsnz_to!(output, f, polynomial(t))
36+
end
37+
function MP.mapcoefficientsnz_to!(output::Polynomial, f::Function, p::Polynomial)
38+
resize!(output.a, length(p.a))
39+
@. output.a = f(p.a)
40+
Future.copy!(output.x.vars, p.x.vars)
41+
# TODO reuse the part of `Z` that is already in `output`.
42+
resize!(output.x.Z, length(p.x.Z))
43+
for i in eachindex(p.x.Z)
44+
output.x.Z[i] = copy(p.x.Z[i])
45+
end
46+
return output
47+
end
3448

3549
# I do not want to cast x to TermContainer because that would force the promotion of eltype(q) with Int
3650
function Base.:(*)(x::DMonomialLike, p::Polynomial)
@@ -48,7 +62,7 @@ function Base.:(*)(p::Polynomial, x::DMonomialLike)
4862
end
4963

5064
function _term_poly_mult(t::Term{C, S}, p::Polynomial{C, T}, op::Function) where {C, S, T}
51-
U = Base.promote_op(op, S, T)
65+
U = MA.promote_operation(op, S, T)
5266
if iszero(t)
5367
zero(Polynomial{C, U})
5468
else
@@ -67,35 +81,64 @@ end
6781
Base.:(*)(p::Polynomial, t::Term) = _term_poly_mult(t, p, (α, β) -> β * α)
6882
Base.:(*)(t::Term, p::Polynomial) = _term_poly_mult(t, p, *)
6983
_sumprod(a, b) = a * b + a * b
70-
function Base.:(*)(p::Polynomial{C, S}, q::Polynomial{C, T}) where {C, S, T}
71-
U = Base.promote_op(_sumprod, S, T)
72-
if iszero(p) || iszero(q)
73-
zero(Polynomial{C, U})
84+
function _mul(::Type{T}, p::AbstractPolynomialLike, q::AbstractPolynomialLike) where T
85+
return _mul(T, polynomial(p), polynomial(q))
86+
end
87+
function _mul(::Type{T}, p::Polynomial{true}, q::Polynomial{true}) where T
88+
samevars = _vars(p) == _vars(q)
89+
if samevars
90+
allvars = _vars(p)
7491
else
75-
samevars = _vars(p) == _vars(q)
76-
if samevars
77-
allvars = _vars(p)
78-
else
79-
allvars, maps = mergevars([_vars(p), _vars(q)])
80-
end
81-
N = length(p)*length(q)
82-
Z = Vector{Vector{Int}}(undef, N)
83-
a = Vector{U}(undef, N)
84-
i = 0
85-
for u in p
86-
for v in q
87-
if samevars
88-
z = u.x.z + v.x.z
89-
else
90-
z = zeros(Int, length(allvars))
91-
z[maps[1]] += u.x.z
92-
z[maps[2]] += v.x.z
93-
end
94-
i += 1
95-
Z[i] = z
96-
a[i] = u.α * v.α
92+
allvars, maps = mergevars([_vars(p), _vars(q)])
93+
end
94+
N = length(p) * length(q)
95+
Z = Vector{Vector{Int}}(undef, N)
96+
a = Vector{T}(undef, N)
97+
i = 0
98+
for u in p
99+
for v in q
100+
if samevars
101+
z = u.x.z + v.x.z
102+
else
103+
z = zeros(Int, length(allvars))
104+
z[maps[1]] += u.x.z
105+
z[maps[2]] += v.x.z
97106
end
107+
i += 1
108+
Z[i] = z
109+
a[i] = u.α * v.α
98110
end
99-
polynomialclean(allvars, a, Z)
100111
end
112+
return allvars, a, Z
113+
end
114+
function Base.:(*)(p::Polynomial{true, S}, q::Polynomial{true, T}) where {S, T}
115+
if iszero(p) || iszero(q)
116+
zero(MA.promote_operation(*, typeof(p), typeof(q)))
117+
else
118+
polynomialclean(_mul(MA.promote_operation(MA.add_mul, S, T), p, q)...)
119+
end
120+
end
121+
function MA.mutable_operate_to!(p::Polynomial{false, T}, ::typeof(*), q1::MP.AbstractPolynomialLike, q2::MP.AbstractPolynomialLike) where T
122+
if iszero(q1) || iszero(q2)
123+
MA.mutable_operate!(zero, p)
124+
else
125+
ts = Term{false, T}[]
126+
MP.mul_to_terms!(ts, q1, q2)
127+
# TODO do better than create tmp
128+
tmp = polynomial!(ts)
129+
Future.copy!(p.a, tmp.a)
130+
Future.copy!(p.x.vars, tmp.x.vars)
131+
Future.copy!(p.x.Z, tmp.x.Z)
132+
return p
133+
end
134+
end
135+
function MA.mutable_operate_to!(p::Polynomial{true, T}, ::typeof(*), q1::MP.AbstractPolynomialLike, q2::MP.AbstractPolynomialLike) where T
136+
if iszero(q1) || iszero(q2)
137+
MA.mutable_operate!(zero, p)
138+
else
139+
polynomialclean_to!(p, _mul(T, q1, q2)...)
140+
end
141+
end
142+
function MA.mutable_operate!(::typeof(*), p::Polynomial{C}, q::Polynomial{C}) where C
143+
return MA.mutable_operate_to!(p, *, p, q)
101144
end

0 commit comments

Comments
 (0)