Skip to content

Commit d27239d

Browse files
bmxammbouyges
andauthored
Antidifferentiation (primitive) (#230)
* prep PR * blegat suggestions * using Rational{Int} to preserve type * fix doc --------- Co-authored-by: mbouyges <[email protected]>
1 parent 044bbae commit d27239d

File tree

3 files changed

+87
-0
lines changed

3 files changed

+87
-0
lines changed

docs/src/differentiation.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,12 @@ We can also differentiate it by both of its variable and get the vector ``[6xy+1
66
```@docs
77
differentiate
88
```
9+
10+
# Antidifferentiation
11+
12+
Given a polynomial, say `p(x, y) = 3x^2y + x + 2y + 1`, we can antidifferentiate it by a variable, say `x` and get ``\int_0^x p(X, y)\mathrm{d}X = x^3y + 1/2x^2 + 2xy + x``.
13+
We can also antidifferentiate it by both of its variable and get the vector `[x^3y + 1/2x^2 + 2xy + x, 3/2x^2y^2 + xy + y^2 + y]`.
14+
15+
```@docs
16+
antidifferentiate
17+
```

src/MultivariatePolynomials.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ include("comparison.jl")
8585

8686
include("substitution.jl")
8787
include("differentiation.jl")
88+
include("antidifferentiation.jl")
8889
include("division.jl")
8990
include("gcd.jl")
9091
include("det.jl")

src/antidifferentiation.jl

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
export antidifferentiate
2+
3+
"""
4+
antidifferentiate(p::AbstractPolynomialLike, v::AbstractVariable, deg::Union{Int, Val}=1)
5+
6+
Antidifferentiate `deg` times the polynomial `p` by the variable `v`. The free constant involved by
7+
the antidifferentiation is set to 0.
8+
9+
10+
antidifferentiate(p::AbstractPolynomialLike, vs, deg::Union{Int, Val}=1)
11+
12+
Antidifferentiate `deg` times the polynomial `p` by the variables of the vector or
13+
tuple of variable `vs` and return an array of dimension `deg`. It is recommended
14+
to pass `deg` as a `Val` instance when the degree is known at compile time, e.g.
15+
`antidifferentiate(p, v, Val{2}())` instead of `antidifferentiate(p, x, 2)`, as this
16+
will help the compiler infer the return type.
17+
18+
### Examples
19+
20+
```julia
21+
p = 3x^2*y + x + 2y + 1
22+
antidifferentiate(p, x) # should return 3x^3* + 1/2*x + 2xy + x
23+
antidifferentiate(p, x, Val{1}()) # equivalent to the above
24+
antidifferentiate(p, (x, y)) # should return [3x^3* + 1/2*x + 2xy + x, 3/2x^2*y^2 + xy + y^2 + y]
25+
```
26+
"""
27+
function antidifferentiate end
28+
29+
# Fallback for everything else
30+
antidifferentiate::T, v::AbstractVariable) where {T} = α * v
31+
antidifferentiate(v1::AbstractVariable, v2::AbstractVariable) = v1 == v2 ? 1 // 2 * v1 * v2 : v1 * v2
32+
antidifferentiate(t::AbstractTermLike, v::AbstractVariable) = coefficient(t) * antidifferentiate(monomial(t), v)
33+
antidifferentiate(p::APL, v::AbstractVariable) = polynomial!(antidifferentiate.(terms(p), v), SortedState())
34+
35+
# TODO: this signature is probably too wide and creates the potential
36+
# for stack overflows
37+
antidifferentiate(p::APL, xs) = [antidifferentiate(p, x) for x in xs]
38+
39+
# antidifferentiate(p, [x, y]) with TypedPolynomials promote x to a Monomial
40+
antidifferentiate(p::APL, m::AbstractMonomial) = antidifferentiate(p, variable(m))
41+
42+
# The `R` argument indicates a desired result type. We use this in order
43+
# to attempt to preserve type-stability even though the value of `deg` cannot
44+
# be known at compile time. For scalar `p` and `x`, we set R to be the type
45+
# of antidifferentiate(p, x) to give a stable result type regardless of `deg`. For
46+
# vectors p and/or x this is impossible (since antidifferentiate may return an array),
47+
# so we just set `R` to `Any`
48+
function (_antidifferentiate_recursive(p, x, deg::Int, ::Type{R})::R) where {R}
49+
if deg < 0
50+
throw(DomainError(deg, "degree is negative"))
51+
elseif deg == 0
52+
return p
53+
else
54+
return antidifferentiate(antidifferentiate(p, x), x, deg - 1)
55+
end
56+
end
57+
58+
antidifferentiate(p, x, deg::Int) = _antidifferentiate_recursive(p, x, deg, Base.promote_op(antidifferentiate, typeof(p), typeof(x)))
59+
antidifferentiate(p::AbstractArray, x, deg::Int) = _antidifferentiate_recursive(p, x, deg, Any)
60+
antidifferentiate(p, x::Union{AbstractArray,Tuple}, deg::Int) = _antidifferentiate_recursive(p, x, deg, Any)
61+
antidifferentiate(p::AbstractArray, x::Union{AbstractArray,Tuple}, deg::Int) = _antidifferentiate_recursive(p, x, deg, Any)
62+
63+
64+
# This is alternative, Val-based interface for nested antidifferentiation.
65+
# It has the advantage of not requiring an conversion or calls to
66+
# Base.promote_op, while maintaining type stability for any argument
67+
# type.
68+
antidifferentiate(p, x, ::Val{0}) = p
69+
antidifferentiate(p, x, ::Val{1}) = antidifferentiate(p, x)
70+
71+
function antidifferentiate(p, x, deg::Val{N}) where {N}
72+
if N < 0
73+
throw(DomainError(deg))
74+
else
75+
antidifferentiate(antidifferentiate(p, x), x, Val{N - 1}())
76+
end
77+
end

0 commit comments

Comments
 (0)