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

Complex-valued variables #121

merged 29 commits into from
May 13, 2024

Conversation

projekter
Copy link
Contributor

This is in conjuction with JuliaAlgebra/MultivariatePolynomials.jl#213 and provides the DynamicPolynomials implementation of the complex-valued variable functionality.

@projekter
Copy link
Contributor Author

Issue with sometimes working implementation for incomplete substitution (I just quote the code comment)

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.

Design question: With the new implementation, the "dynamic" part becomes slightly more "typed", as the variable and monomial ordering are now also part of the type. Still, the complex kind is a field instead of a type parameter. While I definitely would keep this to distinguish between the variable, its conjugate, its real and imaginary part (else all containers holding such variables would need to be abstract containers, making this very inefficient), it may be debatable whether the issue of "is this a complex type at all" should be encoded as a type parameter. Pro: Some replacements may be simpler to decide based on static dispatch. Con: Duplication of information; and mixing real and complex variables again has to fall back to a generic implementation.

src/mono.jl Outdated Show resolved Hide resolved
@blegat
Copy link
Member

blegat commented May 31, 2023

Yes, I would definitely not incorporate whether it's a real, imaginary part etc... as type as it would disallow mixing different ones in a polynomial etc...
About whether there should be a type for saying whether it was created with @polyvar and @polycvar, that would disallow mixing the two types of variables in the same polynomial for instance so that wouldn't be ideal right ?

@projekter
Copy link
Contributor Author

This would depend on the actual implementation in Monomial. Combining both would still work if the vars vectors still only contained elements of type Variable{V,M}, not specializing the concrete complex type C. But then, this would be horribly inefficient as Julia would have to generate type checks and dynamic dispatches on every operation. So I agree this would be very "not ideal".

@projekter projekter marked this pull request as ready for review August 17, 2023 16:05
@projekter projekter changed the title First step to implement complex variables functionality Complex-valued variables Sep 12, 2023
src/var.jl Outdated Show resolved Hide resolved
@blegat
Copy link
Member

blegat commented Apr 19, 2024

@projekter I forgot about this PR, we should merge it. I'll try to take a look next week, remind me if I don't

@projekter
Copy link
Contributor Author

@blegat Here's a reminder

src/DynamicPolynomials.jl Outdated Show resolved Hide resolved
src/subs.jl Outdated Show resolved Hide resolved
src/subs.jl Outdated
# 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.

src/var.jl Outdated Show resolved Hide resolved
src/subs.jl Outdated Show resolved Hide resolved
src/subs.jl Outdated
vals[j] = value
elseif vars[j].kind != REAL_PART
error(
"Found complex variable with substitution of imaginary part - not implemented",
Copy link
Member

Choose a reason for hiding this comment

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

Nice, could you add some tests for these edge cases ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a few testcases. Note that I did not use the substitution syntax without explicit variable specification. This is because variables(p) would list all variables, complex, conjugates, real, and imaginary parts separately. So you'd have to specify values for all of them (unnecessarily - and this would even be more restrictive as then: z + real(z), called with arguments (1+2im, 1) would trigger the new error, because z occurs but a value is assigned to real(z)). Either the whole variable assignment would need to be redesigned or we just say that for complex variables, you should always explicitly specify a substitution rule.
(But even then, substitution rules could be redundant; [z, real(z)] => [1+2im, 1] is also a valid rule. Should we check whether the rules are self-consistent? Given that variables occuring twice are also not checked, I think the current lax behavior is fine.

Copy link
Member

Choose a reason for hiding this comment

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

Indeed, that's an issue. We should only allow COMPLEX_PART to used in substitutions then. In this PR, I think we should target the simplest implementation that's bullet proof. We can try to make something more complicated but I'd rather do it in a separate PR if there is a use case for it. So I'd just restrict z to be a COMPLEX_PART whenever z => ... is used

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This simplifies the substitution process significantly. I don't know whether I like it, but there's some sense to it.

Copy link
Member

Choose a reason for hiding this comment

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

We can revisit in a follow up PR without being breaking. We'll be able to recover your implementation easily from this branch.

Copy link
Member

@blegat blegat left a comment

Choose a reason for hiding this comment

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

Thanks a lot for pushing this! It's been a big request from many users. I'll merge once CI turns green

projekter added a commit to projekter/MultivariatePolynomials.jl that referenced this pull request May 13, 2024
- Macro for complex variables is now called `@complex_polyvar`
- DP does not support partial substitutions, so disable the tests
@blegat blegat merged commit 121bd58 into JuliaAlgebra:master May 13, 2024
4 checks passed
blegat pushed a commit to JuliaAlgebra/MultivariatePolynomials.jl that referenced this pull request May 15, 2024
* Update for changes discussed in JuliaAlgebra/DynamicPolynomials.jl#121
- Macro for complex variables is now called `@complex_polyvar`
- DP does not support partial substitutions, so disable the tests

* Add explanation, format
@blegat blegat mentioned this pull request May 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants