# basic self-contained tests of the external functionality of tinygrad import unittest, random from tinygrad import Tensor, Context, Variable, TinyJit, dtypes, Device, nn from tinygrad.helpers import IMAGE, CI class TestTiny(unittest.TestCase): # *** basic functionality *** def test_plus(self): out = Tensor([1.,2,3]) + Tensor([4.,5,6]) self.assertListEqual(out.tolist(), [5.0, 7.0, 9.0]) def test_plus_int(self): out = Tensor([1,2,3], dtype=dtypes.int) + Tensor([4,5,6], dtype=dtypes.int) self.assertListEqual(out.tolist(), [5, 7, 9]) def test_plus_big(self): out = Tensor.ones(16).contiguous() + Tensor.ones(16).contiguous() self.assertListEqual(out.tolist(), [2]*16) def test_cat(self): out = Tensor.cat(Tensor.ones(8).contiguous(), Tensor.ones(8).contiguous()) self.assertListEqual(out.tolist(), [1]*16) def test_sum(self): out = Tensor.ones(256).contiguous().sum() self.assertEqual(out.item(), 256) def test_gemm(self, N=64, out_dtype=dtypes.float): a = Tensor.ones(N,N).contiguous() b = Tensor.eye(N).contiguous() self.assertListEqual((out:=a@b).flatten().tolist(), [1.0]*(N*N)) if IMAGE < 2: self.assertEqual(out.dtype, out_dtype) # *** randomness *** def test_random(self): out = Tensor.rand(10) for x in out.tolist(): self.assertGreaterEqual(x, 0.0) self.assertLessEqual(x, 1.0) # *** JIT (for Python speed) *** def test_jit(self): cnt = 0 random.seed(0) def new_rand_list(ln=10): return [random.randint(0, 100000) for _ in range(ln)] @TinyJit def fxn(a,b) -> Tensor: nonlocal cnt cnt += 1 return a+b for _ in range(3): la,lb = new_rand_list(), new_rand_list() fa,fb = Tensor(la), Tensor(lb) ret = fxn(fa, fb) # math is correct self.assertListEqual(ret.tolist(), [a+b for a,b in zip(la, lb)]) # function is only called twice self.assertEqual(cnt, 2) # *** BEAM (for Kernel speed) *** def test_beam(self): with Context(BEAM=1, IGNORE_BEAM_CACHE=1): self.test_plus() # *** symbolic (to allow less recompilation) *** def test_symbolic(self): i = Variable('i', 1, 10) for s in [2,5]: ret = Tensor.ones(s).contiguous().reshape(i.bind(s)) + 1 self.assertListEqual(ret.reshape(s).tolist(), [2.0]*s) def test_symbolic_reduce(self): i = Variable('i', 1, 10) for s in [2,5]: ret = Tensor.ones(s).contiguous().reshape(i.bind(s)).sum() self.assertEqual(ret.item(), s) # *** a model *** # TODO: this is failing because of how swizzling rewrites the ShapeTracker of the final STORE @unittest.skipIf(IMAGE>0 or (CI and Device.DEFAULT == "DSP"), "failing because of make things that can't be images not images") def test_mnist_model(self): layers = [ nn.Conv2d(1, 32, 5), Tensor.relu, nn.Conv2d(32, 32, 5), Tensor.relu, nn.BatchNorm(32), Tensor.max_pool2d, nn.Conv2d(32, 64, 3), Tensor.relu, nn.Conv2d(64, 64, 3), Tensor.relu, nn.BatchNorm(64), Tensor.max_pool2d, lambda x: x.flatten(1), nn.Linear(576, 10)] # replace random weights with ones for p in nn.state.get_parameters(layers): p.replace(Tensor.ones_like(p).contiguous()).realize() # run model inference probs = Tensor.rand(1, 1, 28, 28).sequential(layers).tolist() self.assertEqual(len(probs[0]), 10) # *** image *** @unittest.skipIf(Device.DEFAULT != "GPU", "image only supported on GPU") def test_image(self): with Context(IMAGE=2): self.test_gemm(N=4, out_dtype=dtypes.imagef((4, 1, 4))) def test_beam_image(self): with Context(BEAM=1, IGNORE_BEAM_CACHE=1): self.test_image() if __name__ == '__main__': unittest.main()