Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make compose do what it says #1606

Merged
merged 2 commits into from
Feb 13, 2024
Merged
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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "AbstractAlgebra"
uuid = "c3fe647b-3220-5bb0-a1ea-a7954cac585d"
version = "0.38.1"
version = "0.39.0"

[deps]
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
Expand Down
2 changes: 1 addition & 1 deletion docs/src/polynomial.md
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,7 @@ julia> k = evaluate(f, 3)
julia> m = evaluate(f, x^2 + 2x + 1)
x^5 + 4*x^4 + 7*x^3 + 7*x^2 + 4*x + 4

julia> n = compose(f, g)
julia> n = compose(f, g; inner = :second)
(x^3 + 2*x^2 + x)*y^2 + (2*x^5 + 2*x^4 + 4*x^3 + 9*x^2 + 6*x + 1)*y + x^7 + 4*x^5 + 5*x^4 + 5*x^3 + 10*x^2 + 8*x + 5

julia> p = subst(f, M)
Expand Down
26 changes: 21 additions & 5 deletions src/AbsSeries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -764,13 +764,29 @@
###############################################################################

@doc raw"""
compose(a::AbsPowerSeriesRingElem, b::AbsPowerSeriesRingElem)
compose(f::AbsPowerSeriesRingElem, g::AbsPowerSeriesRingElem; inner)

Compose the series $a$ with the series $b$ and return the result,
i.e. return $a\circ b$. The two series do not need to be in the same ring,
however the series $b$ must have positive valuation or an exception is raised.
Compose the series $a$ with the series $b$ and return the result.
- If `inner = :second`, then `f(g)` is returned and `g` must have positive valuation.
- If `inner = :first`, then `g(f)` is returned and `f` must have positive valuation.
"""
function compose(a::AbsPowerSeriesRingElem, b::AbsPowerSeriesRingElem)
function compose(f::AbsPowerSeriesRingElem, g::AbsPowerSeriesRingElem; inner = nothing)
if inner === nothing
error("""compose(f, g) requires a keyword argument inner

Check warning on line 775 in src/AbsSeries.jl

View check run for this annotation

Codecov / codecov/patch

src/AbsSeries.jl#L775

Added line #L775 was not covered by tests
- inner = :second yields f(g)
- inner = :first yields g(f)
Alternatively, use directly f(g) or g(f)
""")
end

if inner == :second
return _compose_right(f, g)
else
return _compose_right(g, f)

Check warning on line 785 in src/AbsSeries.jl

View check run for this annotation

Codecov / codecov/patch

src/AbsSeries.jl#L785

Added line #L785 was not covered by tests
end
end

function _compose_right(a::AbsPowerSeriesRingElem, b::AbsPowerSeriesRingElem)
valuation(b) == 0 && error("Series being substituted must have positive valuation")
i = length(a)
R = base_ring(a)
Expand Down
29 changes: 24 additions & 5 deletions src/Poly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2201,12 +2201,31 @@
end

@doc raw"""
compose(a::PolyRingElem, b::PolyRingElem)
compose(f::PolyRingElem, g::PolyRingElem; inner)

Compose the polynomial $a$ with the polynomial $b$ and return the result.
- If `inner = :right`, then `f(g)` is returned.
- If `inner = :left`, then `g(f)` is returned.
"""
function compose(f::PolyRingElem, g::PolyRingElem; inner = nothing)
if inner === nothing
error("""compose(f, g) requires a keyword argument inner

Check warning on line 2212 in src/Poly.jl

View check run for this annotation

Codecov / codecov/patch

src/Poly.jl#L2212

Added line #L2212 was not covered by tests
- inner = :second yields f(g)
- inner = :first yields g(f)
Alternatively, use directly f(g) or g(f)
""")
end

if inner == :second
return _compose_right(f, g)
elseif inner == :first
return _compose_right(g, f)
else
error("Keyword inner must be :first or :second")

Check warning on line 2224 in src/Poly.jl

View check run for this annotation

Codecov / codecov/patch

src/Poly.jl#L2224

Added line #L2224 was not covered by tests
end
end

Compose the polynomial $a$ with the polynomial $b$ and return the result,
i.e. return $a\circ b$.
"""
function compose(a::PolyRingElem, b::PolyRingElem)
function _compose_right(a::PolyRingElem, b::PolyRingElem)
i = length(a)
R = base_ring(a)
S = parent(b)
Expand Down
26 changes: 21 additions & 5 deletions src/RelSeries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -991,13 +991,29 @@
###############################################################################

@doc raw"""
compose(a::RelPowerSeriesRingElem, b::RelPowerSeriesRingElem)
compose(f::RelPowerSeriesRingElem, g::RelPowerSeriesRingElem; inner)

Compose the series $a$ with the series $b$ and return the result,
i.e. return $a\circ b$. The two series do not need to be in the same ring,
however the series $b$ must have positive valuation or an exception is raised.
Compose the series $a$ with the series $b$ and return the result.
- If `inner = :second`, then `f(g)` is returned and `g` must have positive valuation.
- If `inner = :first`, then `g(f)` is returned and `f` must have positive valuation.
"""
function compose(a::RelPowerSeriesRingElem, b::RelPowerSeriesRingElem)
function compose(f::RelPowerSeriesRingElem, g::RelPowerSeriesRingElem; inner = nothing)
if inner === nothing
error("""compose(f, g) requires a keyword argument inner

Check warning on line 1002 in src/RelSeries.jl

View check run for this annotation

Codecov / codecov/patch

src/RelSeries.jl#L1002

Added line #L1002 was not covered by tests
- inner = :second yields f(g)
- inner = :first yields g(f)
Alternatively, use directly f(g) or g(f)
""")
end

if inner == :second
return _compose_right(f, g)
else
return _compose_right(g, f)

Check warning on line 1012 in src/RelSeries.jl

View check run for this annotation

Codecov / codecov/patch

src/RelSeries.jl#L1012

Added line #L1012 was not covered by tests
end
end

function _compose_right(a::RelPowerSeriesRingElem, b::RelPowerSeriesRingElem)
valuation(b) == 0 && error("Series being substituted must have positive valuation")
i = pol_length(a)
R = base_ring(a)
Expand Down
2 changes: 1 addition & 1 deletion src/polysubst.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ for T in subtypes(PolyRingElem)
if parent(f) != parent(a)
return subst(f, a)
end
return compose(f, a)
return compose(f, a; inner = :second)
end

(f::T)(a::Integer) = evaluate(f, a)
Expand Down
36 changes: 18 additions & 18 deletions test/generic/AbsSeries-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -783,10 +783,10 @@ end

g = rand(R, 1:10, -10:10)

@test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g)
@test compose(x, g) == g
@test compose(x^2, g) == g^2
@test compose(R(), g) == R()
@test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second)
@test compose(x, g; inner = :second) == g
@test compose(x^2, g; inner = :second) == g^2
@test compose(R(), g; inner = :second) == R()
end

S, y = power_series_ring(ZZ, 10, "y", model=:capped_absolute)
Expand All @@ -796,8 +796,8 @@ end

g = rand(S, 1:10, -10:10)

@test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g)
@test compose(R(), g) == S()
@test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second)
@test compose(R(), g; inner = :second) == S()
end

# Inexact field
Expand All @@ -808,10 +808,10 @@ end

g = rand(R, 1:10, -10:10)

@test isapprox(compose(f1 + f2, g), compose(f1, g) + compose(f2, g))
@test isapprox(compose(x, g), g)
@test isapprox(compose(x^2, g), g^2)
@test isapprox(compose(R(), g), R())
@test isapprox(compose(f1 + f2, g; inner = :second), compose(f1, g; inner = :second) + compose(f2, g; inner = :second))
@test isapprox(compose(x, g; inner = :second), g)
@test isapprox(compose(x^2, g; inner = :second), g^2)
@test isapprox(compose(R(), g; inner = :second), R())
end

S, y = power_series_ring(RealField, 10, "y", model=:capped_absolute)
Expand All @@ -820,8 +820,8 @@ end
f2 = rand(R, 0:10, -10:10)

g = rand(S, 1:10, -10:10)
@test isapprox(compose(f1 + f2, g), compose(f1, g) + compose(f2, g))
@test isapprox(compose(R(), g), S())
@test isapprox(compose(f1 + f2, g; inner = :second), compose(f1, g; inner = :second) + compose(f2, g; inner = :second))
@test isapprox(compose(R(), g; inner = :second), S())
end

# Non-integral domain
Expand All @@ -833,10 +833,10 @@ end

g = rand(R, 1:10, -10:10)

@test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g)
@test compose(x, g) == g
@test compose(x^2, g) == g^2
@test compose(R(), g) == R()
@test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second)
@test compose(x, g; inner = :second) == g
@test compose(x^2, g; inner = :second) == g^2
@test compose(R(), g; inner = :second) == R()
end

S, y = power_series_ring(T, 10, "y", model=:capped_absolute)
Expand All @@ -846,8 +846,8 @@ end

g = rand(S, 1:10, -10:10)

@test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g)
@test compose(R(), g) == S()
@test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second)
@test compose(R(), g; inner = :second) == S()
end
end

Expand Down
8 changes: 5 additions & 3 deletions test/generic/Poly-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1847,7 +1847,9 @@ end
g = rand(R, -1:5, -10:10)
h = rand(R, -1:5, -10:10)

@test compose(f, compose(g, h)) == compose(compose(f, g), h)
@test compose(f, compose(g, h; inner = :second); inner = :second) == compose(compose(f, g; inner = :second), h; inner = :second)
@test compose(f, g; inner = :first) == g(f)
@test compose(f, g; inner = :second) == f(g)
end

# Inexact field
Expand All @@ -1858,7 +1860,7 @@ end
g = rand(R, -1:5, 0:1)
h = rand(R, -1:5, 0:1)

@test isapprox(compose(f, compose(g, h)), compose(compose(f, g), h))
@test isapprox(compose(f, compose(g, h; inner = :second); inner = :second), compose(compose(f, g; inner = :second), h; inner = :second))
end

# Non-integral domain
Expand All @@ -1870,7 +1872,7 @@ end
g = rand(R, -1:5, 0:5)
h = rand(R, -1:5, 0:5)

@test compose(f, compose(g, h)) == compose(compose(f, g), h)
@test compose(f, compose(g, h; inner = :second); inner = :second) == compose(compose(f, g; inner = :second), h; inner = :second)
end
end

Expand Down
36 changes: 18 additions & 18 deletions test/generic/RelSeries-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -806,10 +806,10 @@ end

g = rand(R, 1:10, -10:10)

@test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g)
@test compose(x, g) == g
@test compose(x^2, g) == g^2
@test compose(R(), g) == R()
@test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second)
@test compose(x, g; inner = :second) == g
@test compose(x^2, g; inner = :second) == g^2
@test compose(R(), g; inner = :second) == R()
end

S, y = power_series_ring(ZZ, 10, "y")
Expand All @@ -819,8 +819,8 @@ end

g = rand(S, 1:10, -10:10)

@test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g)
@test compose(R(), g) == S()
@test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second)
@test compose(R(), g; inner = :second) == S()
end

# Inexact field
Expand All @@ -831,10 +831,10 @@ end

g = rand(R, 1:10, -10:10)

@test isapprox(compose(f1 + f2, g), compose(f1, g) + compose(f2, g))
@test isapprox(compose(x, g), g)
@test isapprox(compose(x^2, g), g^2)
@test isapprox(compose(R(), g), R())
@test isapprox(compose(f1 + f2, g; inner = :second), compose(f1, g; inner = :second) + compose(f2, g; inner = :second))
@test isapprox(compose(x, g; inner = :second), g)
@test isapprox(compose(x^2, g; inner = :second), g^2)
@test isapprox(compose(R(), g; inner = :second), R())
end

S, y = power_series_ring(RealField, 10, "y")
Expand All @@ -843,8 +843,8 @@ end
f2 = rand(R, 0:10, -10:10)

g = rand(S, 1:10, -10:10)
@test isapprox(compose(f1 + f2, g), compose(f1, g) + compose(f2, g))
@test isapprox(compose(R(), g), S())
@test isapprox(compose(f1 + f2, g; inner = :second), compose(f1, g; inner = :second) + compose(f2, g; inner = :second))
@test isapprox(compose(R(), g; inner = :second), S())
end

# Non-integral domain
Expand All @@ -856,10 +856,10 @@ end

g = rand(R, 1:10, -10:10)

@test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g)
@test compose(x, g) == g
@test compose(x^2, g) == g^2
@test compose(R(), g) == R()
@test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second)
@test compose(x, g; inner = :second) == g
@test compose(x^2, g; inner = :second) == g^2
@test compose(R(), g; inner = :second) == R()
end

S, y = power_series_ring(T, 10, "y")
Expand All @@ -869,8 +869,8 @@ end

g = rand(S, 1:10, -10:10)

@test compose(f1 + f2, g) == compose(f1, g) + compose(f2, g)
@test compose(R(), g) == S()
@test compose(f1 + f2, g; inner = :second) == compose(f1, g; inner = :second) + compose(f2, g; inner = :second)
@test compose(R(), g; inner = :second) == S()
end
end

Expand Down
Loading