sagemath/sage

Refactor to break cyclic import in complex numbers

tobiasdiez opened this issue · 32 comments

Importing complex_mpfr does currently not work without previously importing sage.all. This is because of a couple of cyclic imports (e.g. complex_number -> complex_double -> complex_number).

In this ticket, we break these cyclic imports by narrowing down some of the global module imports to local function-wide imports. This was not possible in the case of the CCtoCDF class, since there ComplexDoubleElement is cimported, which has to be done on the module level. I've hence extracted this class to a new module complex_conversion.pyx.

With these changes, the executing the following code

from sage.rings.complex_mpfr import ComplexField
print(ComplexField())

using ./venv/bin/python3 works. Previously, it failed with

  File "/home/tobias/sage/src/test.py", line 1, in <module>
    from sage.rings.complex_mpfr import ComplexField
  File "sage/rings/complex_mpfr.pyx", line 1, in init sage.rings.complex_mpfr
  File "sage/rings/real_mpfr.pyx", line 1, in init sage.rings.real_mpfr
  File "sage/libs/mpmath/utils.pyx", line 17, in init sage.libs.mpmath.utils
ImportError: cannot import name ComplexField

Depends on #32612
Depends on #32610
Depends on #32174

CC: @mkoeppe @tscrim @antonio-rojas

Component: refactoring

Author: Tobias Diez

Branch/Commit: 401fee5

Reviewer: Matthias Koeppe

Issue created by migration from https://trac.sagemath.org/ticket/30741

comment:2

Changes of this type need careful review regarding possible performance penalties from the import

comment:3

Instead of

+            from sage.rings.complex_conversion import CCtoCDF
+            return CCtoCDF(S, self)

Why don't you add a proper .pxd file and does a cimport at module level?

comment:4

A module level cipmort CCtoCDF would lead again to a circular import: complex_double -> complex_conversion (for CCtoCDF) -> complex_double (for ComplexDoubleElement).

comment:5

Replying to @tobiasdiez:

A module level cipmort CCtoCDF would lead again to a circular import: complex_double -> complex_conversion (for CCtoCDF) -> complex_double (for ComplexDoubleElement).

cimport does not trigger the Python module import (the things in the .pyx). It is just reading the corresponding .pxd file.

EDIT: and it is perfectly fine to have circular imports here as these are guarded with macros at the C level

comment:8

Setting new milestone based on a cursory review of ticket status, priority, and last modification date.

comment:9

Setting a new milestone for this ticket based on a cursory review.

Branch pushed to git repo; I updated commit sha1. New commits:

cf61104Merge branch 'develop' of git://github.com/sagemath/sage into public/refactoring/complexCycle
381dc67Add pxd file
1d0d20dTry global cimport

Changed commit from 8c220db to 1d0d20d

comment:11

With #32612 you can also use isinstance(..., sage.rings.abc.ComplexIntervalField) instead of using a local import

Changed commit from 1d0d20d to dfa1de0

Branch pushed to git repo; I updated commit sha1. New commits:

dfa1de0Use sage.rings.abc.ComplexIntervalField instead of soon-deprecated class
comment:13

Replying to @mkoeppe:

With #32612 you can also use isinstance(..., sage.rings.abc.ComplexIntervalField) instead of using a local import

Thanks for the pointer! Done

Dependencies: #32612

Branch pushed to git repo; I updated commit sha1. New commits:

3f2d873Merge branch 'develop' of git://github.com/sagemath/sage into public/refactoring/complexCycle
acf57b5Fix typo

Changed commit from dfa1de0 to acf57b5

comment:16

still needs work or ready for review?

Changed commit from acf57b5 to fb09fda

Branch pushed to git repo; I updated commit sha1. New commits:

fb09fdaFix up imports
comment:18

I've fixed the branch but does it actually solve anything?

Changed dependencies from #32612 to #32612, #32610

Branch pushed to git repo; I updated commit sha1. New commits:

55da358Merge branch 'develop' of https://github.com/sagemath/sage into public/refactoring/complexCycle
401fee5Remove cycle around complex_double

Changed commit from fb09fda to 401fee5

New commits:

55da358Merge branch 'develop' of https://github.com/sagemath/sage into public/refactoring/complexCycle
401fee5Remove cycle around complex_double

Description changed:

--- 
+++ 
@@ -1,3 +1,20 @@
-Importing `complex_number` does currently not work without previously importing `sage.all`. This is because of a couple of cyclic imports (e.g. `complex_number` -> `complex_double` -> `complex_number`).
+Importing `complex_mpfr` does currently not work without previously importing `sage.all`. This is because of a couple of cyclic imports (e.g. `complex_number` -> `complex_double` -> `complex_number`).
 
 In this ticket, we break these cyclic imports by narrowing down some of the global module imports to local function-wide imports. This was not possible in the case of the `CCtoCDF` class, since there `ComplexDoubleElement` is cimported, which has to be done on the module level. I've hence extracted this class to a new module `complex_conversion.pyx`.
+
+With these changes, the executing the following code
+
+```
+from sage.rings.complex_mpfr import ComplexField
+print(ComplexField())
+```
+using `./venv/bin/python3` works. Previously, it failed with 
+
+```
+  File "/home/tobias/sage/src/test.py", line 1, in <module>
+    from sage.rings.complex_mpfr import ComplexField
+  File "sage/rings/complex_mpfr.pyx", line 1, in init sage.rings.complex_mpfr
+  File "sage/rings/real_mpfr.pyx", line 1, in init sage.rings.real_mpfr
+  File "sage/libs/mpmath/utils.pyx", line 17, in init sage.libs.mpmath.utils
+ImportError: cannot import name ComplexField
+```
comment:23

Thanks for the help Matthias!

It still needed a bit further cleanup, but should work now. I've updated the ticket description to discuss what didn't work before but now does.

comment:24

Thanks, this looks good. I'll test this now also with #32601, but I don't expect problems

Reviewer: Matthias Koeppe

comment:26

Thanks for the review!

comment:27
sage -t --long --warn-long 43.1 --random-seed=227602538950590531556014576097185838563 src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py
**********************************************************************
File "src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py", line 2038, in sage.geometry.hyperbolic_space.hyperbolic_geodesic.HyperbolicGeodesicPD.plot
Failed example:
    H = h.plot(thickness=6, color="orange"); H           # optional - sage.plot
Expected:
    Graphics object consisting of 2 graphics primitives        # optional - sage.plot
Got:
    Graphics object consisting of 2 graphics primitives
**********************************************************************
comment:28

Fixed in #32174.

Changed dependencies from #32612, #32610 to #32612, #32610, #32174