Skip to content

Commit

Permalink
Add support of const fused type memory views (GH-3118) (GH-5076)
Browse files Browse the repository at this point in the history
Backport of #3118
Fixes #3783

Co-authored-by: Thomas VINCENT <thomas.vincent@esrf.fr>
  • Loading branch information
da-woods and t20100 committed Jan 4, 2023
1 parent 60fe731 commit 08e4478
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 12 deletions.
1 change: 1 addition & 0 deletions Cython/Compiler/PyrexTypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1561,6 +1561,7 @@ def __hash__(self):
class CConstType(BaseType):

is_const = 1
subtypes = ['const_base_type']

def __init__(self, const_base_type):
self.const_base_type = const_base_type
Expand Down
34 changes: 24 additions & 10 deletions tests/errors/fused_types.pyx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# mode: error
# ticket: 1772

cimport cython
from cython import fused_type
Expand Down Expand Up @@ -64,17 +65,30 @@ ctypedef fused fused2:
func(x, y)


cdef fused mix_const_t:
int
const int

cdef cdef_func_with_mix_const_type(mix_const_t val):
print(val)

# Mixing const and non-const type makes fused type ambiguous
cdef_func_with_mix_const_type(1)


_ERRORS = u"""
10:15: fused_type does not take keyword arguments
15:33: Type specified multiple times
26:0: Invalid use of fused types, type cannot be specialized
26:4: Not enough types specified to specialize the function, int2_t is still fused
11:15: fused_type does not take keyword arguments
16:33: Type specified multiple times
27:0: Invalid use of fused types, type cannot be specialized
27:4: Not enough types specified to specialize the function, int2_t is still fused
28:16: Call with wrong number of arguments (expected 2, got 1)
29:16: Call with wrong number of arguments (expected 2, got 3)
36:6: Invalid base type for memoryview slice: int *
39:0: Fused lambdas not allowed
42:5: Fused types not allowed here
45:9: Fused types not allowed here
28:0: Invalid use of fused types, type cannot be specialized
28:4: Not enough types specified to specialize the function, int2_t is still fused
29:16: Call with wrong number of arguments (expected 2, got 1)
30:16: Call with wrong number of arguments (expected 2, got 3)
37:6: Invalid base type for memoryview slice: int *
40:0: Fused lambdas not allowed
43:5: Fused types not allowed here
46:9: Fused types not allowed here
76:0: Invalid use of fused types, type cannot be specialized
76:29: ambiguous overloaded method
"""
50 changes: 48 additions & 2 deletions tests/memoryview/numpy_memoryview_readonly.pyx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# mode: run
# tag: readonly, const, numpy
# ticket: 1772

import numpy as np
cimport cython

def new_array():
return np.arange(10).astype('float')
def new_array(dtype='float', writeable=True):
array = np.arange(10, dtype=dtype)
array.setflags(write=writeable)
return array

ARRAY = new_array()

Expand Down Expand Up @@ -124,3 +128,45 @@ def test_copy():
rw[1] = 2
rw2[2] = 2
return rw[0], rw[1], rw[2], rw2[0], rw2[1], rw2[2]


cdef getmax_floating(const cython.floating[:] x):
"""Function with fused type, should work with both ro and rw memoryviews"""
cdef cython.floating max_val = - float('inf')
for val in x:
if val > max_val:
max_val = val
return max_val


def test_mmview_const_fused_cdef():
"""Test cdef function with const fused type memory view as argument.
>>> test_mmview_const_fused_cdef()
"""
cdef float[:] data_rw = new_array(dtype='float32')
assert getmax_floating(data_rw) == 9

cdef const float[:] data_ro = new_array(dtype='float32', writeable=False)
assert getmax_floating(data_ro) == 9


def test_mmview_const_fused_def(const cython.floating[:] x):
"""Test def function with const fused type memory view as argument.
With read-write numpy array:
>>> test_mmview_const_fused_def(new_array('float32', writeable=True))
0.0
>>> test_mmview_const_fused_def(new_array('float64', writeable=True))
0.0
With read-only numpy array:
>>> test_mmview_const_fused_def(new_array('float32', writeable=False))
0.0
>>> test_mmview_const_fused_def(new_array('float64', writeable=False))
0.0
"""
cdef cython.floating result = x[0]
return result
53 changes: 53 additions & 0 deletions tests/run/fused_types.pyx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# mode: run
# ticket: 1772

cimport cython
from cython.view cimport array
Expand Down Expand Up @@ -363,6 +364,22 @@ def test_fused_memslice_dtype_repeated_2(cython.floating[:] array1, cython.float
"""
print cython.typeof(array1), cython.typeof(array2), cython.typeof(array3)

def test_fused_const_memslice_dtype_repeated(const cython.floating[:] array1, cython.floating[:] array2):
"""Test fused types memory view with one being const
>>> sorted(test_fused_const_memslice_dtype_repeated.__signatures__)
['double', 'float']
>>> test_fused_const_memslice_dtype_repeated(get_array(8, 'd'), get_array(8, 'd'))
const double[:] double[:]
>>> test_fused_const_memslice_dtype_repeated(get_array(4, 'f'), get_array(4, 'f'))
const float[:] float[:]
>>> test_fused_const_memslice_dtype_repeated(get_array(8, 'd'), get_array(4, 'f'))
Traceback (most recent call last):
ValueError: Buffer dtype mismatch, expected 'double' but got 'float'
"""
print cython.typeof(array1), cython.typeof(array2)

def test_cython_numeric(cython.numeric arg):
"""
Test to see whether complex numbers have their utility code declared
Expand All @@ -388,6 +405,18 @@ def test_index_fused_args(cython.floating f, ints_t i):
"""
_test_index_fused_args[cython.floating, ints_t](f, i)

cdef _test_index_const_fused_args(const cython.floating f, const ints_t i):
print(cython.typeof(f), cython.typeof(i))

def test_index_const_fused_args(const cython.floating f, const ints_t i):
"""Test indexing function implementation with const fused type args
>>> import cython
>>> test_index_const_fused_args[cython.double, cython.int](2.0, 3)
('const double', 'const int')
"""
_test_index_const_fused_args[cython.floating, ints_t](f, i)


def test_composite(fused_composite x):
"""
Expand All @@ -404,6 +433,30 @@ def test_composite(fused_composite x):
return 2 * x


cdef cdef_func_const_fused_arg(const cython.floating val,
const fused_type1 * ptr_to_const,
const (cython.floating *) const_ptr):
print(val, cython.typeof(val))
print(ptr_to_const[0], cython.typeof(ptr_to_const[0]))
print(const_ptr[0], cython.typeof(const_ptr[0]))

ptr_to_const = NULL # pointer is not const, value is const
const_ptr[0] = 0.0 # pointer is const, value is not const

def test_cdef_func_with_const_fused_arg():
"""Test cdef function with const fused type argument
>>> test_cdef_func_with_const_fused_arg()
(0.0, 'const float')
(1, 'const int')
(2.0, 'float')
"""
cdef float arg0 = 0.0
cdef int arg1 = 1
cdef float arg2 = 2.0
cdef_func_const_fused_arg(arg0, &arg1, &arg2)


### see GH3642 - presence of cdef inside "unrelated" caused a type to be incorrectly inferred
cdef unrelated(cython.floating x):
cdef cython.floating t = 1
Expand Down

0 comments on commit 08e4478

Please sign in to comment.