Source code for delphi.translators.for2py.arrays

"""
    File: for2py_arrays.py
    Purpose: Code to handle array manipulation in the Python code generated
    by for2py.

    Usage: see the document "for2py: Handling Fortran Arrays"
"""

import copy
import itertools
import sys
from . import For2PyError

_GET_ = 0
_SET_ = 1

################################################################################
#                                                                              #
#                                 Array objects                                #
#                                                                              #
################################################################################


[docs]class Array: """ bounds is a list [(lo1,hi1), (lo2,hi2), ..., (loN, hiN)] of pairs of lower and upper bounds for the dimensions of the array. The length of the list bounds gives the number of dimensions of the array. """ def __init__(self, types, bounds): self._bounds = bounds self._types = types self._values = self._mk_uninit_array(bounds) def _mk_uninit_array(self, bounds): """ given a list of bounds for the N dimensions of an array, _mk_uninit_array() creates and returns an N-dimensional array of the size specified by the bounds with each element set to the value None.""" if len(bounds) == 0: raise For2PyError("Zero-length arrays current not handled!.") this_dim = bounds[0] lo, hi = this_dim[0], this_dim[1] sz = hi-lo+1 if len(bounds) == 1: return [None] * sz sub_array = self._mk_uninit_array(bounds[1:]) this_array = [copy.deepcopy(sub_array) for _ in range(sz)] return this_array
[docs] def bounds(self): """bounds() returns a list of pairs (lo,hi) giving the lower and upper bounds of the array.""" return self._bounds
[docs] def lower_bd(self, i): """lower_bd(i) returns the lower bound of the array in dimension i. Dimensions are numbered from 0 up.""" this_dim_bounds = self._bounds[i] return this_dim_bounds[0]
[docs] def upper_bd(self, i): """upper_bd(i) returns the upper bound of the array in dimension i. Dimensions are numbered from 0 up.""" this_dim_bounds = self._bounds[i] return this_dim_bounds[1]
def _posn(self, bounds, idx): """given bounds = (lo,hi) and an index value idx, _posn(bounds, idx) returns the position in a 0-based array corresponding to idx in the (lo,hi)-based array. It generates an error if idx < lo or idx > hi.""" lo, hi = bounds[0], bounds[1] assert lo <= idx <= hi, f"Array index {idx} out of bounds: {bounds}\n" return idx-lo def _access(self, subs, acc_type, val): """_access(subs, acc_type, val) accesses the array element specified by the tuple of subscript values, subs. If acc_type == _GET_ it returns the value of this element; else it sets this element to the value of the argument val.""" if isinstance(subs, int): # if subs is just an integer, take it to be an index value. subs = (subs,) if len(subs) == 0: raise For2PyError("Zero-length arrays currently not handled.") bounds = self._bounds sub_arr = self._values ndims = len(subs) for i in range(ndims): this_pos = self._posn(bounds[i], subs[i]) if i == ndims-1: if acc_type == _GET_: return sub_arr[this_pos] else: sub_arr[this_pos] = val else: sub_arr = sub_arr[this_pos]
[docs] def set_(self, subs, val): """set_() sets the value of the array element specified by the given tuple of array subscript values to the argument val.""" self._access(subs, _SET_, val)
[docs] def get_(self, subs): """get_() returns the value of the array element specified by the given tuple of array subscript values.""" return self._access(subs, _GET_, None)
[docs] def get_elems(self, subs_list): """get_elems(subs_list) returns a list of values of the array elements specified by the list of subscript values subs_list (each element of subs_list is a tuple of subscripts identifying an array element).""" return [self.get_(subs) for subs in subs_list]
[docs] def set_elems(self, subs, vals): """set_elems(subs, vals) sets the array elements specified by the list of subscript values subs (each element of subs is a tuple of subscripts identifying an array element) to the corresponding value in vals.""" if isinstance(vals, (int, float)): # if vals is a scalar, extend it to a list of appropriate length vals = [vals] * len(subs) for i in range(len(subs)): self.set_(subs[i], vals[i])
################################################################################ # # # Functions for accessing parts of arrays # # # ################################################################################
[docs]def all_subs(bounds): """given a list of tuples specifying the bounds of an array, all_subs() returns a list of all the tuples of subscripts for that array.""" idx_list = [] for i in range(len(bounds)): this_dim = bounds[i] lo, hi = this_dim[0], this_dim[1] # bounds for this dimension this_dim_idxs = range(lo, hi+1) # indexes for this dimension idx_list.append(this_dim_idxs) return idx2subs(idx_list)
[docs]def idx2subs(idx_list): """Given a list idx_list of index values for each dimension of an array, idx2subs() returns a list of the tuples of subscripts for all of the array elements specified by those index values. Note: This code adapted from that posted by jfs at https://stackoverflow.com/questions/533905/get-the-cartesian-product- of-a-series-of-lists""" if not idx_list: return [()] return [items + (item,) for items in idx2subs(idx_list[:-1]) for item in idx_list[-1]]
[docs]def array_values(expr): """Given an expression expr denoting a list of values, array_values(expr) returns a list of values for that expression.""" if isinstance(expr, Array): return expr.get_elems(all_subs(expr._bounds)) elif isinstance(expr, list): vals = [array_values(x) for x in expr] return flatten(vals) else: return [expr]
[docs]def array_subscripts(expr): """Given a subscript expression expr (i.e., an expression that denotes the set of elements of some array that are to be accessed), array_subscripts() returns a list of the elements denoted by expr.""" if isinstance(expr, Array): return all_subs(expr._bounds) elif isinstance(expr, list): subs = [subscripts(x) for x in expr] return flatten(subs) else: return [expr]
################################################################################ # # # Assorted utilities # # # ################################################################################
[docs]def flatten(in_list): """given a list of values in_list, flatten returns the list obtained by flattening the top-level elements of in_list.""" out_list = [] for val in in_list: if isinstance(val, list): out_list.extend(val) else: out_list.append(val) return out_list
[docs]def implied_loop_expr(expr, start, end, delta): """given the parameters of an implied loop -- namely, the start and end values together with the delta per iteration -- implied_loop_expr() returns a list of values of the lambda expression expr applied to successive values of the implied loop.""" if delta > 0: stop = end+1 else: stop = end-1 result_list = [expr(x) for x in range(start, stop, delta)] # return the flattened list of results return list(itertools.chain(result_list))