mirror of
https://github.com/tinygrad/tinygrad.git
synced 2026-08-29 19:56:06 +00:00
reduce removes ones (#16847)
* reduce removes ones * test changes * lil clean
This commit is contained in:
@@ -89,7 +89,7 @@ class TestLocalAmax(unittest.TestCase):
|
||||
x = Tensor.arange(16).reshape(4, 4).cast(dtypes.float).clone(devices[0]).realize().shard(devices, axis=0).realize()
|
||||
GlobalCounters.reset()
|
||||
out = (x * local_abs_max(x)).clone().realize()
|
||||
self.assertEqual(GlobalCounters.kernel_count, 4)
|
||||
self.assertEqual(GlobalCounters.kernel_count, 2)
|
||||
self.assertEqual(out.tolist(), [[0., 7., 14., 21.], [28., 35., 42., 49.], [120., 135., 150., 165.], [180., 195., 210., 225.]])
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -266,7 +266,7 @@ class TestSchedule(unittest.TestCase):
|
||||
x = Tensor.empty(big_enough).realize()
|
||||
with Context(SPLIT_REDUCEOP=1):
|
||||
out = (x - x.max(keepdim=True)).max()
|
||||
check_schedule(out, 4)
|
||||
check_schedule(out, 3)
|
||||
|
||||
def test_example_matmul_contig(self):
|
||||
x = Tensor.eye(64).clone().realize()
|
||||
@@ -355,8 +355,7 @@ class TestSchedule(unittest.TestCase):
|
||||
b = Tensor.empty((1, 16)).realize()
|
||||
out0 = a.sum() + 2
|
||||
out1 = a.sum() + b
|
||||
# check_schedule([out0, out1], 2)
|
||||
check_schedule([out0, out1], 3)
|
||||
check_schedule([out0, out1], 2)
|
||||
|
||||
def test_scaled_dot_product_attention_multireduce_fusion(self):
|
||||
q = Tensor.empty(32,8,16,8).realize()
|
||||
@@ -546,8 +545,7 @@ class TestSchedule(unittest.TestCase):
|
||||
a = Tensor.empty(3, 4, 5).abs().realize()
|
||||
b = Tensor.empty(3, 4, 5).abs().realize()
|
||||
out = (a.log2().pad(((0, 1), (0, 1), (0, 1)), value=1.0).sum()+b).abs().log2().pad(((0, 1), (0, 1), (0, 1)), value=1.0).sum().contiguous()
|
||||
# check_schedule(out, 1)
|
||||
check_schedule(out, 2)
|
||||
check_schedule(out, 1)
|
||||
|
||||
def test_shrink_pad_safe(self):
|
||||
a = Tensor.ones((3, )).contiguous().realize()
|
||||
|
||||
@@ -39,7 +39,7 @@ def simplify_merge_adjacent(u:UOp) -> UOp|None:
|
||||
return u
|
||||
|
||||
def mark_gated(ctx, idx):
|
||||
if idx.src[1].op is Ops.WHERE:
|
||||
if len(idx.src) > 1 and idx.src[1].op is Ops.WHERE:
|
||||
x, cond = idx.src[1].get_idx(), idx.src[1].get_valid()
|
||||
# get all ranges r with guards "r < c" for some const c
|
||||
guards = {r:c for v in cond.split_uop(Ops.AND) if v.op is Ops.CMPLT and (r:=v.src[0]).op is Ops.RANGE and (c:=v.src[1]).op is Ops.CONST}
|
||||
|
||||
@@ -5,7 +5,14 @@ from tinygrad.helpers import argsort
|
||||
from tinygrad.dtype import sum_acc_dtype
|
||||
|
||||
def reduce_gradient(ctx:UOp, ret:UOp, op:Ops):
|
||||
def broadcast_to_input(x): return x.reshape(x.shape+(1,)*(len(ret.src[0].shape)-len(x.shape))).expand(ret.src[0].shape)
|
||||
def broadcast_to_input(x):
|
||||
shape, j = [], 0
|
||||
for i in range(len(ret.src[0].shape)):
|
||||
if i in ret.arg[1]: shape.append(1)
|
||||
else:
|
||||
shape.append(x.shape[j])
|
||||
j += 1
|
||||
return x.reshape(tuple(shape)).expand(ret.src[0].shape)
|
||||
if op == Ops.ADD: return (broadcast_to_input(ctx),)
|
||||
if op == Ops.MAX:
|
||||
assert ret.op is Ops.REDUCE, "only works on REDUCE"
|
||||
@@ -69,7 +76,7 @@ pm_gradient = PatternMatcher([
|
||||
(UPat(Ops.RESHAPE, name="ret"), lambda ctx, ret: (ctx.reshape(ret.src[0].shape), None)),
|
||||
(UPat(Ops.EXPAND, name="ret"), lambda ctx, ret:
|
||||
(ctx.cast(sum_acc_dtype(ctx.dtype))._rop(Ops.ADD, tuple(i for i,(s,n) in enumerate(zip(ret.src[0].shape, ret.shape)) if s!=n))
|
||||
.cast(ctx.dtype), None)),
|
||||
.reshape(ret.src[0].shape).cast(ctx.dtype), None)),
|
||||
(UPat(Ops.PAD, name="ret"), lambda ctx, ret: (ctx.shrink(tuple([(p[0], s+p[0]) for s,p in zip(ret.src[0].shape, ret.marg)])), None, None)),
|
||||
(UPat(Ops.SHRINK, name="ret"), lambda ctx, ret: (ctx.pad(tuple([(p[0], s-p[0]-p[1]) for s,p in zip(ret.src[0].shape, ret.marg)])), None, None)),
|
||||
(UPat(Ops.PERMUTE, name="ret"), lambda ctx, ret: (ctx.permute(argsort(ret.marg)),)),
|
||||
|
||||
@@ -14,7 +14,7 @@ class ReduceMixin(DTypeMixin, MovementMixin):
|
||||
axis = tuple(self._resolve_dim(x) for x in (range(self.ndim) if axis is None else make_tuple(axis, 1)))
|
||||
if self.ndim == 0: axis = ()
|
||||
ret = self._rop(op, axis)
|
||||
return ret if keepdim else ret.reshape(tuple(s for i,s in enumerate(self.shape) if i not in axis))
|
||||
return ret.reshape(tuple(1 if i in axis else s for i,s in enumerate(self.shape))) if keepdim else ret
|
||||
|
||||
def sum(self, axis:int|Sequence[int]|None=None, keepdim=False, dtype:DTypeLike|None=None) -> Self:
|
||||
"""
|
||||
|
||||
@@ -252,7 +252,13 @@ def run_rangeify(tsink:UOp, debug:bool=False) -> tuple[UOp, IndexingContext]:
|
||||
|
||||
# REDUCE creates ranges for the axes it is reducing
|
||||
if x.op is Ops.REDUCE and len(x.arg[1]):
|
||||
rngs = tuple(rctx.new_range(s, axistype=AxisType.REDUCE) if i in x.arg[1] else r for i,(r,s) in enumerate(zip(rngs, x.src[0].shape)))
|
||||
out_i, in_rngs = 0, []
|
||||
for i,s in enumerate(x.src[0].shape):
|
||||
if i in x.arg[1]: in_rngs.append(rctx.new_range(s, axistype=AxisType.REDUCE))
|
||||
else:
|
||||
in_rngs.append(out_rngs[out_i])
|
||||
out_i += 1
|
||||
rngs = tuple(in_rngs)
|
||||
|
||||
if debug:
|
||||
realized_ranges = rctx.realize_map.get(x, None)
|
||||
|
||||
@@ -75,7 +75,8 @@ def reduce_multi(root:UOp, multi:UOp):
|
||||
return local.cast(orig_dtype).allreduce(op, multi.device).cast(local.dtype)
|
||||
return local.allreduce(op, multi.device)
|
||||
# reduce on non sharded axes, piecewise is fine. if axis is None this is also correct
|
||||
return multi.src[0]._rop(op, axis).multi(axis=multi.axis)
|
||||
new_axis = multi.axis - sum(1 for a in axis if a < multi.axis) if multi.axis is not None else None
|
||||
return multi.src[0]._rop(op, axis).multi(axis=new_axis)
|
||||
|
||||
def reshape_multi(root:UOp, multi:UOp):
|
||||
if prod(multi.shape) != prod(new_shape:=root.marg): raise RuntimeError("reshape must maintain prod(shape)")
|
||||
|
||||
+10
-4
@@ -346,7 +346,7 @@ class UOp(RandMixin, metaclass=UOpMetaClass):
|
||||
axis_arg = self.arg[1]
|
||||
if not isinstance(axis_arg, tuple) or not all(isinstance(x, int) and x>=0 and x<len(ps) for x in axis_arg):
|
||||
raise ValueError(f"invalid type for axis: {axis_arg}")
|
||||
return tuple(1 if i in axis_arg else s for i,s in enumerate(ps))
|
||||
return tuple(s for i,s in enumerate(ps) if i not in axis_arg)
|
||||
|
||||
if self.op in GroupOp.Unary.union({Ops.CAST}):
|
||||
assert len(self.src) == 1, "unary ops must have 1 src"
|
||||
@@ -559,8 +559,11 @@ class UOp(RandMixin, metaclass=UOpMetaClass):
|
||||
@staticmethod
|
||||
def special(end:sint, name:str, dtype=dtypes.weakint): return UOp(Ops.SPECIAL, dtype=dtype, src=(sint_to_uop(end, dtype),), arg=name)
|
||||
def _rop(self, op:Ops, axis:tuple[int, ...]):
|
||||
axis = tuple(sorted([x for x in axis if resolve(self.shape[x] != 1)]))
|
||||
return UOp(Ops.REDUCE, self.dtype, (self,), (op, axis)) if len(axis) else self
|
||||
# NOTE: we don't allow reduce on 1s axis
|
||||
axis = tuple(sorted(axis))
|
||||
reduce_axis = tuple(x for x in axis if resolve(self.shape[x] != 1))
|
||||
ret = UOp(Ops.REDUCE, self.dtype, (self,), (op, reduce_axis)) if len(reduce_axis) else self
|
||||
return ret.reshape(tuple(s for i,s in enumerate(self.shape) if i not in axis)) if axis != reduce_axis else ret
|
||||
@staticmethod
|
||||
def invalid(count=1): return UOp(Ops.CONST, dtypes.weakint.vec(count), src=(), arg=Invalid)
|
||||
def valid(self, cond):
|
||||
@@ -629,7 +632,10 @@ class UOp(RandMixin, metaclass=UOpMetaClass):
|
||||
src_axis = self.src[0].axis
|
||||
if self.op is Ops.SHRINK and src_axis is not None and self.marg[src_axis] != (0, self.src[0].shape[src_axis]):
|
||||
return None # SHRINK will remove the sharding if it's on axis
|
||||
if self.op is Ops.REDUCE: return None if src_axis is not None and src_axis in self.arg[1] else src_axis
|
||||
if self.op is Ops.REDUCE:
|
||||
if src_axis is None: return None
|
||||
if src_axis in self.arg[1]: return None
|
||||
return src_axis - sum(1 for a in self.arg[1] if a < src_axis)
|
||||
if self.op is Ops.RESHAPE:
|
||||
if src_axis is None: return None
|
||||
arg_acc:list[sint] = list(itertools.accumulate(self.marg, operator.mul, initial=1))
|
||||
|
||||
Reference in New Issue
Block a user