Skip to content

Commit c9cbca9

Browse files
authored
Shortcut for dividing by one (#241)
* Specialize on one term * Export * Fixes * DP#bl/mapexponents * Fixes * Fix * Fix * Fix
1 parent d222395 commit c9cbca9

File tree

9 files changed

+83
-28
lines changed

9 files changed

+83
-28
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
shell: julia --project=@. {0}
5252
run: |
5353
using Pkg
54-
Pkg.add(PackageSpec(name="DynamicPolynomials", rev="bl/mutable_rem"))
54+
Pkg.add(PackageSpec(name="DynamicPolynomials", rev="bl/mapexponents"))
5555
- uses: julia-actions/julia-buildpkg@v1
5656
- uses: julia-actions/julia-runtest@v1
5757
with:

docs/src/division.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The `divrem` function returns ``(q, r)``.
1212

1313
```@docs
1414
divides
15+
div_multiple
1516
pseudo_rem
1617
rem_or_pseudo_rem
1718
```

src/default_polynomial.jl

+18-1
Original file line numberDiff line numberDiff line change
@@ -301,14 +301,31 @@ function MA.operate_to!(output::Polynomial, ::typeof(*), p::Polynomial, q::Polyn
301301
return output
302302
end
303303
function MA.operate!(::typeof(*), p::Polynomial, q::Polynomial)
304-
return MA.operate_to!(p, *, MA.mutable_copy(p), q)
304+
if iszero(q)
305+
return MA.operate!(zero, p)
306+
elseif nterms(q) == 1
307+
return MA.operate!(*, p, leadingterm(q))
308+
else
309+
return MA.operate_to!(p, *, MA.mutable_copy(p), q)
310+
end
305311
end
306312
function MA.operate!(::typeof(*), p::Polynomial, t::AbstractTermLike)
307313
for i in eachindex(p.terms)
308314
p.terms[i] = MA.operate!!(*, p.terms[i], t)
309315
end
310316
return p
311317
end
318+
function mapexponents!(f, p::Polynomial, m::AbstractMonomialLike)
319+
for i in eachindex(p.terms)
320+
t = p.terms[i]
321+
p.terms[i] = term(coefficient(t), mapexponents(f, monomial(t), m))
322+
end
323+
return p
324+
end
325+
function mapexponents(f, p::Polynomial, m::AbstractMonomialLike)
326+
P = MA.promote_operation(*, typeof(p), typeof(m))
327+
return mapexponents!(f, MA.mutable_copy(convert(P, p)), m)
328+
end
312329

313330
function MA.operate!(::typeof(zero), p::Polynomial)
314331
empty!(p.terms)

src/division.jl

+48-19
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export divides, pseudo_rem, rem_or_pseudo_rem
1+
export divides, div_multiple, pseudo_rem, rem_or_pseudo_rem
22

33
function Base.round(p::APL; args...)
44
# round(0.1) is zero so we cannot use `mapcoefficientsnz`
@@ -44,32 +44,61 @@ _field_absorb(::UFD, ::Field) = Field()
4444
_field_absorb(::Field, ::UFD) = Field()
4545
_field_absorb(::Field, ::Field) = Field()
4646

47-
# _div(a, b) assumes that b divides a
48-
_div(::Field, a, b) = a / b
49-
_div(::UFD, a, b) = div(a, b)
50-
_div(a, b) = _div(algebraic_structure(promote_type(typeof(a), typeof(b))), a, b)
51-
_div(m1::AbstractMonomialLike, m2::AbstractMonomialLike) = mapexponents(-, m1, m2)
52-
function _div(t::AbstractTerm, m::AbstractMonomial)
53-
term(coefficient(t), _div(monomial(t), m))
47+
"""
48+
div_multiple(a, b, ma::MA.MutableTrait)
49+
50+
Return the division of `a` by `b` assuming that `a` is a multiple of `b`.
51+
If `a` is not a multiple of `b` then this function may return anything.
52+
"""
53+
div_multiple(::Field, a, b, ma::MA.MutableTrait) = a / b
54+
div_multiple(::UFD, a, b, ma::MA.IsMutable) = MA.operate!!(div, a, b)
55+
div_multiple(::UFD, a, b, ma::MA.IsNotMutable) = div(a, b)
56+
function div_multiple(a, b, ma::MA.MutableTrait=MA.IsNotMutable())
57+
return div_multiple(algebraic_structure(promote_type(typeof(a), typeof(b))), a, b, ma)
5458
end
55-
function _div(t1::AbstractTermLike, t2::AbstractTermLike)
56-
term(_div(coefficient(t1), coefficient(t2)), _div(monomial(t1), monomial(t2)))
59+
function div_multiple(m1::AbstractMonomialLike, m2::AbstractMonomialLike, ::MA.MutableTrait=MA.IsNotMutable())
60+
return mapexponents(-, m1, m2)
5761
end
58-
function _div(f::APL, g::APL)
62+
function div_multiple(t::AbstractTerm, m::AbstractMonomial, mt::MA.MutableTrait=MA.IsNotMutable())
63+
term(_copy(coefficient(t), mt), div_multiple(monomial(t), m))
64+
end
65+
function div_multiple(t1::AbstractTermLike, t2::AbstractTermLike, m1::MA.MutableTrait=MA.IsNotMutable())
66+
term(div_multiple(coefficient(t1), coefficient(t2), m1), div_multiple(monomial(t1), monomial(t2)))
67+
end
68+
function right_constant_div_multiple(f::APL, g, mf::MA.MutableTrait=MA.IsNotMutable())
69+
if isone(g)
70+
return _copy(f, mf)
71+
end
72+
return mapcoefficients(coef -> div_multiple(coef, g, mf), f, mf; nonzero = true)
73+
end
74+
function div_multiple(f::APL, g::AbstractMonomialLike, mf::MA.MutableTrait=MA.IsNotMutable())
75+
if isconstant(g)
76+
return _copy(f, mf)
77+
end
78+
return mapexponents(-, f, g, mf)
79+
end
80+
function div_multiple(f::APL, g::AbstractTermLike, mf::MA.MutableTrait=MA.IsNotMutable())
81+
f = right_constant_div_multiple(f, coefficient(g), mf)
82+
return div_multiple(f, monomial(g), MA.IsMutable())
83+
end
84+
function div_multiple(f::APL, g::APL, mf::MA.MutableTrait=MA.IsNotMutable())
5985
lt = leadingterm(g)
60-
rf = MA.copy_if_mutable(f)
86+
if nterms(g) == 1
87+
return div_multiple(f, lt, mf)
88+
end
89+
rf = _copy(f, mf)
6190
rg = removeleadingterm(g)
6291
q = zero(rf)
6392
while !iszero(rf)
6493
ltf = leadingterm(rf)
6594
if !divides(lt, ltf)
6695
# In floating point arithmetics, it may happen
6796
# that `rf` is not zero even if it cannot be reduced further.
68-
# As `_div` assumes that `g` divides `f`, we know that
97+
# As `div_multiple` assumes that `g` divides `f`, we know that
6998
# `rf` is approximately zero anyway.
7099
break
71100
end
72-
qt = _div(ltf, lt)
101+
qt = div_multiple(ltf, lt)
73102
q = MA.add!!(q, qt)
74103
rf = MA.operate!!(removeleadingterm, rf)
75104
rf = MA.operate!!(MA.sub_mul, rf, qt, rg)
@@ -98,7 +127,7 @@ function _pseudo_divrem(::UFD, f::APL, g::APL, algo)
98127
else
99128
st = constantterm(coefficient(ltg), f)
100129
new_f = st * removeleadingterm(f)
101-
qt = term(coefficient(ltf), _div(monomial(ltf), monomial(ltg)))
130+
qt = term(coefficient(ltf), div_multiple(monomial(ltf), monomial(ltg)))
102131
new_g = qt * rg
103132
# Check with `::` that we don't have any type unstability on this variable.
104133
return convert(typeof(f), st), convert(typeof(f), qt), (new_f - new_g)::typeof(f)
@@ -136,11 +165,11 @@ end
136165

137166
function _prepare_s_poly!(::typeof(pseudo_rem), f, ltf, ltg)
138167
MA.operate!(right_constant_mult, f, coefficient(ltg))
139-
return term(coefficient(ltf), _div(monomial(ltf), monomial(ltg)))
168+
return term(coefficient(ltf), div_multiple(monomial(ltf), monomial(ltg)))
140169
end
141170

142171
function _prepare_s_poly!(::typeof(rem), ::APL, ltf, ltg)
143-
return _div(ltf, ltg)
172+
return div_multiple(ltf, ltg)
144173
end
145174

146175
function MA.operate!(op::Union{typeof(rem), typeof(pseudo_rem)}, f::APL, g::APL, algo)
@@ -255,7 +284,7 @@ function Base.divrem(f::APL{T}, g::APL{S}; kwargs...) where {T, S}
255284
if isapproxzero(ltf; kwargs...)
256285
rf = MA.operate!!(removeleadingterm, rf)
257286
elseif divides(lm, ltf)
258-
qt = _div(ltf, lt)
287+
qt = div_multiple(ltf, lt)
259288
q = MA.add!!(q, qt)
260289
rf = MA.operate!!(removeleadingterm, rf)
261290
rf = MA.operate!!(MA.sub_mul, rf, qt, rg)
@@ -291,7 +320,7 @@ function Base.divrem(f::APL{T}, g::AbstractVector{<:APL{S}}; kwargs...) where {T
291320
divisionoccured = false
292321
for i in useful
293322
if divides(lm[i], ltf)
294-
qt = _div(ltf, lt[i])
323+
qt = div_multiple(ltf, lt[i])
295324
q[i] = MA.add!!(q[i], qt)
296325
rf = MA.operate!!(removeleadingterm, rf)
297326
rf = MA.operate!!(MA.sub_mul, rf, qt, rg[i])

src/gcd.jl

+6-3
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ function flatten_variable!(::Type{TT}, poly::APL) where {TT<:AbstractTerm}
390390
push!(ts, _t * m)
391391
end
392392
end
393-
return polynomial!(ts)
393+
return polynomial!(ts, UniqState())
394394
end
395395

396396
_polynomial(ts, state, ::MA.IsNotMutable) = polynomial(ts, state)
@@ -521,7 +521,10 @@ function univariate_gcd(::UFD, p1::APL, p2::APL, algo::AbstractUnivariateGCDAlgo
521521
pp = primitive_univariate_gcd!(f1, f2, algo)
522522
gg = _gcd(g1, g2, algo, MA.IsMutable(), MA.IsMutable())#::MA.promote_operation(gcd, typeof(g1), typeof(g2))
523523
# Multiply each coefficient by the gcd of the contents.
524-
return mapcoefficients!(Base.Fix1(*, gg), pp, nonzero = true)
524+
if !isone(gg)
525+
MA.operate!(right_constant_mult, pp, gg)
526+
end
527+
return pp
525528
end
526529

527530
function univariate_gcd(::Field, p1::APL, p2::APL, algo::AbstractUnivariateGCDAlgorithm, m1::MA.MutableTrait, m2::MA.MutableTrait)
@@ -652,5 +655,5 @@ Addison-Wesley Professional. Third edition.
652655
"""
653656
function primitive_part_content(p, algo::AbstractUnivariateGCDAlgorithm, mutability::MA.MutableTrait)
654657
g = content(p, algo, MA.IsNotMutable())
655-
return mapcoefficients(Base.Fix2(_div, g), p, mutability; nonzero = true), g
658+
return right_constant_div_multiple(p, g, mutability), g
656659
end

src/monomial.jl

+4-1
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,16 @@ If ``m_1 = \\prod x^{\\alpha_i}`` and ``m_2 = \\prod x^{\\beta_i}`` then it retu
130130
131131
### Examples
132132
133-
The multiplication `m1 * m2` is equivalent to `mapexponents(+, m1, m2)`, the unsafe division `_div(m1, m2)` is equivalent to `mapexponents(-, m1, m2)`, `gcd(m1, m2)` is equivalent to `mapexponents(min, m1, m2)`, `lcm(m1, m2)` is equivalent to `mapexponents(max, m1, m2)`.
133+
The multiplication `m1 * m2` is equivalent to `mapexponents(+, m1, m2)`, the unsafe division `div_multiple(m1, m2)` is equivalent to `mapexponents(-, m1, m2)`, `gcd(m1, m2)` is equivalent to `mapexponents(min, m1, m2)`, `lcm(m1, m2)` is equivalent to `mapexponents(max, m1, m2)`.
134134
"""
135135
mapexponents(f, m1::AbstractMonomialLike, m2::AbstractMonomialLike) = mapexponents(f, monomial(m1), monomial(m2))
136136

137137
function mapexponents_to! end
138138
function mapexponents! end
139139

140+
mapexponents(f, a, b, ::MA.IsMutable) = mapexponents!(f, a, b)
141+
mapexponents(f, a, b, ::MA.IsNotMutable) = mapexponents(f, a, b)
142+
140143
Base.one(::Type{TT}) where {TT<:AbstractMonomialLike} = constantmonomial(TT)
141144
Base.one(t::AbstractMonomialLike) = constantmonomial(t)
142145
MA.promote_operation(::typeof(one), MT::Type{<:AbstractMonomialLike}) = monomialtype(MT)

src/operators.jl

+3-1
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ Base.:*(m::AbstractMonomialLike, t::AbstractTermLike) = term(coefficient(t), m *
271271
Base.:*(t::AbstractTermLike, m::AbstractMonomialLike) = term(coefficient(t), monomial(t) * m)
272272
Base.:*(t1::AbstractTermLike, t2::AbstractTermLike) = term(coefficient(t1) * coefficient(t2), monomial(t1) * monomial(t2))
273273

274+
MA.operate!(::typeof(*), p::APL, t::AbstractMonomialLike) = mapexponents!(+, p, t)
275+
Base.:*(p::APL, t::AbstractMonomialLike) = mapexponents(+, p, t)
274276
Base.:*(t::AbstractTermLike, p::APL) = polynomial!(map(te -> t * te, terms(p)))
275277
Base.:*(p::APL, t::AbstractTermLike) = polynomial!(map(te -> te * t, terms(p)))
276278
Base.:*(p::APL, q::APL) = polynomial(p) * polynomial(q)
@@ -361,7 +363,7 @@ function MA.buffered_operate_to!(buffer::AbstractPolynomial, output::AbstractPol
361363
product = MA.operate_to!!(buffer, *, y, z, args...)
362364
return MA.operate_to!(output, MA.add_sub_op(op), x, product)
363365
end
364-
function MA.buffered_operate!(buffer::AbstractPolynomial, op::MA.AddSubMul, x::AbstractPolynomial, y, z, args::Vararg{Any, N}) where N
366+
function MA.buffered_operate!(buffer, op::MA.AddSubMul, x::AbstractPolynomial, y, z, args::Vararg{Any, N}) where N
365367
product = MA.operate_to!!(buffer, *, y, z, args...)
366368
return MA.operate!(MA.add_sub_op(op), x, product)
367369
end

src/term.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ function coefficient(f::APL, m::AbstractMonomialLike, vars)
102102
end
103103
match || continue
104104

105-
coeff += term(coefficient(t), _div(monomial(t), m))
105+
coeff += term(coefficient(t), div_multiple(monomial(t), m))
106106
end
107107
coeff
108108
end

test/monomial.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ end
8181

8282
@test monic(x^2) == x^2
8383

84-
@test MP._div(2x^2*y[1]^3, x*y[1]^2) == 2x*y[1]
84+
@test MP.div_multiple(2x^2*y[1]^3, x*y[1]^2) == 2x*y[1]
8585

8686
@test transpose(x) == x
8787
@test adjoint(x) == x

0 commit comments

Comments
 (0)