Skip to content

The complex conjugate and the principal argument are functions - #2181

Merged
Rafael-SOWNet merged 2 commits into
masterfrom
a-complex-conjugate-is-a-function
Oct 28, 2026
Merged

Rafael-SOWNet merged 2 commits into
masterfrom
a-complex-conjugate-is-a-function

Conversation

@Rafael-SOWNet

@Rafael-SOWNet Rafael-SOWNet commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Chapter III of Modenov's Problems in Geometry writes every point as a complex number and every statement in z and its conjugate, and its angles are arguments. conjugate was refused by name, and conj and arg since #2136. Both are now functions, which Happypig375's answer on #212 opened up: "You can add new functions as you see fit as long as they are mathematically precise and unambiguous".

conjugate(z), also conj(z), is Entity.Conjugatef, and MathS.Conjugate.

  • A number is conjugated, conj(3 + 4i) being 3 - 4i, and the conjugate is an involution: conj(conj(z)) is z.
  • It is a field automorphism, so it is carried through sums, differences, products, quotients and whole powers. That keeps it on the symbols, and an identity between conjugates is then decided as one between symbols: conj((z_2 - z_1)/(z_3 - z_1)) - (conj(z_2) - conj(z_1))/(conj(z_3) - conj(z_1)) is 0 where the quotient is defined.
  • It is not carried through a root or a logarithm, whose cuts lie on the negative real axis: conj(sqrt(-1)) is -i and sqrt(conj(-1)) is i.
  • What is known to be real is its own conjugate: a modulus, and a symbol declared real with domain(x, RR). A bare symbol keeps its conjugate. The MathS.Settings.Codomain setting is not read, because InnerSimplified is cached on the node and the setting is not; a test pins that.
  • conj(x) = 2 + i is solved to { 2 - i }. The derivative is taken along the variable's real line, as the modulus's is: that of conj(f) is conj(f'). A matrix is conjugated entry by entry.

arg(z) is Entity.Argf, and MathS.Arg: the principal argument, in (-pi, pi], so that z = abs(z) e^(i arg(z)). It is real, and has no value at 0, where every angle fits; its domain condition says so.

  • A number's argument is exact. The axes are 0, pi and +-pi/2, so arg(-1) is pi, the end of the interval that is included, as the principal logarithm's imaginary part is.
  • Elsewhere it is arctan(b/a), turned by pi into the left half-plane and gathered with a table value of the arctangent: arg(-1 - i) is -3/4 * pi and arg(-2 + i) is arctan(-1/2) + pi. Numerically it is the phase.
  • An equation in it is left unsolved, since where the argument is a value is a ray. Its derivative is left unevaluated, since along the real line it would be Im(f'/f), and there is no node for that.

Both print by name, draw as \overline{z} and \arg\left(z\right), and export to sympy.conjugate and sympy.arg. Neither is compiled by Compile yet.

Also

  • The parser is regenerated, and only trunc is still refused by name.
  • conj, arg, re, im, bar and det are read as products, not refused like conjugate #2136's breaking entry, not released, is rewritten as this one.
  • The MCP server's syntax document names the three, and points at (z + conj(z))/2 for a real part, since re and im are still read as products, as TheTwoLetterNamesAreLeftAsProducts records.
  • The native smoke test evaluates both.
Input Was (2.6.0) Now
conjugate(z) throws UnrecognizedFunctionParseException conjugate(z)
conj(z) conj * z conjugate(z)
conj(3 + 4i), simplified (3 + 4i) * conj 3 - 4i
arg(z) arg * z arg(z)
arg(-1), simplified -arg pi

Measured

  • Suite: 17,288 passed, 0 failed, 13 skipped, in the chain's run on the code as pushed. An earlier run's one failure was the documented-examples test catching "3 + 4 i" in MathS.Conjugate's own example, which reads as 3 + 4 ^ i (3 i parses as 3 ^ i: an imaginary literal after a factor is read as its exponent #1503); the example says 3 + 4i now.
  • McpUnitTests 349, TerminalUnitTests 69, InteractiveWrapperUnitTests 19 passed. NativeAOT smoke test 49 of 49, published and run locally.
  • Harnesses: CI's BoundCheck found arg(prime(x)) simplified to 0, from prime's codomain, where at x = -3 the original has no value. The second commit decides the argument for numbers only and adds the row. Then, locally: BoundCheck 1076 shapes with 0 disagreements, RootCheck 596/596, CasBench 120/121 with 0 wrong, SimpSweep 10463/10463, CrashCheck 0 crashed, PropCheck and RuleCheck clean, CanonCheck (2091) and Confluence (3) matching their baselines; suite 17,288 passed, 0 failed.
  • Intbench chain against master 00d857b: identical on every family, 0 moves. Performance gate passed on all 19 gated benchmarks, every figure equal to master's.

Part of #2145. re and im are left for a PR of their own, since they change how re(x) reads.

🤖 Generated with Claude Code

https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura

Chapter III of Modenov's Problems in Geometry writes every point as a complex number and
every statement in z and its conjugate, and its angles are arguments. conjugate was refused
by name, and conj and arg since #2136.

conjugate(z), also conj(z), is Entity.Conjugatef. A number is conjugated, and the conjugate
is an involution. It is a field automorphism, so it is carried through sums, differences,
products, quotients and whole powers, which keeps it on the symbols, and an identity between
conjugates is then decided as one between symbols. It is not carried through a root or a
logarithm, whose cuts break the reflection: conj(sqrt(-1)) is -i, sqrt(conj(-1)) is i. What
is known to be real is its own conjugate: a modulus, and a symbol declared real. The
codomain setting is not read, since InnerSimplified is cached on the node and the setting is
not. conj(f) = v is solved as f = conj(v), and the derivative is taken along the variable's
real line, as the modulus's is: that of conj(f) is conj(f').

arg(z) is Entity.Argf, the principal argument in (-pi, pi], real, with no value at 0. A number's
argument is exact: the axes are 0, pi and +-pi/2, and elsewhere arctan(b/a), turned by pi into
the left half-plane and gathered with a table value of the arctangent, so arg(-1 - i) is
-3/4 * pi. Numerically it is the phase.

Both print by name, draw as \overline{z} and \arg(z), and export to SymPy's conjugate and arg.
The MCP server's syntax document names them, the native smoke test evaluates both, and re and
im are still read as products.

Part of #2145.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
@Rafael-SOWNet Rafael-SOWNet moved this from Todo to In review in AngouriMath Oct 28, 2026
@codecov

codecov Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.12903% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.44%. Comparing base (00d857b) to head (df1f1d9).

Files with missing lines Patch % Lines
...ntinuous/Entity.Continuous.ComplexParts.Classes.cs 80.00% 2 Missing ⚠️
...ngouriMath/Functions/Continuous/Differentiation.cs 0.00% 0 Missing and 1 partial ⚠️
...nuous/Evaluation.Continuous.Arithmetics.Classes.cs 97.43% 0 Missing and 1 partial ⚠️
Sources/AngouriMath/Functions/Substitute.cs 50.00% 0 Missing and 1 partial ⚠️
...AngouriMath/Functions/TreeAnalyzer/Sort.Classes.cs 50.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2181      +/-   ##
==========================================
- Coverage   86.44%   86.44%   -0.01%     
==========================================
  Files         296      297       +1     
  Lines       65332    65406      +74     
  Branches    19180    19198      +18     
==========================================
+ Hits        56479    56541      +62     
- Misses       4802     4812      +10     
- Partials     4051     4053       +2     
Flag Coverage Δ
cpp 37.77% <ø> (ø)
csharp 87.01% <96.12%> (-0.01%) ⬇️
fsharp 32.97% <ø> (ø)
interactive 40.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
Sources/AngouriMath/Convenience/MathS.cs 89.96% <100.00%> (+0.06%) ⬆️
Sources/AngouriMath/Core/Antlr/AngouriMathLexer.cs 99.00% <100.00%> (ø)
...ources/AngouriMath/Core/Antlr/AngouriMathParser.cs 86.61% <100.00%> (+0.09%) ⬆️
Sources/AngouriMath/Core/Domains.Classes.cs 100.00% <100.00%> (ø)
...Math/Core/Transformations/Matching/MatchPattern.cs 85.96% <100.00%> (+0.06%) ⬆️
...nuous/Solvers/EquationSolver/InvertNode.Classes.cs 57.14% <100.00%> (+0.38%) ⬆️
...unctions/Output/Latex/Latex.Arithmetics.Classes.cs 97.32% <100.00%> (+0.04%) ⬆️
...ns/Output/ToString/ToString.Arithmetics.Classes.cs 100.00% <100.00%> (ø)
...ions/Output/ToSympy/ToSympy.Arithmetics.Classes.cs 71.15% <100.00%> (+1.15%) ⬆️
...s/AngouriMath/Functions/TreeAnalyzer/RealValued.cs 83.75% <ø> (ø)
... and 5 more

... and 7 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

arg(prime(x)) simplified to 0, because prime's codomain is the primes. But at x = -3 prime(x)
has no value, and neither has its argument, so BoundCheck found the simplification defined
where the original was not. A codomain says what a node's values are and not where it has
one; the argument is now decided for numbers only.

Part of #2145.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant