Skip to content

Complex numbers: the parts keep their own errors, as in WMA - #2066

Merged
rocky merged 1 commit into
masterfrom
complex-precision
Oct 26, 2026
Merged

rocky merged 1 commit into
masterfrom
complex-precision

Conversation

@tombouctou

@tombouctou tombouctou commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

The rest of #2047 step 6, for complex numbers: complex arithmetic, and functions with a complex value at a real argument, now get their precision the way WMA does.

In WMA each part of a Complex keeps its own error. Accuracy combines the two errors in quadrature, and Precision is that accuracy plus Log10 of the modulus; an exact part also counts toward the modulus. Before this PR, Mathics3 gave a complex result the precision of its least precise input, or machine precision, which is why 1.5`20.3 + 2.5`20.3 I had precision 15.

  • Plus and Times on complex numbers with exact or PrecisionReal parts work out each part with the real rules. Times multiplies as a balanced tree and squares a pair of equal factors. z^n squares starting from the top bit of n, and 1/z is (a - b I)/(a^2 + b^2). This is the order WMA uses, and it changes the last digits of the precision. 2.`20 I keeps an exact zero real part.
  • A function with a complex value at a real argument (ArcSin[2.`20], Log[-1.`20], ArcCosh[020], ArcSec[0.520]``, …) gives each part the error from that part of x f'[x], as #2052–#2056 do for real values. A part that does not change gets 2^-54of the error, so thePi part of ``Log[-1.20]`` is 16 digits more precise than the real part, as in WMA. At a zero or a branch point, the change of f within the error goes to the nonzero parts.
  • A negative PrecisionReal to a Rational power: x^(1/q) shares the error of the whole value between its parts, and x^(p/q) is x^n (x^(1/q))^r. Checked against WMA for p/q from −5/2 to 7/3.
  • Log[x] is no longer rewritten as Log[E, x]: the numerified E base added an error of its own.
  • An inexact zero real part is now shown, as in WMA (019.4 + 2.19.7 I``). N[I, 30] is ``1.30 I``.
Input master this PR WMA 15
Precision[ArcSec[0.5`20]] 15.1196 20.0571 20.0571
Precision[1.5`20.3 + 2.5`20.3 I] 15. 20.3 20.3
ArcCosh[020]`` 1.5707963…`20.*I 1.5707963…`20.1961*I 1.5707963…`20.1961*I
Sqrt[-2.`20] 1.4142135…`20.*I 1.4142135…`20.301*I 1.4142135…`20.301*I
Precision[Log[-1.`20]] 15.4971 20.4971 20.4971
Accuracy[Im[Log[-1.`20]]] - Accuracy[Re[Log[-1.`20]]] 4.50285 16.2556 16.2556
Precision[(1.`20 + 2.`25 I) (3.`22 - 1.`18 I)] 15. 18.4939 18.4939
Precision[(1.`20 + 2.`25 I)^5] 15. 19.3049 19.3049
Precision[(-8.`20)^(1/3)] 20. 20.4771 20.4771

Not in this PR: functions of a complex argument (Sqrt[1.`20 + I], Exp[1.`20 I]), a negative base with an inexact exponent, and N of an exact complex number with two nonzero parts, where WMA splits the precision between the parts.

Tests

test_complex_precision and test_complex_valued_precision in test/builtin/atomic/test_numbers.py, next to test_real_valued_precision, with WMA 15 values. Full run on kimmy: all doctests pass. pytest under xdist (-n 4) fails a different handful of tests on each run, on master as well, because those tests depend on symbols that earlier tests in the same worker define or clear (f, F, x). With --dist loadfile, master fails 15 (test_forms.py, test_options.py, test_testing_expressions.py, test_latex.py) and this branch fails 7 (test_list.py, test_diffeqns.py, test_constructing.py). None of them is about numbers, and each of these files passes when run on its own.

Benchmark from #2050 on kimmy, best of 5, ms:

Case master this PR
Total[Table[N[1/k, 50], {k, 2000}]] 2645 2738
Times @@ Table[N[1 + 1/k, 50], {k, 2000}] 3255 3324
Do[x = N[k, 50] + Pi, {k, 2000}] 5388 5238
Total[Table[1./k, {k, 2000}]] 1988 2056
Total[Table[N[1/k + I/(k+1), 50], {k, 2000}]] 4884 5127
Times @@ Table[N[1 + I/k, 50], {k, 500}] 883 1070
Table[N[1 + I/k, 30]^5, {k, 500}] 1435 1820
Table[1 / N[1 + I/k, 30], {k, 500}] 1592 1670
Table[ArcSin[N[1 + k, 30]], {k, 500}] 1112 1504
Table[Log[N[-k, 30]], {k, 500}] 1421 1322

The real cases are unchanged within noise. Complex Times, z^5 and ArcSin outside its domain are 20–35% slower: their parts are now computed from real operations, and ArcSin takes more derivatives.

🤖 Generated with Claude Code

MATH-257, the rest of #2047 step 6 for complex numbers.

- Precision of a Complex is its accuracy (the parts' errors in
  quadrature) plus Log10 of its modulus, to which an exact part counts.
- Plus and Times on Complex numbers with exact or PrecisionReal parts
  work out each part from the real rules. Times multiplies as a balanced
  tree and squares a pair of the same number, z^n squares from the top
  bit of n, and 1/z is (a - b I)/(a^2 + b^2), as WMA does. 2.`20 I keeps
  an exact zero real part.
- A function with a complex value at real arguments (ArcSin[2.`20],
  Log[-1.`20], ArcCosh[0``20], ...) gives each part the error from that
  part of x f'[x]; a part that does not change gets 2^-54 of the error.
- A negative PrecisionReal to a Rational power: x^(1/q) shares the error
  of the whole between its parts; x^(p/q) is x^n (x^(1/q))^r.
- Log[x] no longer becomes Log[E, x], whose base added an error.
- A Complex shows an inexact zero real part; N[I, 30] is 1.`30 I.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rocky

rocky commented Oct 26, 2026

Copy link
Copy Markdown
Member

LGTM

@rocky
rocky merged commit 35e662e into master Oct 26, 2026
15 checks passed
@rocky
rocky deleted the complex-precision branch October 26, 2026 00:54
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