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

Improve np.array implementation #1256

Merged
merged 27 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2747bfa
order check before transposing
jalalium Dec 13, 2022
0860fd7
Add tests which fail on devel
EmilyBourne Aug 14, 2023
c197ae5
Fix #1496
EmilyBourne Aug 14, 2023
bcc04e8
Allow passing a single variable argument
EmilyBourne Aug 14, 2023
77d02b8
Simplify function
EmilyBourne Aug 14, 2023
69f351b
Update CHANGELOG
EmilyBourne Aug 15, 2023
c42acd7
Update CHANGELOG
EmilyBourne Aug 15, 2023
42da6af
Add docstring. Correct default order. Add support for ndmin parameter
EmilyBourne Aug 15, 2023
f8212bc
Add tests for default order. Remove unnecessary xfails
EmilyBourne Aug 15, 2023
cec45eb
Correct shape and order
EmilyBourne Aug 15, 2023
784c8b8
Print ndmin
EmilyBourne Aug 15, 2023
81f0397
Add test for ndmin parameter. Check array results more precisely. Use…
EmilyBourne Aug 15, 2023
55b5a92
Handle mis-matched ranks
EmilyBourne Aug 15, 2023
4b3357c
Put back skip. Add issue reference. Correct type check for specific e…
EmilyBourne Aug 15, 2023
884c391
Don't check type of argument
EmilyBourne Aug 15, 2023
b5aca7c
Put back assert
EmilyBourne Aug 15, 2023
8bf4986
Reference issue
EmilyBourne Aug 15, 2023
17e6e84
Increase coverage
EmilyBourne Aug 15, 2023
14fc162
Deactivate failing test with clearer message
EmilyBourne Aug 15, 2023
a922314
Fix windows precision
EmilyBourne Aug 15, 2023
a19564f
Correct dtype
EmilyBourne Aug 15, 2023
beed927
Merge branch 'devel' into issue1241
yguclu Aug 23, 2023
aefc3f3
Add missing information to CHANGELOG.md
yguclu Aug 23, 2023
9684165
Remove unnecessary white space from CHANGELOG.md
yguclu Aug 23, 2023
627557f
Update tests/epyccel/test_epyccel_return_arrays.py
EmilyBourne Aug 23, 2023
7527f94
Update pyccel/ast/numpyext.py
EmilyBourne Aug 23, 2023
dfd00bf
Update tests/epyccel/test_epyccel_return_arrays.py
EmilyBourne Aug 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.

### Added

### Fixed

- #1499 : Fix passing temporary arrays to functions.
- #1241 : Missing transpose when converting from a C-ordered array to F-ordered array.
- #1241 : Incorrect transpose when copying an F-ordered array.
- #1241 : Fix infinite loop when passing an array as the only argument to `np.array`.

### Changed

### Deprecated

## \[1.9.0\] - 2023-08-22

### Added

- #752 : Allow passing array variables to `numpy.array`.
- #1280 : Allow copying arrays using `numpy.array`.
- Allow interfaces in classes.
Expand All @@ -18,8 +33,8 @@ All notable changes to this project will be documented in this file.

- #682 : Wrong data layout when copying a slice of an array.
- #1453 : Fix error-level developer mode output.
- #1499 : Fix passing temporary arrays to functions.
- \[INTERNALS\] Fix string base class selection.
- #1496 : Fix interfaces which differ only by order or rank.

### Changed

Expand Down
31 changes: 22 additions & 9 deletions pyccel/ast/numpyext.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,12 +502,9 @@ def _process_order(rank, order):
return order

#==============================================================================
# TODO [YG, 18.02.2020]: accept Numpy array argument
# TODO [YG, 18.02.2020]: use order='K' as default, like in numpy.array
# TODO [YG, 22.05.2020]: move dtype & prec processing to __init__
class NumpyArray(NumpyNewArray):
"""
Represents a call to numpy.array for code generation.
Represents a call to `numpy.array` for code generation.

A class representing a call to the NumPy `array` function.

Expand All @@ -521,12 +518,16 @@ class NumpyArray(NumpyNewArray):

order : str
The ordering of the array (C/Fortran).

ndmin : LiteralInteger, int, optional
The minimum number of dimensions that the resulting array should
have.
"""
__slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order')
_attribute_nodes = ('_arg',)
name = 'array'

def __init__(self, arg, dtype=None, order='C'):
def __init__(self, arg, dtype=None, order='K', ndmin=None):

if not isinstance(arg, (PythonTuple, PythonList, Variable)):
raise TypeError('Unknown type of %s.' % type(arg))
Expand All @@ -538,6 +539,17 @@ def __init__(self, arg, dtype=None, order='C'):
if not (is_homogeneous_tuple or is_array):
raise TypeError('we only accept homogeneous arguments')

if not isinstance(order, (LiteralString, str)):
raise TypeError("The order must be specified explicitly with a string.")
elif isinstance(order, LiteralString):
order = order.python_value

if ndmin is not None:
if not isinstance(ndmin, (LiteralInteger, int)):
raise TypeError("The minimum number of dimensions must be specified explicitly with an integer.")
elif isinstance(ndmin, LiteralInteger):
ndmin = ndmin.python_value

init_dtype = dtype

# Verify dtype and get precision
Expand All @@ -546,12 +558,14 @@ def __init__(self, arg, dtype=None, order='C'):
prec = get_final_precision(arg)
else:
dtype, prec = process_dtype(dtype)
# ... Determine ordering
order = str(order).strip("\'")

shape = process_shape(False, arg.shape)
rank = len(shape)

if ndmin and ndmin>rank:
shape = (LiteralInteger(1),)*(ndmin-rank) + shape
rank = ndmin

if rank < 2:
order = None
else:
Expand All @@ -561,9 +575,8 @@ def __init__(self, arg, dtype=None, order='C'):
if order not in ('K', 'A', 'C', 'F'):
raise ValueError(f"Cannot recognize '{order}' order")

# TODO [YG, 18.02.2020]: set correct order based on input array
if order in ('K', 'A'):
order = 'C'
order = arg.order or 'C'
# ...

self._arg = arg
Expand Down
81 changes: 40 additions & 41 deletions pyccel/codegen/printing/fcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -1075,50 +1075,49 @@ def _print_NumpyWhere(self, expr):
return stmt

def _print_NumpyArray(self, expr):
"""Fortran print."""

expr_args = (expr.arg,) if isinstance(expr.arg, Variable) else expr.arg
order = expr.order
# If Numpy array is stored with column-major ordering, transpose values
# use reshape with order for rank > 2
if expr.order == 'F':
if expr.rank == 2:
rhs_code = self._print(expr.arg)
rhs_code = 'transpose({})'.format(rhs_code)
elif expr.rank > 2:
args = [self._print(a) for a in expr.arg]
new_args = []
for ac, a in zip(args, expr.arg):
if a.order == 'C':
shape = ', '.join(self._print(i) for i in a.shape)
order = ', '.join(self._print(LiteralInteger(i)) for i in range(a.rank, 0, -1))
ac = 'reshape({}, [{}], order=[{}])'.format(ac, shape, order)
new_args.append(ac)

args = new_args
rhs_code = '[' + ' ,'.join(args) + ']'
shape = ', '.join(self._print(i) for i in expr.shape)
order = [LiteralInteger(i) for i in range(1, expr.rank+1)]
order = order[1:]+ order[:1]
order = ', '.join(self._print(i) for i in order)
rhs_code = 'reshape({}, [{}], order=[{}])'.format(rhs_code, shape, order)
elif expr.order == 'C':
if expr.rank > 2:
args = [self._print(a) for a in expr.arg]
new_args = []
for ac, a in zip(args, expr.arg):
if a.order == 'F':
shape = ', '.join(self._print(i) for i in a.shape[::-1])
order = ', '.join(self._print(LiteralInteger(i)) for i in range(a.rank, 0, -1))
ac = 'reshape({}, [{}], order=[{}])'.format(ac, shape, order)
new_args.append(ac)

args = new_args
rhs_code = '[' + ' ,'.join(args) + ']'
shape = ', '.join(self._print(i) for i in expr.shape[::-1])
rhs_code = 'reshape({}, [{}])'.format(rhs_code, shape)
else:
rhs_code = self._print(expr.arg)
elif expr.order is None:
if expr.rank <= 2:
rhs_code = self._print(expr.arg)
if expr.arg.order and expr.arg.order != expr.order:
rhs_code = f'transpose({rhs_code})'
if expr.arg.rank < expr.rank:
if order == 'F':
shape_code = ', '.join(self._print(i) for i in expr.shape)
else:
EmilyBourne marked this conversation as resolved.
Show resolved Hide resolved
shape_code = ', '.join(self._print(i) for i in expr.shape[::-1])
rhs_code = f"reshape({rhs_code}, [{shape_code}])"
else:
new_args = []
inv_order = 'C' if order == 'F' else 'F'
for a in expr_args:
ac = self._print(a)
if a.order == inv_order:
shape = a.shape if a.order == 'C' else a.shape[::-1]
shape_code = ', '.join(self._print(i) for i in shape)
order_code = ', '.join(self._print(LiteralInteger(i)) for i in range(a.rank, 0, -1))
ac = f'reshape({ac}, [{shape_code}], order=[{order_code}])'
new_args.append(ac)

if len(new_args) == 1:
rhs_code = new_args[0]
else:
rhs_code = '[' + ' ,'.join(new_args) + ']'

if len(new_args) != 1 or expr.arg.rank < expr.rank:
if order == 'C':
shape_code = ', '.join(self._print(i) for i in expr.shape[::-1])
rhs_code = f'reshape({rhs_code}, [{shape_code}])'
else:
shape_code = ', '.join(self._print(i) for i in expr.shape)
order_index = [LiteralInteger(i) for i in range(1, expr.rank+1)]
order_index = order_index[1:]+ order_index[:1]
order_code = ', '.join(self._print(i) for i in order_index)
rhs_code = f'reshape({rhs_code}, [{shape_code}], order=[{order_code}])'


return rhs_code

def _print_NumpyFloor(self, expr):
Expand Down
6 changes: 4 additions & 2 deletions pyccel/codegen/printing/pycode.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,11 +704,13 @@ def _print_Deallocate(self, expr):

def _print_NumpyArray(self, expr):
name = self._get_numpy_name(expr)
arg_var = expr.arg

arg = self._print(expr.arg)
arg = self._print(arg_var)
dtype = self._print_dtype_argument(expr, expr.init_dtype)
order = f"order='{expr.order}'" if expr.order else ''
args = ', '.join(a for a in [arg, dtype, order] if a!= '')
ndmin = f"ndmin={expr.rank}" if expr.rank > arg_var.rank else ''
args = ', '.join(a for a in [arg, dtype, order, ndmin] if a!= '')
return f"{name}({args})"

def _print_NumpyAutoFill(self, expr):
Expand Down
28 changes: 28 additions & 0 deletions tests/epyccel/modules/arrays.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,33 @@ def multiple_2d_stack_array_2():
s = s + b[i][j] - a[j] / c[i][j]
return s

#==============================================================================
# TEST: Array with ndmin argument
#==============================================================================
@template('T', ['int[:]', 'int[:,:]', 'int[:,:,:]', 'int[:,:](order=F)', 'int[:,:,:](order=F)'])
def array_ndmin_1(x : 'T'):
from numpy import array
y = array(x, ndmin=1)
return y

@template('T', ['int[:]', 'int[:,:]', 'int[:,:,:]', 'int[:,:](order=F)', 'int[:,:,:](order=F)'])
def array_ndmin_2(x : 'T'):
from numpy import array
y = array(x, ndmin=2)
return y

@template('T', ['int[:]', 'int[:,:]', 'int[:,:,:]', 'int[:,:](order=F)', 'int[:,:,:](order=F)'])
def array_ndmin_4(x : 'T'):
from numpy import array
y = array(x, ndmin=4)
return y

@template('T', ['int[:]', 'int[:,:]', 'int[:,:,:]', 'int[:,:](order=F)', 'int[:,:,:](order=F)'])
def array_ndmin_2_order(x : 'T'):
from numpy import array
y = array(x, ndmin=2, order='F')
return y

#==============================================================================
# TEST: Product and matrix multiplication
#==============================================================================
Expand Down Expand Up @@ -1602,6 +1629,7 @@ def arrs_2d_different_shapes_0():
pm = np.array([[1, 1, 1]])
x = pn + pm
return np.shape(x)[0], np.shape(x)[1]

def arrs_1d_negative_index_1():
import numpy as np
a = np.zeros(10)
Expand Down
Loading