add xlib.avecl

This commit is contained in:
iperov 2021-09-30 18:21:30 +04:00
commit 0058474da7
56 changed files with 5569 additions and 0 deletions

View file

@ -0,0 +1,74 @@
from typing import List
import numpy as np
from ..AShape import AShape
from ..AAxes import AAxes
class BroadcastInfo:
"""
Broadcast info for multi shapes.
arguments
shapes List[AShape]
errors during the construction:
ValueError
Example:
shapes = ( 3,),
(3, 1,)
#Example | comment
o_shape #(3, 3) broadcasted together o_shape
br_shapes #(1, 3) shape[0] broadcasted to o_shape ndim
#(3, 1) shape[1] broadcasted to o_shape ndim
shapes_tiles #(3, 1) tiles to make o_shape from br_shapes[0]
#(1, 3) tiles to make o_shape from br_shapes[1]
shapes_reduction_axes #(0,) reduction_axes to make shapes[0] from o_shape
#(1,) reduction_axes to make shapes[1] from o_shape
"""
__slots__ = ['o_shape', 'br_shapes', 'shapes_tiles', 'shapes_reduction_axes']
def __init__(self, shapes : List[AShape]):
highest_rank = sorted([shape.ndim for shape in shapes])[-1]
# Broadcasted all shapes to highest ndim with (1,)-s in from left side
br_shapes = [ (1,)*(highest_rank-shape.ndim) + shape for shape in shapes ]
# Determine o_shape from all shapes
o_shape = AShape( np.max( [ [ br_shape.shape[axis] for br_shape in br_shapes] for axis in range(highest_rank) ], axis=1 ) )
shapes_tiles = []
shapes_reduction_axes = []
for br_shape in br_shapes:
shape_tile = []
shape_reduction_axes = []
for axis, (x,y) in enumerate(zip(br_shape.shape, o_shape.shape)):
if x != y:
if x == 1 and y != 1:
shape_tile.append(y)
shape_reduction_axes.append(axis)
elif x != 1 and y == 1:
shape_tile.append(1)
else:
raise ValueError(f'operands could not be broadcast together with shapes {br_shape} {o_shape}')
else:
shape_tile.append(1)
shapes_tiles.append(shape_tile)
shapes_reduction_axes.append( AAxes(shape_reduction_axes) )
self.o_shape : AShape = AShape(o_shape)
self.br_shapes : List[AShape] = br_shapes
self.shapes_tiles : List[List] = shapes_tiles
self.shapes_reduction_axes : List [AAxes] = shapes_reduction_axes

View file

@ -0,0 +1,62 @@
from ..AShape import AShape
class ConcatInfo:
__slots__ = ['o_shape', 'axis', 'axis_sizes', 'axis_offsets']
def __init__(self, shapes, axis):
"""
Concat info
arguments
shapes Iterable of shapes
errors during the construction:
ValueError
result
.o_shape AShape
.axis Int fixed axis argument
.axis_sizes List[Int] axis sizes for every shape in shapes
.axis_offsets List[Int] axis offset in o_shape
for every shape in shapes
"""
shapes = tuple(shapes)
if len(shapes) == 0:
raise ValueError('shapes is empty')
shape = shapes[0]
if axis < 0:
axis = shape.ndim + axis
if axis < 0 or axis >= shape.ndim:
raise ValueError(f'Wrong axis {axis}')
fixed_shapes = [ tuple(a for i,a in enumerate(shape) if i != axis) for shape in shapes ]
req_shape = fixed_shapes[0]
if not all(shape == req_shape for shape in fixed_shapes[1:]):
raise ValueError(f'All shapes must match shape {tuple(a if i != axis else "*" for i,a in enumerate(shape))}')
axis_sizes = [ shape[axis] for shape in shapes ]
axis_offset = 0
axis_offsets = []
for axis_size in axis_sizes:
axis_offsets.append(axis_offset)
axis_offset += axis_size
self.o_shape = AShape( tuple(shape)[0:axis] + (sum(axis_sizes),) + tuple(shape)[axis+1:] )
self.axis = axis
self.axis_sizes = axis_sizes
self.axis_offsets = tuple(axis_offsets)

View file

@ -0,0 +1,78 @@
from collections import Iterable
import math
class Conv2DInfo:
"""
Conv2DInfo
arguments
H, W int axes sizes
KH, KW int kernel sizes
stride int
dilation int
padding 'valid' no padding
'same' output size will be the same
or divided by stride
int padding value for all sides
Iterable of 4 ints
paddings for left,top,right,bottom sides
errors during the construction:
ValueError
result:
.PADL .PADR paddings for W axis
.PADT .PADB paddings for H axis
.OH .OW result axes
.OH_T .OW_T result transposed axes.
it is None if padding != 'valid','same'
"""
__slots__ = ['PADL', 'PADR', 'PADT', 'PADB', 'OH', 'OW', 'OH_T', 'OW_T']
def __init__(self, H, W, KH, KW, stride, dilation, padding):
# Effective kernel sizes with dilation
EKH = (KH-1)*dilation + 1
EKW = (KW-1)*dilation + 1
# Determine pad size of sides
OH_T = OW_T = None
if padding == 'valid':
PADL = PADT = PADR = PADB = 0
OH_T = H * stride + max(EKH - stride, 0)
OW_T = W * stride + max(EKW - stride, 0)
elif padding == 'same':
PADL = int(math.floor((EKW - 1)/2))
PADT = int(math.floor((EKH - 1)/2))
PADR = int(math.ceil((EKW - 1)/2))
PADB = int(math.ceil((EKH - 1)/2))
OH_T = H * stride
OW_T = W * stride
elif isinstance(padding, int):
PADL = PADT = PADR = PADB = padding
elif isinstance(padding, Iterable):
padding = tuple(int(x) for x in padding)
if len(padding) != 4:
raise ValueError("Invalid paddings list length.")
PADL, PADT, PADR, PADB = padding
else:
raise ValueError("Invalid padding value.")
self.PADL = PADL
self.PADT = PADT
self.PADR = PADR
self.PADB = PADB
self.OH = max(1, int((H + PADT + PADB - EKH) / stride + 1) )
self.OW = max(1, int((W + PADL + PADR - EKW) / stride + 1) )
self.OH_T = OH_T
self.OW_T = OW_T

View file

@ -0,0 +1,56 @@
from typing import List
import numpy as np
from ..AShape import AShape
class PadInfo:
"""
Pad info.
arguments
shape AShape
axes_paddings list of (l_pad, r_pad)
if [0] == ... (Ellipsis), then left-side paddings will be filled with (0,0) for remain axes
if [-1] == ... , same for ride-side
errors during the construction:
ValueError
result:
.o_shape AShape
"""
__slots__ = ['o_shape','axes_paddings']
def __init__(self, shape, axes_paddings : List):
if Ellipsis in axes_paddings:
if sum(1 if x == Ellipsis else 0 for x in axes_paddings) > 1:
raise ValueError('only 1 ...(ellipsis) allowed in axes_paddings')
if axes_paddings[0] == Ellipsis:
axes_paddings = ((0,0),)*(shape.ndim-(len(axes_paddings)-1))+ axes_paddings[1:]
elif axes_paddings[-1] == Ellipsis:
axes_paddings = axes_paddings[:-1] + ((0,0),)*(shape.ndim-(len(axes_paddings)-1))
else:
raise ValueError('...(ellipsis) must be at the begin or the end of axes_paddings')
if len(axes_paddings) != shape.ndim:
raise ValueError(f'axes_paddings should match shape.ndim {shape.ndim}')
self.axes_paddings = axes_paddings
o_shape = []
for axis, (axis_size, (l_pad, r_pad)) in enumerate(zip(shape, axes_paddings)):
new_axis_size = l_pad + axis_size + r_pad
o_shape.append(new_axis_size)
self.o_shape = AShape(o_shape)

View file

@ -0,0 +1,50 @@
from ..AShape import AShape
class ReductionInfo:
"""
Reduction info
arguments
shape AShape
axes AAxes
keepdims bool
can raise ValueError, TypeError during the construction
"""
__slots__ = [
'reduction_axes', # sorted reduction AAxes
'o_axes', # remain AAxes after reduction
'o_shape', # result AShape of reduction
'o_shape_kd', # result AShape of reduction with keepdims
]
def __init__(self, shape, axes, keepdims):
shape_axes = shape.axes_arange()
if axes.is_none_axes():
axes = shape_axes
# Check correctness of axes
for axis in axes:
if axis not in shape_axes:
raise ValueError(f'Wrong axis {axis} not in {shape_axes}')
self.reduction_axes = reduction_axes = axes.sorted()
# Output axes. Remove axes from shape_axes
self.o_axes = o_axes = shape_axes - axes
if o_axes.is_none_axes():
o_shape = AShape( (1,) )
else:
o_shape = shape[o_axes]
self.o_shape = o_shape
self.o_shape_kd = AShape( 1 if axis in reduction_axes else shape[axis] for axis in range(shape.ndim))
if keepdims:
self.o_shape = self.o_shape_kd

View file

@ -0,0 +1,44 @@
from ..AShape import AShape
class ReshapeInfo:
"""
Reshape info.
can raise ValueError,TypeError during the construction
arguments
shape AShape
target_shape Iterable of ints
can be any len and contains only one '-1'
Example
shape (2, 512, 8, 8, 64)
target_shape (2, 512, -1)
o_shape (2, 512, 4096)
"""
__slots__ = ['o_shape']
def __init__(self, shape, target_shape):
o_shape = []
remain_size = shape.size
unk_axis = None
for t_size in target_shape:
t_size = int(t_size)
if t_size != -1:
mod = remain_size % t_size
if mod != 0:
raise ValueError(f'Cannot reshape {shape} to {target_shape}.')
remain_size /= t_size
else:
if unk_axis is not None:
raise ValueError('Can specify only one unknown dimension.')
unk_axis = len(o_shape)
o_shape.append( t_size )
if unk_axis is not None:
o_shape[unk_axis] = int(remain_size)
self.o_shape = AShape(o_shape)

View file

@ -0,0 +1,148 @@
import math
import numpy as np
from ..AShape import AShape
class SliceInfo:
__slots__ = ['o_shape', 'o_shape_kd', 'just_reshaped','axes_bes','axes_abs_bes']
def __init__(self, shape : AShape, slices):
"""
Slice info.
can raise ValueError,TypeError during the construction
arguments
slices
Example
o_shape result shape after slice
axes_bes list of (begin,step,end) per axis
if s==0, then single axis element is fetched from position b
s can be negative.
"""
# Validate slices argument for given shape.
new_slices = []
before_ellipsis = None
for s in slices:
if s is Ellipsis:
before_ellipsis = new_slices
new_slices = []
continue
elif s is not None and not isinstance(s, (int,tuple) ):
raise ValueError(f'unknown slice argument {s} of type {s.__class__}')
new_slices.append(s)
if before_ellipsis is not None:
# Process Ellipsis separator
new_slices_n_axes = sum([ 1 for x in new_slices if x != None])
before_ellipsis_n_axes = sum([ 1 for x in before_ellipsis if x != None])
# Expand slices by filling intermediate (None,None,None) for each remaining axis
new_slices = before_ellipsis + \
[(None,None,None)]*max(0, shape.ndim-before_ellipsis_n_axes-new_slices_n_axes) + \
new_slices
new_slices_n_axes = sum([ 1 for x in new_slices if x != None])
if new_slices_n_axes > shape.ndim:
raise ValueError('slices arguments more than shape axes')
elif new_slices_n_axes < shape.ndim:
# Fill remaining axes
new_slices += [(None,None,None)]*( shape.ndim - new_slices_n_axes )
slices = tuple(new_slices)
# Compute shapes
output_is_reshaped = True # Flag determines that output_tensor
# can be just reshaped without any computation
o_shape = [] # output tensor shape
o_shape_kd = [] # output shape used in kernel, must match input shape
axes_bes = []
axes_abs_bes = []
i_axis = 0
# Process slices arguments
for v in slices:
if v is None:
# None is new axis
# We can add unlimited number of (1,) axes at any place of shape
o_shape.append(1)
continue
i_axis_size = shape[i_axis]
i_axis += 1
if isinstance(v, int):
if v < 0:
v += i_axis_size
if v < 0 or v >= i_axis_size:
raise ValueError(f'index {v} is out of bounds for axis {i_axis} with size {i_axis_size}')
b,e,s = v,v,0
else:
b,e,s = v
if s == 0:
raise ValueError(f'slice step cannot be zero')
# Fix begin, end, step values
if s is None:
s = 1
if b is None:
b = 0 if s >= 0 else i_axis_size-1
if e is None:
e = i_axis_size if s >= 0 else -1
elif e < 0:
e += i_axis_size
if b < 0:
b += i_axis_size
if s >= 0:
b = np.clip(b, 0, i_axis_size)
e = np.clip(e, 0, i_axis_size)
if b > e:
raise ValueError('for positive step, begin cannot be > end.')
abs_b, abs_e, abs_s = b,e,s
else:
b = np.clip(b, 0, i_axis_size-1)
e = np.clip(e, -1, i_axis_size)
if b <= e:
raise ValueError('for negative step, begin cannot be <= end.')
abs_s = -s
abs_e = b + 1
abs_b = b - (math.ceil( (b-e) / abs_s ) -1) * abs_s
# for every o_shape_kd axis
# we have exact begin,step values to fetch value from input
axes_bes.append( (b,e,s))
axes_abs_bes.append( (abs_b, abs_e, abs_s))
if i_axis_size != 1 and not (b == 0 and e == i_axis_size and s == 1):
# Such params of axis slice will change input, thus output cannot be as just reshaped input
output_is_reshaped = False
# Compute output_axis_size based on begin,end,step
o_axis_size = max(0, math.ceil ( (e-b) / (s if s != 0 else 1) ) )
if o_axis_size >= 1:
# >= 1 : select range of indexes, axis will remain
o_shape.append(o_axis_size)
# ^ othwerwise axis will be supressed
# o_shape with keepdims, must match ndim of input shape
o_shape_kd.append( max(1,o_axis_size) )
self.just_reshaped = output_is_reshaped
self.o_shape = AShape(o_shape)
self.o_shape_kd = AShape(o_shape_kd)
self.axes_bes = axes_bes
self.axes_abs_bes = axes_abs_bes

View file

@ -0,0 +1,37 @@
from ..AShape import AShape
class StackInfo:
__slots__ = ['o_shape', 'axis']
def __init__(self, shape, axis, stack_count):
"""
Stack info
arguments
shape AShape
axis Int
stack_count Int
errors during the construction:
ValueError
result:
.o_shape AShape
.axis Int positive axis argument
"""
if axis < 0:
axis = shape.ndim + 1 + axis
if axis < 0 or axis > shape.ndim:
raise ValueError(f'Wrong axis {axis}')
if stack_count <= 0:
raise ValueError(f'Invalid stack_count {stack_count}')
self.o_shape = AShape( tuple(shape)[0:axis] + (stack_count,) + tuple(shape)[axis:] )
self.axis = axis

View file

@ -0,0 +1,52 @@
import numpy as np
from ..AShape import AShape, AShape
class TileInfo:
"""
Tile info.
arguments
shape AShape
tiles Iterable of ints
errors during the construction:
ValueError
result:
.o_shape AShape
.axes_slices list of slice() to fetch original shape
from o_shape for each tile
"""
__slots__ = ['o_shape', 'axes_slices']
def __init__(self, shape, tiles):
if len(tiles) != shape.ndim:
raise ValueError(f'tiles should match shape.ndim {shape.ndim}')
self.o_shape = AShape(dim*tiles[i] for i,dim in enumerate(shape))
c = [0]*shape.ndim
axes_offsets = []
for n in range(np.prod(tiles)):
axes_offsets.append( c.copy() )
for i in range(shape.ndim-1,-1,-1):
c[i] += 1
if c[i] < tiles[i]:
break
c[i] = 0
axes_slices = []
for axes_offset in axes_offsets:
sl = []
for axis,tile in enumerate(axes_offset):
axis_size = shape[axis]
sl.append( slice(axis_size*tile, axis_size*(tile+1)) )
axes_slices.append(tuple(sl))
self.axes_slices = tuple(axes_slices)

View file

@ -0,0 +1,37 @@
from ..AShape import AShape
from ..AAxes import AAxes
class TransposeInfo:
"""
TransposeInfo
arguments
shape AShape
axes_order AAxes
errors during the construction:
ValueError
result
.o_shape AShape
.no_changes bool transpose changes nothing
"""
__slots__ = ['no_changes', 'o_shape']
def __init__(self, shape : AShape, axes_order : AAxes):
if shape.ndim != axes_order.ndim:
raise ValueError('axes must match the shape')
# Axes order changes nothing?
self.o_shape = shape[axes_order]
self.no_changes = axes_order == shape.axes_arange()

View file

@ -0,0 +1,10 @@
from .BroadcastInfo import BroadcastInfo
from .ConcatInfo import ConcatInfo
from .PadInfo import PadInfo
from .ReductionInfo import ReductionInfo
from .ReshapeInfo import ReshapeInfo
from .SliceInfo import SliceInfo
from .StackInfo import StackInfo
from .TileInfo import TileInfo
from .TransposeInfo import TransposeInfo
from .Conv2DInfo import Conv2DInfo