Skip to content

Commit 3952d85

Browse files
authored
Fix degree_complex (#292)
* Fix degree_complex More consistent behavior of degree_complex and halfdegree Make assertions into actual checks * Add example for degree_complex/halfdegree to docstring * Extend examples
1 parent 076b54d commit 3952d85

File tree

2 files changed

+56
-34
lines changed

2 files changed

+56
-34
lines changed

src/complex.jl

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -214,26 +214,68 @@ end
214214
# Also give complex-valued degree definitions. We choose not to overwrite degree, as this will lead to issues in monovecs
215215
# and their sorting. So now there are two ways to calculate degrees: strictly by considering all variables independently,
216216
# and also by looking at their complex structure.
217+
for fn in (:degree_complex, :halfdegree)
218+
@eval function $fn(t::AbstractTermLike)
219+
realdeg = 0
220+
cpdeg = 0
221+
conjdeg = 0
222+
for (var, exp) in powers(t)
223+
if isreal(var)
224+
realdeg += exp
225+
(isrealpart(var) || isimagpart(var)) && error(
226+
"Cannot calculate complex degrees when real or imaginary parts are present",
227+
)
228+
else
229+
if isconj(var)
230+
conjdeg += exp
231+
else
232+
cpdeg += exp
233+
end
234+
end
235+
end
236+
return $(
237+
fn === :degree_complex ? :(realdeg) : :(div(realdeg, 2, RoundUp))
238+
) + max(cpdeg, conjdeg)
239+
end
240+
end
241+
217242
"""
218243
degree_complex(t::AbstractTermLike)
219244
220245
Return the _total complex degree_ of the monomial of the term `t`, i.e., the maximum of the total degree of the declared
221246
variables in `t` and the total degree of the conjugate variables in `t`.
222247
To be well-defined, the monomial must not contain real parts or imaginary parts of variables.
248+
If `x₁` and `x₂` are real-valued variables and `z₁` and `z₂` are complex-valued,
249+
- `degree_complex(x₁^2 * x₂^3) = 5`
250+
- `degree_complex(z₁^3 * conj(z₁)^4) = max(3, 4) = 4` and `degree_complex(z₁^4 * conj(z₁)^3) = max(4, 3) = 4`
251+
- `degree_complex(z₁^3 * z₂ * conj(z₁)^2 * conj(z₂)^4) = max(4, 6) = 6` and
252+
`degree_complex(z₁^4 * z₂ * conj(z₁) * conj(z₂)^3) = max(5, 4) = 5`
253+
- `degree_complex(x₁^2 * x₂^3 * z₁^3 * z₂ * conj(z₁)^2 * conj(z₂)^4) = 5 + max(4, 6) = 11`
254+
"""
255+
degree_complex(t::AbstractTermLike)
256+
257+
"""
258+
halfdegree(t::AbstractTermLike)
259+
260+
Return the equivalent of `ceil(degree(t)/2)`` for real-valued terms or `degree_complex(t)` for terms with only complex
261+
variables; however, respect any mixing between complex and real-valued variables.
262+
To be well-defined, the monomial must not contain real parts or imaginary parts of variables.
263+
If `x₁` and `x₂` are real-valued variables and `z₁` and `z₂` are complex-valued,
264+
- `halfdegree(x₁^2 * x₂^3) = ⌈5/2⌉ = 3`
265+
- `halfdegree(z₁^3 * conj(z₁)^4) = max(3, 4) = 4` and `halfdegree(z₁^4 * conj(z₁)^3) = max(4, 3) = 4`
266+
- `halfdegree(z₁^3 * z₂ * conj(z₁)^2 * conj(z₂)^4) = max(4, 6) = 6` and
267+
`halfdegree(z₁^4 * z₂ * conj(z₁) * conj(z₂)^3) = max(5, 4) = 5`
268+
- `halfdegree(x₁^2 * x₂^3 * z₁^3 * z₂ * conj(z₁)^2 * conj(z₂)^4) = ⌈5/2⌉ + max(4, 6) = 9`
269+
"""
270+
halfdegree(t::AbstractTermLike)
223271

272+
"""
224273
degree_complex(t::AbstractTermLike, v::AbstractVariable)
225274
226275
Returns the exponent of the variable `v` or its conjugate in the monomial of the term `t`, whatever is larger.
227276
228277
See also [`isconj`](@ref).
229278
"""
230-
function degree_complex(t::AbstractTermLike)
231-
vars = variables(t)
232-
@assert(!any(isrealpart, vars) && !any(isimagpart, vars))
233-
grouping = isconj.(vars)
234-
exps = exponents(t)
235-
return max(sum(exps[grouping]), sum(exps[map(!, grouping)]))
236-
end
237279
function degree_complex(t::AbstractTermLike, var::AbstractVariable)
238280
return degree_complex(monomial(t), var)
239281
end
@@ -243,7 +285,9 @@ function degree_complex(m::AbstractMonomial, v::AbstractVariable)
243285
deg_c = 0
244286
c_v = conj(v)
245287
for (var, exp) in powers(m)
246-
@assert(!isrealpart(var) && !isimagpart(var))
288+
(isrealpart(var) || isimagpart(var)) && error(
289+
"Cannot calculate complex degrees when real or imaginary parts are present",
290+
)
247291
if var == v
248292
deg += exp
249293
elseif var == c_v
@@ -253,31 +297,6 @@ function degree_complex(m::AbstractMonomial, v::AbstractVariable)
253297
return max(deg, deg_c)
254298
end
255299

256-
"""
257-
halfdegree(t::AbstractTermLike)
258-
259-
Return the equivalent of `ceil(degree(t)/2)`` for real-valued terms or `degree_complex(t)` for terms with only complex
260-
variables; however, respect any mixing between complex and real-valued variables.
261-
"""
262-
function halfdegree(t::AbstractTermLike)
263-
realdeg = 0
264-
cpdeg = 0
265-
conjdeg = 0
266-
for (var, exp) in powers(t)
267-
if isreal(var)
268-
realdeg += exp
269-
else
270-
if isconj(var)
271-
conjdeg += exp
272-
else
273-
@assert(!isrealpart(var) && !isimagpart(var))
274-
cpdeg += exp
275-
end
276-
end
277-
end
278-
return ((realdeg + 1) >> 1) + max(cpdeg, conjdeg)
279-
end
280-
281300
"""
282301
mindegree_complex(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}})
283302

test/complex.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,11 @@
6464
@test degree_complex(x * y^2 * conj(y)^3) == 3
6565
@test degree_complex(x * y^2 * conj(y)^3, x) == 1
6666
@test degree_complex(x * y^2 * conj(y)^3, y) == 3
67+
@test degree_complex(a^5 * x * y^2 * conj(y)^4) == 9
68+
@test degree_complex(a^5 * x * y^2 * conj(y)^2) == 8
6769
@test halfdegree(x * y^2 * conj(y^3)) == 3
68-
@test halfdegree(x * a^5 * conj(y)) == 4
70+
@test halfdegree(x * a^5 * conj(y^2)) == 5
71+
@test halfdegree(x^2 * a^5 * conj(y^2)) == 5
6972
@test ordinary_variable([x, y, conj(x), a, real(x), imag(y)]) == [x, y, a]
7073

7174
@test subs(4x + 8y^2 - 6x^3, [x, y] => [2 + 4im, 9 - im]) ==

0 commit comments

Comments
 (0)