Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complex-valued variables #121

Merged
merged 29 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b46b905
First step to implement complex variables functionality
projekter Dec 26, 2022
a5cb35d
Move monomial function to correct file
projekter Dec 26, 2022
91583e2
Rename ordvar to ordinary_variable
projekter Apr 21, 2023
b3d8d73
Specialized complex routines for MonomialVector
projekter May 9, 2023
0cd4cfd
fix
projekter May 9, 2023
098ee29
coefficienttype -> coefficient_type
blegat May 10, 2023
43a0a61
Remove incorrect method
blegat May 10, 2023
b93b6bc
Merge branch 'bl/coefficient_type' of https://github.com/JuliaAlgebra…
projekter May 22, 2023
e417490
Merge branch 'master' of https://github.com/JuliaAlgebra/DynamicPolyn…
projekter May 28, 2023
214d169
Add a (sometimes working) implementation for incomplete substitution
projekter May 28, 2023
c5c1c99
Fix old syntax
projekter May 31, 2023
25ac2cf
Merge branch 'master' of https://github.com/JuliaAlgebra/DynamicPolyn…
projekter Jul 7, 2023
31a8d4c
Format
projekter Jul 7, 2023
a19c0b0
Remove TODO
projekter Aug 17, 2023
0bf8175
Merge branch 'master' of https://github.com/JuliaAlgebra/DynamicPolyn…
projekter Aug 24, 2023
6b055bd
Add complex kind parameter to VT("name") constructor
projekter Aug 24, 2023
a61aaa2
Remove iscomplex
projekter Sep 1, 2023
f87fb62
Remove dev dependency of MP, 0.5.3 has landed
projekter Nov 30, 2023
71ffcb5
Merge branch 'master' of https://github.com/JuliaAlgebra/DynamicPolyn…
projekter Nov 30, 2023
f9873bf
Merge remote-tracking branch 'upstream/master'
projekter Feb 19, 2024
da7fd62
Add ComplexKind docstring
projekter Feb 21, 2024
f8d3214
Rename ComplexKind instances
projekter Feb 21, 2024
7e07585
typo
projekter May 4, 2024
8cf9c12
change substitution rules
projekter May 4, 2024
6c230f6
Rename polycvar macro
projekter May 4, 2024
a6e1d80
Error for partial substitutions
projekter May 8, 2024
f9337ff
Format
projekter May 8, 2024
e0ad44d
Complex substitution test cases
projekter May 8, 2024
0845707
More strict substitution rules: only allow ordinary variable
projekter May 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[compat]
MultivariatePolynomials = "0.5"
MultivariatePolynomials = "0.5.3"
MutableArithmetics = "1"
Reexport = "1"
julia = "1"
Expand Down
10 changes: 5 additions & 5 deletions src/DynamicPolynomials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ function MP.similar_variable(
) where {V,M,S}
return MP.similar_variable(P, S)
end
function MP.similar_variable(
::Union{PolyType{V,M},Type{<:PolyType{V,M}}},
s::Symbol,
) where {V,M}
return Variable(string(s), V, M)
function MP.similar_variable(p::PolyType{V,M}, s::Symbol) where {V,M}
return Variable(string(s), V, M, isreal(p) ? REAL : COMPLEX)
end
function MP.similar_variable(::Type{<:PolyType{V,M}}, s::Symbol) where {V,M}
return Variable(string(s), V, M, REAL) # we cannot infer this from the type,
projekter marked this conversation as resolved.
Show resolved Hide resolved
end

include("promote.jl")
Expand Down
9 changes: 7 additions & 2 deletions src/comp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,19 @@ function (==)(
x::Variable{<:AnyCommutative{CreationOrder}},
y::Variable{<:AnyCommutative{CreationOrder}},
)
return x.variable_order.order.id == y.variable_order.order.id
return x.variable_order.order.id == y.variable_order.order.id &&
x.kind == y.kind
end

function Base.isless(
x::Variable{<:AnyCommutative{CreationOrder}},
y::Variable{<:AnyCommutative{CreationOrder}},
)
return isless(y.variable_order.order.id, x.variable_order.order.id)
if x.variable_order.order.id == y.variable_order.order.id
return isless(y.kind, x.kind)
else
return isless(y.variable_order.order.id, x.variable_order.order.id)
end
end

# Comparison of Monomial
Expand Down
7 changes: 7 additions & 0 deletions src/mono.jl
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,10 @@ function __add_variables!(
mono.z[map] = tmp
return
end

# for efficiency reasons
function Base.conj(x::Monomial{V,M}) where {V<:Commutative,M}
cv = conj.(x.vars)
perm = sortperm(cv, rev = true)
return Monomial{V,M}(cv[perm], x.z[perm])
end
28 changes: 27 additions & 1 deletion src/monomial_vector.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,28 @@
function MultivariatePolynomials.maxdegree(x::MonomialVector)
return isempty(x) ? 0 : maximum(sum.(x.Z))
end
# Complex-valued degrees for monomial vectors
for (fun, call, def, ret) in [
(:extdegree_complex, :extrema, (0, 0), :((min(v1, v2), max(v1, v2)))),
(:mindegree_complex, :minimum, 0, :(min(v1, v2))),
(:maxdegree_complex, :maximum, 0, :(max(v1, v2))),
]
eval(quote
function MultivariatePolynomials.$fun(x::MonomialVector)
isempty(x) && return $def
vars = variables(x)
@assert(!any(isrealpart, vars) && !any(isimagpart, vars))
grouping = isconj.(vars)
v1 = $call(sum(z[grouping]) for z in x.Z)
grouping = map(!, grouping)
v2 = $call(sum(z[grouping]) for z in x.Z)
return $ret

Check warning on line 109 in src/monomial_vector.jl

View check run for this annotation

Codecov / codecov/patch

src/monomial_vector.jl#L101-L109

Added lines #L101 - L109 were not covered by tests
end
end)
end
# faster complex-related functions
Base.isreal(x::MonomialVector) = all(isreal, x.vars)

Check warning on line 114 in src/monomial_vector.jl

View check run for this annotation

Codecov / codecov/patch

src/monomial_vector.jl#L114

Added line #L114 was not covered by tests
Base.conj(x::MonomialVector) = MonomialVector(conj.(x.vars), x.Z)

MP.variables(m::Union{Monomial,MonomialVector}) = m.vars

Expand Down Expand Up @@ -121,7 +143,11 @@
# TODO replace by MP function
function _error_for_negative_degree(deg)
if deg < 0
throw(ArgumentError("The degree should be a nonnegative number but the provided degree `$deg` is negative."))
throw(
ArgumentError(
"The degree should be a nonnegative number but the provided degree `$deg` is negative.",
),
)
end
end

Expand Down
80 changes: 65 additions & 15 deletions src/subs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,73 @@

function fillmap!(
vals,
vars::Vector{<:Variable{<:NonCommutative}},
vars::Vector{<:Variable{C}},
s::MP.Substitution,
)
for j in eachindex(vars)
if vars[j] == s.first
vals[j] = s.second
) where {C}
# We may assign a complex or real variable to its value (ordinary substitution).
# We may also assign a complex value to its conjugate, or just the real or imaginary parts
# Any combination of z, conj(z), real(z), imag(z), imag(conj(z)) can occur in either the polynomial or the substitution,
# and we must handle all of them correctly.
# Note: This may or may not work... Issues can arise if the substitutions contain the real and imaginary (or only one of
# those) of a variable separately whenever vals is not of the correct type:
# - Unless subs() originally had a polynomial-valued rhs, vals will be scalars, monomials, or terms. So when we try to
# assign a polynomial to its value (which is necessary, as the one-step substitution of only the real or only the
# imaginary part is incomplete), this is an impossible conversion.
# - The coefficients in vals might not be complex-valued; but to assign only a part of the variable, we necessarily need to
# introduce an explicit imaginary coefficient to the value.
# Currently, we don't do anything to catch these errors.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather have a solid implementation that only handle some cases ans error in other cases than an implementation that does not error but that may have incorrect behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, now I modified it to a different implementation. Basically, real and imaginary parts, when occurring in the substitution rules, are seen as assignments to real values, so they won't act on the complex variables at all, if those occur. You could then say that this (call it lexicographic substitution) is inconsistent with the complex variable in the rule still acting on the real and imaginary parts, and I agree. However, where to draw the line? It is then also inconsistent to apply it to the conjugate, but this is (I think) a very reasonable choice.
I guess that a typical situation will probably never mix real/imaginary part variables with full complex variables, so I don't expect whatever behavior is defined here to actually be used somewhere.

if s.first.kind == REAL
for j in eachindex(vars)
if vars[j] == s.first
vals[j] = s.second
C == Commutative && break
end
end
else
for j in eachindex(vars)
if vars[j].variable_order.order.id ==
s.first.variable_order.order.id
if s.first.kind == COMPLEX || s.first.kind == CONJ
value = s.first.kind == CONJ ? conj(s.second) : s.second
if vars[j].kind == COMPLEX
vals[j] = value
elseif vars[j].kind == CONJ
vals[j] = conj(value)
elseif vars[j].kind == REAL_PART
vals[j] = real(value)

Check warning on line 45 in src/subs.jl

View check run for this annotation

Codecov / codecov/patch

src/subs.jl#L44-L45

Added lines #L44 - L45 were not covered by tests
else
vals[j] = imag(value)

Check warning on line 47 in src/subs.jl

View check run for this annotation

Codecov / codecov/patch

src/subs.jl#L47

Added line #L47 was not covered by tests
end
elseif s.first.kind == REAL_PART
isreal(s.second) || error(
"Cannot assign a complex value to the real part of an expression",
)
value = real(s.second) # just to make sure the type is correct
if vars[j].kind == COMPLEX
vals[j] = value + im * imag(vals[j])
projekter marked this conversation as resolved.
Show resolved Hide resolved
elseif vars[j].kind == CONJ
vals[j] = value - im * imag(vals[j])
elseif vars[j].kind == REAL_PART

Check warning on line 58 in src/subs.jl

View check run for this annotation

Codecov / codecov/patch

src/subs.jl#L56-L58

Added lines #L56 - L58 were not covered by tests
vals[j] = value
end
# else we know the real part but use the imaginary part; do nothing
else
@assert(s.first.kind == IMAG_PART)
isreal(s.second) || error(

Check warning on line 64 in src/subs.jl

View check run for this annotation

Codecov / codecov/patch

src/subs.jl#L63-L64

Added lines #L63 - L64 were not covered by tests
"Cannot assign a complex value to the imaginary part of an expression",
)
value = real(s.second) # just to make sure the type is correct
if vars[j].kind == COMPLEX
vals[j] = real(vals[j]) + im * value
elseif vars[j].kind == CONJ
vals[j] = real(vals[j]) - im * value
elseif vars[j].kind == IMAG_PART

Check warning on line 72 in src/subs.jl

View check run for this annotation

Codecov / codecov/patch

src/subs.jl#L67-L72

Added lines #L67 - L72 were not covered by tests
vals[j] = value
end
# else we know the imaginary part but use the real part; do nothing
end
end
end
end
end
function fillmap!(
vals,
vars::Vector{<:Variable{<:Commutative}},
s::MP.Substitution,
)
j = findfirst(isequal(s.first), vars)
if j !== nothing
vals[j] = s.second
end
end
function fillmap!(vals, vars, s::MP.AbstractMultiSubstitution)
Expand Down
Loading
Loading