Line | Exclusive | Inclusive | Code |
---|---|---|---|
1 | Base.one(::Type{P}) where {C, T, P <: Polynomial{C, T}} = Polynomial(one(T)) | ||
2 | Base.one(p::Polynomial) = one(typeof(p)) | ||
3 | |||
4 | Base.zero(::Type{Polynomial{C, T, A}}) where {C, T, A} = Polynomial(A()) | ||
5 | Base.zero(t::PolynomialLike) = zero(typeof(t)) | ||
6 | |||
7 | combine(t1::Term, t2::Term) = combine(promote(t1, t2)...) | ||
8 | combine(t1::T, t2::T) where {T <: Term} = Term(t1.coefficient + t2.coefficient, t1.monomial) | ||
9 | compare(t1::Term, t2::Term) = monomial(t1) > monomial(t2) | ||
10 | |||
11 | # Graded Lexicographic order | ||
12 | # First compare total degree, then lexicographic order | ||
13 | function isless(m1::Monomial{V}, m2::Monomial{V}) where {V} | ||
14 | 14 (2 %) |
14 (100 %)
samples spent calling
degree
d1 = degree(m1)
|
|
15 | 123 (15 %) |
123 (100 %)
samples spent calling
degree
d2 = degree(m2)
|
|
16 | 53 (6 %) |
53 (100 %)
samples spent calling
<
if d1 < d2
|
|
17 | return true | ||
18 | 9 (1 %) |
9 (100 %)
samples spent calling
>
elseif d1 > d2
|
|
19 | return false | ||
20 | else | ||
21 | 10 (1 %) | return exponents(m1) < exponents(m2) | |
22 | end | ||
23 | end | ||
24 | function grlex(m1::Monomial{V}, m2::Monomial{V}) where {V} | ||
25 | d1 = degree(m1) | ||
26 | d2 = degree(m2) | ||
27 | if d1 != d2 | ||
28 | return d1 - d2 | ||
29 | else | ||
30 | if exponents(m1) == exponents(m2) | ||
31 | return 0 | ||
32 | elseif exponents(m1) < exponents(m2) | ||
33 | return -1 | ||
34 | else | ||
35 | return 1 | ||
36 | end | ||
37 | end | ||
38 | end | ||
39 | grlex(m1::Monomial, m2::Monomial) = grlex(promote(m1, m2)...) | ||
40 | |||
41 | jointerms(terms1::AbstractArray{<:Term}, terms2::AbstractArray{<:Term}) = mergesorted(terms1, terms2, compare, combine) | ||
42 | function jointerms!(output::AbstractArray{<:Term}, terms1::AbstractArray{<:Term}, terms2::AbstractArray{<:Term}) | ||
43 | resize!(output, length(terms1) + length(terms2)) | ||
44 | Sequences.mergesorted!(output, terms1, terms2, compare, combine) | ||
45 | end | ||
46 | |||
47 | (+)(p1::Polynomial, p2::Polynomial) = Polynomial(jointerms(terms(p1), terms(p2))) | ||
48 | (+)(p1::Polynomial, p2::Polynomial{<:UniformScaling}) = p1 + MP.mapcoefficientsnz(J -> J.λ, p2) | ||
49 | (+)(p1::Polynomial{<:UniformScaling}, p2::Polynomial) = MP.mapcoefficientsnz(J -> J.λ, p1) + p2 | ||
50 | (+)(p1::Polynomial{<:UniformScaling}, p2::Polynomial{<:UniformScaling}) = MP.mapcoefficientsnz(J -> J.λ, p1) + p2 | ||
51 | function MA.mutable_operate_to!(result::Polynomial, ::typeof(+), p1::Polynomial, p2::Polynomial) | ||
52 | if result === p1 || result === p2 | ||
53 | error("Cannot call `mutable_operate_to!(output, +, p, q)` with `output` equal to `p` or `q`, call `mutable_operate!` instead.") | ||
54 | end | ||
55 | jointerms!(result.terms, terms(p1), terms(p2)) | ||
56 | result | ||
57 | end | ||
58 | function MA.mutable_operate_to!(result::Polynomial, ::typeof(*), p::Polynomial, t::AbstractTermLike) | ||
59 | if iszero(t) | ||
60 | MA.mutable_operate!(zero, result) | ||
61 | else | ||
62 | resize!(result.terms, nterms(p)) | ||
63 | for i in eachindex(p.terms) | ||
64 | # TODO could use MA.mul_to! for indices that were presents in `result` before the `resize!`. | ||
65 | result.terms[i] = p.terms[i] * t | ||
66 | end | ||
67 | return result | ||
68 | end | ||
69 | end | ||
70 | function MA.mutable_operate_to!(result::Polynomial, ::typeof(*), t::AbstractTermLike, p::Polynomial) | ||
71 | if iszero(t) | ||
72 | MA.mutable_operate!(zero, result) | ||
73 | else | ||
74 | resize!(result.terms, nterms(p)) | ||
75 | for i in eachindex(p.terms) | ||
76 | # TODO could use MA.mul_to! for indices that were presents in `result` before the `resize!`. | ||
77 | result.terms[i] = t * p.terms[i] | ||
78 | end | ||
79 | return result | ||
80 | end | ||
81 | end | ||
82 | (-)(p1::Polynomial, p2::Polynomial) = Polynomial(jointerms(terms(p1), (-).(terms(p2)))) | ||
83 | (-)(p1::Polynomial, p2::Polynomial{<:UniformScaling}) = p1 - MP.mapcoefficientsnz(J -> J.λ, p2) | ||
84 | (-)(p1::Polynomial{<:UniformScaling}, p2::Polynomial) = MP.mapcoefficientsnz(J -> J.λ, p1) - p2 | ||
85 | (-)(p1::Polynomial{<:UniformScaling}, p2::Polynomial{<:UniformScaling}) = MP.mapcoefficientsnz(J -> J.λ, p1) - p2 | ||
86 | |||
87 | function MA.mutable_operate!(op::Union{typeof(+), typeof(-)}, p::Polynomial{T}, q::Polynomial) where T | ||
88 | get1(i) = p.terms[i] | ||
89 | function get2(i) | ||
90 | t = q.terms[i] | ||
91 | Term(MA.scaling_convert(T, MA.operate(op, t.coefficient)), t.monomial) | ||
92 | end | ||
93 | set(i, t::Term) = p.terms[i] = t | ||
94 | push(t::Term) = push!(p.terms, t) | ||
95 | compare_monomials(t::Term, j::Int) = grlex(q.terms[j].monomial, t.monomial) | ||
96 | compare_monomials(i::Int, j::Int) = compare_monomials(get1(i), j) | ||
97 | combine(i::Int, j::Int) = p.terms[i] = Term(MA.operate!(op, p.terms[i].coefficient, q.terms[j].coefficient), p.terms[i].monomial) | ||
98 | combine(t::Term, j::Int) = Term(MA.operate!(op, t.coefficient, q.terms[j].coefficient), t.monomial) | ||
99 | resize(n) = resize!(p.terms, n) | ||
100 | # We can modify the coefficient since it's the result of `combine`. | ||
101 | keep(t::Term) = !MA.iszero!(t.coefficient) | ||
102 | keep(i::Int) = !MA.iszero!(p.terms[i].coefficient) | ||
103 | MP.polynomial_merge!( | ||
104 | nterms(p), nterms(q), get1, get2, set, push, | ||
105 | compare_monomials, combine, keep, resize | ||
106 | ) | ||
107 | return p | ||
108 | end | ||
109 | |||
110 | (==)(::Variable{N}, ::Variable{N}) where {N} = true | ||
111 | (==)(::Variable, ::Variable) = false | ||
112 | 57 (7 %) |
57 (7 %)
samples spent in ==
1 (2 %) (incl.) when called from uniqterms! line 108 56 (98 %) (incl.) when called from isless line 9
57 (100 %)
samples spent calling
==
(==)(m1::Monomial{V}, m2::Monomial{V}) where {V} = exponents(m1) == exponents(m2)
|
|
113 | |||
114 | # Multiplication is handled as a special case so that we can write these | ||
115 | # definitions without resorting to promotion: | ||
116 | MP.multconstant(α, v::Monomial) = Term(α, v) | ||
117 | MP.multconstant(α, v::Variable) = Term(α, Monomial(v)) | ||
118 | |||
119 | (*)(v1::V, v2::V) where {V <: Variable} = Monomial{(V(),), 1}((2,)) | ||
120 | (*)(v1::Variable, v2::Variable) = (*)(promote(v1, v2)...) | ||
121 | |||
122 | function MP.divides(m1::Monomial{V, N}, m2::Monomial{V, N}) where {V, N} | ||
123 | reduce((d, exp) -> d && (exp[1] <= exp[2]), zip(m1.exponents, m2.exponents), init=true) | ||
124 | end | ||
125 | MP.divides(m1::Monomial, m2::Monomial) = divides(promote(m1, m2)...) | ||
126 | function MP.mapexponents(op, m1::M, m2::M) where M<:Monomial | ||
127 | 1 (0 %) |
1 (100 %)
samples spent calling
map
M(map(op, m1.exponents, m2.exponents))
|
|
128 | end | ||
129 | MP.mapexponents(op, m1::Monomial, m2::Monomial) = mapexponents(op, promote(m1, m2)...) | ||
130 | #function MP.mapexponents_to!(output::M, op, m1::M, m2::M) where M<:Monomial | ||
131 | # map!(op, output.exponents, m1.exponents, m2.exponents) | ||
132 | # return output | ||
133 | #end | ||
134 | #function MP.mapexponents!(op, m1::M, m2::M) where M<:Monomial | ||
135 | # map!(op, m1.exponents, m1.exponents, m2.exponents) | ||
136 | # return m1 | ||
137 | #end | ||
138 | |||
139 | function MA.mutable_operate_to!(output::Polynomial, ::typeof(*), p::Polynomial, q::Polynomial) | ||
140 | empty!(output.terms) | ||
141 | MP.mul_to_terms!(output.terms, p, q) | ||
142 | sort!(output.terms, lt=(>)) | ||
143 | MP.uniqterms!(output.terms) | ||
144 | return output | ||
145 | end | ||
146 | function MA.mutable_operate!(::typeof(*), p::Polynomial, q::Polynomial) | ||
147 | return MA.mutable_operate_to!(p, *, MA.mutable_copy(p), q) | ||
148 | end | ||
149 | |||
150 | function MA.mutable_operate!(::typeof(zero), p::Polynomial) | ||
151 | empty!(p.terms) | ||
152 | return p | ||
153 | end | ||
154 | function MA.mutable_operate!(::typeof(one), p::Polynomial{T}) where T | ||
155 | if isempty(p.terms) | ||
156 | push!(p.terms, constantterm(one(T), p)) | ||
157 | else | ||
158 | t = p.terms[1] | ||
159 | p.terms[1] = Term(MA.one!(coefficient(t)), constantmonomial(t)) | ||
160 | resize!(p.terms, 1) | ||
161 | end | ||
162 | return p | ||
163 | end | ||
164 | |||
165 | # The exponents are stored in a tuple, this is not mutable. | ||
166 | # We could remove these methods since it is the default. | ||
167 | MA.mutability(::Type{<:Monomial}) = MA.NotMutable() | ||
168 | MA.mutability(::Type{<:Term}) = MA.NotMutable() | ||
169 | # The polynomials can be mutated. | ||
170 | MA.mutability(::Type{<:Polynomial}) = MA.IsMutable() | ||
171 | |||
172 | ^(v::V, x::Integer) where {V <: Variable} = Monomial{(V(),), 1}((x,)) | ||
173 | |||
174 | # dot(v1::AbstractVector{<:TermLike}, v2::AbstractVector) = dot(v1, v2) | ||
175 | # dot(v1::AbstractVector, v2::AbstractVector{<:TermLike}) = dot(v1, v2) | ||
176 | # dot(v1::AbstractVector{<:TermLike}, v2::AbstractVector{<:TermLike}) = dot(v1, v2) | ||
177 | |||
178 | # All of these types are immutable, so there's no need to copy anything to get | ||
179 | # a shallow copy. | ||
180 | Base.copy(x::TermLike) = x | ||
181 | |||
182 | # Terms are not mutable under the MutableArithmetics API | ||
183 | MA.mutable_copy(p::Polynomial) = Polynomial([Term(MA.copy_if_mutable(t.coefficient), t.monomial) for t in terms(p)]) | ||
184 | Base.copy(p::Polynomial) = MA.mutable_copy(p) | ||
185 | |||
186 | adjoint(v::Variable) = v | ||
187 | adjoint(m::Monomial) = m | ||
188 | adjoint(t::Term) = Term(adjoint(coefficient(t)), monomial(t)) | ||
189 | adjoint(x::Polynomial) = Polynomial(adjoint.(terms(x))) | ||
190 | |||
191 | function MP.mapcoefficientsnz_to!(output::Polynomial, f::Function, p::Polynomial) | ||
192 | resize!(output.terms, nterms(p)) | ||
193 | for i in eachindex(p.terms) | ||
194 | t = p.terms[i] | ||
195 | output.terms[i] = Term(f(coefficient(t)), monomial(t)) | ||
196 | end | ||
197 | return output | ||
198 | end | ||
199 | function MP.mapcoefficientsnz_to!(output::Polynomial, f::Function, p::AbstractPolynomialLike) | ||
200 | return MP.mapcoefficientsnz_to!(output, f, polynomial(p)) | ||
201 | end |