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

BUG: Complex printing tests fail on Windows ARM64 #25626

Open
QuLogic opened this issue Jan 19, 2024 · 7 comments
Open

BUG: Complex printing tests fail on Windows ARM64 #25626

QuLogic opened this issue Jan 19, 2024 · 7 comments
Labels

Comments

@QuLogic
Copy link
Contributor

QuLogic commented Jan 19, 2024

Describe the issue:

6 out of 16 tests fail in test_print.py when running on Windows on ARM. They appear to all be related to complex numbers. If I've read the results correctly, it only affects np.cdouble and np.clongdouble and not np.complex64.

Reproduce the code example:

pytest numpy/_core/tests/test_print.py

Error message:

__________ test_complex_types[complex128] __________

tp = <class 'numpy.complex128'>

    @pytest.mark.parametrize('tp', [np.complex64, np.cdouble, np.clongdouble])
    def test_complex_types(tp):
        """Check formatting of complex types.

            This is only for the str function, and only for simple types.
            The precision of np.float32 and np.longdouble aren't the same as the
            python float precision.

        """
        for x in [0, 1, -1, 1e20]:
            assert_equal(str(tp(x)), str(complex(x)),
                         err_msg='Failed str formatting for type %s' % tp)
            assert_equal(str(tp(x*1j)), str(complex(x*1j)),
                         err_msg='Failed str formatting for type %s' % tp)
>           assert_equal(str(tp(x + x*1j)), str(complex(x + x*1j)),
                         err_msg='Failed str formatting for type %s' % tp)
E           AssertionError:
E           Items are not equal: Failed str formatting for type <class 'numpy.complex128'>
E            ACTUAL: '(1+0j)'
E            DESIRED: '(1+1j)'

tp         = <class 'numpy.complex128'>
x          = 1

numpy\_core\tests\test_print.py:65: AssertionError
__________ test_complex_types[clongdouble] __________

tp = <class 'numpy.clongdouble'>

    @pytest.mark.parametrize('tp', [np.complex64, np.cdouble, np.clongdouble])
    def test_complex_types(tp):
        """Check formatting of complex types.

            This is only for the str function, and only for simple types.
            The precision of np.float32 and np.longdouble aren't the same as the
            python float precision.

        """
        for x in [0, 1, -1, 1e20]:
            assert_equal(str(tp(x)), str(complex(x)),
                         err_msg='Failed str formatting for type %s' % tp)
            assert_equal(str(tp(x*1j)), str(complex(x*1j)),
                         err_msg='Failed str formatting for type %s' % tp)
>           assert_equal(str(tp(x + x*1j)), str(complex(x + x*1j)),
                         err_msg='Failed str formatting for type %s' % tp)
E           AssertionError:
E           Items are not equal: Failed str formatting for type <class 'numpy.clongdouble'>
E            ACTUAL: '(1+0j)'
E            DESIRED: '(1+1j)'

tp         = <class 'numpy.clongdouble'>
x          = 1

numpy\_core\tests\test_print.py:65: AssertionError
__________ test_complex_inf_nan[complex128] __________

dtype = <class 'numpy.complex128'>

    @pytest.mark.parametrize('dtype', [np.complex64, np.cdouble, np.clongdouble])
    def test_complex_inf_nan(dtype):
        """Check inf/nan formatting of complex types."""
        TESTS = {
            complex(np.inf, 0): "(inf+0j)",
            complex(0, np.inf): "infj",
            complex(-np.inf, 0): "(-inf+0j)",
            complex(0, -np.inf): "-infj",
            complex(np.inf, 1): "(inf+1j)",
            complex(1, np.inf): "(1+infj)",
            complex(-np.inf, 1): "(-inf+1j)",
            complex(1, -np.inf): "(1-infj)",
            complex(np.nan, 0): "(nan+0j)",
            complex(0, np.nan): "nanj",
            complex(-np.nan, 0): "(nan+0j)",
            complex(0, -np.nan): "nanj",
            complex(np.nan, 1): "(nan+1j)",
            complex(1, np.nan): "(1+nanj)",
            complex(-np.nan, 1): "(nan+1j)",
            complex(1, -np.nan): "(1+nanj)",
        }
        for c, s in TESTS.items():
>           assert_equal(str(dtype(c)), s)
E           AssertionError:
E           Items are not equal:
E            ACTUAL: '(inf+0j)'
E            DESIRED: '(inf+1j)'

TESTS      = {(inf+0j): '(inf+0j)', infj: 'infj', (-inf+0j): '(-inf+0j)', -infj: '-infj', ...}
c          = (inf+1j)
dtype      = <class 'numpy.complex128'>
s          = '(inf+1j)'

numpy\_core\tests\test_print.py:99: AssertionError
__________ test_complex_inf_nan[clongdouble] __________

dtype = <class 'numpy.clongdouble'>

    @pytest.mark.parametrize('dtype', [np.complex64, np.cdouble, np.clongdouble])
    def test_complex_inf_nan(dtype):
        """Check inf/nan formatting of complex types."""
        TESTS = {
            complex(np.inf, 0): "(inf+0j)",
            complex(0, np.inf): "infj",
            complex(-np.inf, 0): "(-inf+0j)",
            complex(0, -np.inf): "-infj",
            complex(np.inf, 1): "(inf+1j)",
            complex(1, np.inf): "(1+infj)",
            complex(-np.inf, 1): "(-inf+1j)",
            complex(1, -np.inf): "(1-infj)",
            complex(np.nan, 0): "(nan+0j)",
            complex(0, np.nan): "nanj",
            complex(-np.nan, 0): "(nan+0j)",
            complex(0, -np.nan): "nanj",
            complex(np.nan, 1): "(nan+1j)",
            complex(1, np.nan): "(1+nanj)",
            complex(-np.nan, 1): "(nan+1j)",
            complex(1, -np.nan): "(1+nanj)",
        }
        for c, s in TESTS.items():
>           assert_equal(str(dtype(c)), s)
E           AssertionError:
E           Items are not equal:
E            ACTUAL: '(inf+0j)'
E            DESIRED: '(inf+1j)'

TESTS      = {(inf+0j): '(inf+0j)', infj: 'infj', (-inf+0j): '(-inf+0j)', -infj: '-infj', ...}
c          = (inf+1j)
dtype      = <class 'numpy.clongdouble'>
s          = '(inf+1j)'

numpy\_core\tests\test_print.py:99: AssertionError
__________test_complex_type_print[complex128] __________

tp = <class 'numpy.complex128'>

    @pytest.mark.parametrize('tp', [np.complex64, np.cdouble, np.clongdouble])
    def test_complex_type_print(tp):
        """Check formatting when using print """
        # We do not create complex with inf/nan directly because the feature is
        # missing in python < 2.6
        for x in [0, 1, -1, 1e20]:
            _test_redirected_print(complex(x), tp)

        if tp(1e16).itemsize > 8:
            _test_redirected_print(complex(1e16), tp)
        else:
            ref = '(1e+16+0j)'
            _test_redirected_print(complex(1e16), tp, ref)

>       _test_redirected_print(complex(np.inf, 1), tp, '(inf+1j)')

tp         = <class 'numpy.complex128'>
x          = 1e+20

numpy\_core\tests\test_print.py:152:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

x = (inf+1j), tp = <class 'numpy.complex128'>, ref = '(inf+1j)'

    def _test_redirected_print(x, tp, ref=None):
        file = StringIO()
        file_tp = StringIO()
        stdout = sys.stdout
        try:
            sys.stdout = file_tp
            print(tp(x))
            sys.stdout = file
            if ref:
                print(ref)
            else:
                print(x)
        finally:
            sys.stdout = stdout

>       assert_equal(file.getvalue(), file_tp.getvalue(),
                     err_msg='print failed for type%s' % tp)
E       AssertionError:
E       Items are not equal: print failed for type<class 'numpy.complex128'>
E        ACTUAL: '(inf+1j)\n'
E        DESIRED: '(inf+0j)\n'

file       = <_io.StringIO object at 0x000001FEED9B0640>
file_tp    = <_io.StringIO object at 0x000001FEED9B0280>
ref        = '(inf+1j)'
stdout     = <_io.TextIOWrapper name='<tempfile._TemporaryFileWrapper object at 0x000001FEEAA7FA40>' mode='r+' encoding='utf-8'>
tp         = <class 'numpy.complex128'>
x          = (inf+1j)

numpy\_core\tests\test_print.py:118: AssertionError
__________ test_complex_type_print[clongdouble] __________

tp = <class 'numpy.clongdouble'>

    @pytest.mark.parametrize('tp', [np.complex64, np.cdouble, np.clongdouble])
    def test_complex_type_print(tp):
        """Check formatting when using print """
        # We do not create complex with inf/nan directly because the feature is
        # missing in python < 2.6
        for x in [0, 1, -1, 1e20]:
            _test_redirected_print(complex(x), tp)

        if tp(1e16).itemsize > 8:
            _test_redirected_print(complex(1e16), tp)
        else:
            ref = '(1e+16+0j)'
            _test_redirected_print(complex(1e16), tp, ref)

>       _test_redirected_print(complex(np.inf, 1), tp, '(inf+1j)')

tp         = <class 'numpy.clongdouble'>
x          = 1e+20

numpy\_core\tests\test_print.py:152:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

x = (inf+1j), tp = <class 'numpy.clongdouble'>, ref = '(inf+1j)'

    def _test_redirected_print(x, tp, ref=None):
        file = StringIO()
        file_tp = StringIO()
        stdout = sys.stdout
        try:
            sys.stdout = file_tp
            print(tp(x))
            sys.stdout = file
            if ref:
                print(ref)
            else:
                print(x)
        finally:
            sys.stdout = stdout

>       assert_equal(file.getvalue(), file_tp.getvalue(),
                     err_msg='print failed for type%s' % tp)
E       AssertionError:
E       Items are not equal: print failed for type<class 'numpy.clongdouble'>
E        ACTUAL: '(inf+1j)\n'
E        DESIRED: '(inf+0j)\n'

file       = <_io.StringIO object at 0x000001FEED9B0C40>
file_tp    = <_io.StringIO object at 0x000001FEED9B0B80>
ref        = '(inf+1j)'
stdout     = <_io.TextIOWrapper name='<tempfile._TemporaryFileWrapper object at 0x000001FEEAA7FA40>' mode='r+' encoding='utf-8'>
tp         = <class 'numpy.clongdouble'>
x          = (inf+1j)

numpy\_core\tests\test_print.py:118: AssertionError

Python and NumPy Versions:

2.0.0.dev0+git20240118.a7c6be5
3.12.1 (tags/v3.12.1:2305ca5, Dec 7 2023, 22:12:47) [MSC v.1937 64 bit (ARM64)]

Runtime Environment:

[{'numpy_version': '2.0.0.dev0+git20240118.a7c6be5',
  'python': '3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:12:47) [MSC v.1937 '
            '64 bit (ARM64)]',
  'uname': uname_result(system='Windows', node='mars', release='11', version='10.0.22621', machine='ARM64')},
 {'simd_extensions': {'baseline': ['NEON', 'NEON_FP16', 'NEON_VFPV4', 'ASIMD'],
                      'found': [],
                      'not_found': []}}]
None

(PS, this function prints already, so you can remove print from the PR template.)

Context for the issue:

I've started working on building Matplotlib on Windows on Arm.

@QuLogic
Copy link
Contributor Author

QuLogic commented Jan 19, 2024

Missed that numpy/polynomial/tests/test_printing.py::test_complex_coefficients also fails the same way:

__________ test_complex_coefficients __________

    def test_complex_coefficients():
        """Test both numpy and built-in complex."""
        coefs = [0+1j, 1+1j, -2+2j, 3+0j]
        # numpy complex
        p1 = poly.Polynomial(coefs)
        # Python complex
        p2 = poly.Polynomial(array(coefs, dtype=object))
        poly.set_default_printstyle('unicode')
>       assert_equal(str(p1), "1j + (1+1j)·x - (2-2j)·x² + (3+0j)·x³")
E       AssertionError:
E       Items are not equal:
E        ACTUAL: '1j + (1+0j)·x - (2+0j)·x² + (3+0j)·x³'
E        DESIRED: '1j + (1+1j)·x - (2-2j)·x² + (3+0j)·x³'

coefs      = [1j, (1+1j), (-2+2j), (3+0j)]
p1         = Polynomial([ 0.+1.j,  1.+1.j, -2.+2.j,  3.+0.j], domain=[-1,  1], window=[-1,  1], symbol='x')
p2         = Polynomial([1j, (1+1j), (-2+2j), (3+0j)], dtype=object, domain=[-1,  1], window=[-1,  1], symbol='x')

numpy\polynomial\tests\test_printing.py:251: AssertionError

@QuLogic
Copy link
Contributor Author

QuLogic commented Jan 19, 2024

Oops, missed one more in numpy/_core/tests/test_arrayprint.py::TestPrintOptions::test_legacy_mode_scalars, but it's more of the same:

__________ TestPrintOptions.test_legacy_mode_scalars __________

self = <numpy._core.tests.test_arrayprint.TestPrintOptions object at 0x0000027CF6D3B1D0>

    def test_legacy_mode_scalars(self):
        # in legacy mode, str of floats get truncated, and complex scalars
        # use * for non-finite imaginary part
        np.set_printoptions(legacy='1.13')
        assert_equal(str(np.float64(1.123456789123456789)), '1.12345678912')
        assert_equal(str(np.complex128(complex(1, np.nan))), '(1+nan*j)')

        np.set_printoptions(legacy=False)
        assert_equal(str(np.float64(1.123456789123456789)),
                     '1.1234567891234568')
>       assert_equal(str(np.complex128(complex(1, np.nan))), '(1+nanj)')
E       AssertionError:
E       Items are not equal:
E        ACTUAL: '(1+0j)'
E        DESIRED: '(1+nanj)'

self       = <numpy._core.tests.test_arrayprint.TestPrintOptions object at 0x0000027CF6D3B1D0>

numpy\_core\tests\test_arrayprint.py:895: AssertionError

@QuLogic
Copy link
Contributor Author

QuLogic commented Jan 19, 2024

Or even more simply:

$ python
Python 3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:12:47) [MSC v.1937 64 bit (ARM64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> np.complex64(1, 2)
np.complex64(1+2j)
>>> np.complex128(1, 2)
np.complex128(1+0j)

even though:

>>> val = np.complex64(1, 2)
>>> val.real
np.float32(1.0)
>>> val.imag
np.float32(2.0)
>>> val = np.complex128(1, 2)
>>> val.real
np.float64(1.0)
>>> val.imag
np.float64(2.0)
>>>

so this is specifically about printing.

@ngoldbaum
Copy link
Member

I did some printf debugging on a arm64 windows VM and I don't think this is necessarily printing specific. In particular, if I put a printf inside npy_cimag like so:

diff --git a/numpy/_core/include/numpy/npy_math.h b/numpy/_core/include/numpy/npy_math.h
index 216b173fde..c532eb963b 100644
--- a/numpy/_core/include/numpy/npy_math.h
+++ b/numpy/_core/include/numpy/npy_math.h
@@ -372,6 +372,7 @@ static inline void npy_csetreal(npy_cdouble *z, const double r)

 static inline double npy_cimag(const npy_cdouble z)
 {
+    printf("z: %f, %f", ((double *) &z)[0], ((double *) &z)[1]);
     return ((double *) &z)[1];
 }

I see in my terminal output when I execute np.complex128(1, 2) that the imagingary part is zero in the npy_cdouble struct.

It looks like val.imag uses a different code path via gentype_imag_get that interprets the data in the imaginary part as a float and goes through the float scalar repr machinery and somehow that ends up with the correct value.

This is reaching the edge of my windows knowledge.

@ngoldbaum
Copy link
Member

Ping @lysnikolaou, maybe this is related to the complex number refactoring?

@hydroo
Copy link

hydroo commented Feb 20, 2024

This maybe related to issues with x64-translation.
I noticed that on WoA pip tries to compile numpy for me.
Perhaps the OP is running an x64 version.

I just opened #25858 which is somewhat related.

@QuLogic
Copy link
Contributor Author

QuLogic commented Feb 21, 2024

No, I am running entirely native only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants