From 3952d8562986615c8a160cdcf22d7f38282bef4e Mon Sep 17 00:00:00 2001 From: Benjamin Desef Date: Fri, 5 Apr 2024 09:16:20 +0200 Subject: [PATCH] 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 --- src/complex.jl | 85 ++++++++++++++++++++++++++++++------------------- test/complex.jl | 5 ++- 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/src/complex.jl b/src/complex.jl index 7a3f4e1f..9575bc34 100644 --- a/src/complex.jl +++ b/src/complex.jl @@ -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 @@ -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 @@ -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}}) diff --git a/test/complex.jl b/test/complex.jl index cfad5448..35229673 100644 --- a/test/complex.jl +++ b/test/complex.jl @@ -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]) ==