Skip to content

Commit

Permalink
Fix degree_complex (#292)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
projekter authored Apr 5, 2024
1 parent 076b54d commit 3952d85
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 34 deletions.
85 changes: 52 additions & 33 deletions src/complex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -214,26 +214,68 @@ end
# Also give complex-valued degree definitions. We choose not to overwrite degree, as this will lead to issues in monovecs
# and their sorting. So now there are two ways to calculate degrees: strictly by considering all variables independently,
# and also by looking at their complex structure.
for fn in (:degree_complex, :halfdegree)
@eval function $fn(t::AbstractTermLike)
realdeg = 0
cpdeg = 0
conjdeg = 0
for (var, exp) in powers(t)
if isreal(var)
realdeg += exp
(isrealpart(var) || isimagpart(var)) && error(
"Cannot calculate complex degrees when real or imaginary parts are present",
)
else
if isconj(var)
conjdeg += exp
else
cpdeg += exp
end
end
end
return $(
fn === :degree_complex ? :(realdeg) : :(div(realdeg, 2, RoundUp))
) + max(cpdeg, conjdeg)
end
end

"""
degree_complex(t::AbstractTermLike)
Return the _total complex degree_ of the monomial of the term `t`, i.e., the maximum of the total degree of the declared
variables in `t` and the total degree of the conjugate variables in `t`.
To be well-defined, the monomial must not contain real parts or imaginary parts of variables.
If `x₁` and `x₂` are real-valued variables and `z₁` and `z₂` are complex-valued,
- `degree_complex(x₁^2 * x₂^3) = 5`
- `degree_complex(z₁^3 * conj(z₁)^4) = max(3, 4) = 4` and `degree_complex(z₁^4 * conj(z₁)^3) = max(4, 3) = 4`
- `degree_complex(z₁^3 * z₂ * conj(z₁)^2 * conj(z₂)^4) = max(4, 6) = 6` and
`degree_complex(z₁^4 * z₂ * conj(z₁) * conj(z₂)^3) = max(5, 4) = 5`
- `degree_complex(x₁^2 * x₂^3 * z₁^3 * z₂ * conj(z₁)^2 * conj(z₂)^4) = 5 + max(4, 6) = 11`
"""
degree_complex(t::AbstractTermLike)

"""
halfdegree(t::AbstractTermLike)
Return the equivalent of `ceil(degree(t)/2)`` for real-valued terms or `degree_complex(t)` for terms with only complex
variables; however, respect any mixing between complex and real-valued variables.
To be well-defined, the monomial must not contain real parts or imaginary parts of variables.
If `x₁` and `x₂` are real-valued variables and `z₁` and `z₂` are complex-valued,
- `halfdegree(x₁^2 * x₂^3) = ⌈5/2⌉ = 3`
- `halfdegree(z₁^3 * conj(z₁)^4) = max(3, 4) = 4` and `halfdegree(z₁^4 * conj(z₁)^3) = max(4, 3) = 4`
- `halfdegree(z₁^3 * z₂ * conj(z₁)^2 * conj(z₂)^4) = max(4, 6) = 6` and
`halfdegree(z₁^4 * z₂ * conj(z₁) * conj(z₂)^3) = max(5, 4) = 5`
- `halfdegree(x₁^2 * x₂^3 * z₁^3 * z₂ * conj(z₁)^2 * conj(z₂)^4) = ⌈5/2⌉ + max(4, 6) = 9`
"""
halfdegree(t::AbstractTermLike)

"""
degree_complex(t::AbstractTermLike, v::AbstractVariable)
Returns the exponent of the variable `v` or its conjugate in the monomial of the term `t`, whatever is larger.
See also [`isconj`](@ref).
"""
function degree_complex(t::AbstractTermLike)
vars = variables(t)
@assert(!any(isrealpart, vars) && !any(isimagpart, vars))
grouping = isconj.(vars)
exps = exponents(t)
return max(sum(exps[grouping]), sum(exps[map(!, grouping)]))
end
function degree_complex(t::AbstractTermLike, var::AbstractVariable)
return degree_complex(monomial(t), var)
end
Expand All @@ -243,7 +285,9 @@ function degree_complex(m::AbstractMonomial, v::AbstractVariable)
deg_c = 0
c_v = conj(v)
for (var, exp) in powers(m)
@assert(!isrealpart(var) && !isimagpart(var))
(isrealpart(var) || isimagpart(var)) && error(
"Cannot calculate complex degrees when real or imaginary parts are present",
)
if var == v
deg += exp
elseif var == c_v
Expand All @@ -253,31 +297,6 @@ function degree_complex(m::AbstractMonomial, v::AbstractVariable)
return max(deg, deg_c)
end

"""
halfdegree(t::AbstractTermLike)
Return the equivalent of `ceil(degree(t)/2)`` for real-valued terms or `degree_complex(t)` for terms with only complex
variables; however, respect any mixing between complex and real-valued variables.
"""
function halfdegree(t::AbstractTermLike)
realdeg = 0
cpdeg = 0
conjdeg = 0
for (var, exp) in powers(t)
if isreal(var)
realdeg += exp
else
if isconj(var)
conjdeg += exp
else
@assert(!isrealpart(var) && !isimagpart(var))
cpdeg += exp
end
end
end
return ((realdeg + 1) >> 1) + max(cpdeg, conjdeg)
end

"""
mindegree_complex(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}})
Expand Down
5 changes: 4 additions & 1 deletion test/complex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@
@test degree_complex(x * y^2 * conj(y)^3) == 3
@test degree_complex(x * y^2 * conj(y)^3, x) == 1
@test degree_complex(x * y^2 * conj(y)^3, y) == 3
@test degree_complex(a^5 * x * y^2 * conj(y)^4) == 9
@test degree_complex(a^5 * x * y^2 * conj(y)^2) == 8
@test halfdegree(x * y^2 * conj(y^3)) == 3
@test halfdegree(x * a^5 * conj(y)) == 4
@test halfdegree(x * a^5 * conj(y^2)) == 5
@test halfdegree(x^2 * a^5 * conj(y^2)) == 5
@test ordinary_variable([x, y, conj(x), a, real(x), imag(y)]) == [x, y, a]

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

0 comments on commit 3952d85

Please sign in to comment.