forked from tinygrad/tinygrad
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fd14a0889 | ||
|
|
c29075ba8d | ||
|
|
4c593feed3 |
@@ -238,6 +238,8 @@ jobs:
|
|||||||
pip3 install --upgrade --force-reinstall ruff==0.11.0
|
pip3 install --upgrade --force-reinstall ruff==0.11.0
|
||||||
python3 -m ruff check .
|
python3 -m ruff check .
|
||||||
python3 -m ruff check examples/mlperf/ --ignore E501
|
python3 -m ruff check examples/mlperf/ --ignore E501
|
||||||
|
- name: Lint tinygrad with pylint
|
||||||
|
run: python -m pylint tinygrad/
|
||||||
- name: Run mypy
|
- name: Run mypy
|
||||||
run: |
|
run: |
|
||||||
python -m mypy --strict-equality --lineprecision-report .
|
python -m mypy --strict-equality --lineprecision-report .
|
||||||
@@ -272,8 +274,6 @@ jobs:
|
|||||||
# run: NULL=1 DEBUG=1 python3 examples/sdxl.py --seed 0 --noshow --timing --fakeweights
|
# run: NULL=1 DEBUG=1 python3 examples/sdxl.py --seed 0 --noshow --timing --fakeweights
|
||||||
- name: Run Clip tests for SD MLPerf on NULL backend
|
- name: Run Clip tests for SD MLPerf on NULL backend
|
||||||
run: NULL=1 python -m pytest -n=auto test/external/mlperf_stable_diffusion/external_test_models.py::TestOpenClip --durations=20
|
run: NULL=1 python -m pytest -n=auto test/external/mlperf_stable_diffusion/external_test_models.py::TestOpenClip --durations=20
|
||||||
- name: Run AMD emulated BERT training on NULL backend
|
|
||||||
run: EMULATE=AMD_RDNA4 NULL=1 CAPTURE_PROCESS_REPLAY=0 DEFAULT_FLOAT=HALF BENCHMARK=10 BS=66 GPUS=1 BERT_LAYERS=2 MODEL=bert python3 examples/mlperf/model_train.py
|
|
||||||
# TODO: support fake weights
|
# TODO: support fake weights
|
||||||
#- name: Run LLaMA 7B on 4 fake devices
|
#- name: Run LLaMA 7B on 4 fake devices
|
||||||
# run: NULL=1 python3 examples/llama.py --gen 1 --size 7B --shard 4 --prompt "Hello." --count 3 --temperature 0 --timing
|
# run: NULL=1 python3 examples/llama.py --gen 1 --size 7B --shard 4 --prompt "Hello." --count 3 --temperature 0 --timing
|
||||||
|
|||||||
+10
-4
@@ -20,15 +20,21 @@ repos:
|
|||||||
language: system
|
language: system
|
||||||
always_run: true
|
always_run: true
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
- id: tests
|
||||||
|
name: subset of tests
|
||||||
|
entry: env PYTHONPATH="." python3 -m pytest -n=4 test/test_ops.py test/test_dtype.py test/test_schedule.py test/test_assign.py
|
||||||
|
language: system
|
||||||
|
always_run: true
|
||||||
|
pass_filenames: false
|
||||||
- id: example
|
- id: example
|
||||||
name: test all devices
|
name: multi device tests
|
||||||
entry: python3 test/external/external_test_example.py
|
entry: python3 test/external/external_test_example.py
|
||||||
language: system
|
language: system
|
||||||
always_run: true
|
always_run: true
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
- id: tests
|
- id: pylint
|
||||||
name: subset of tests
|
name: pylint
|
||||||
entry: env PYTHONPATH="." python3 -m pytest -n=8 test/test_ops.py test/test_dtype.py test/test_schedule.py test/test_assign.py
|
entry: python3 -m pylint tinygrad/
|
||||||
language: system
|
language: system
|
||||||
always_run: true
|
always_run: true
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
export PYTHONPATH="." NV=1
|
export PYTHONPATH="." NV=1
|
||||||
export MODEL="bert"
|
export MODEL="bert"
|
||||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
|
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
|
||||||
|
|
||||||
export IGNORE_OOB=1
|
export IGNORE_OOB=1
|
||||||
export REWRITE_STACK_LIMIT=500000
|
export REWRITE_STACK_LIMIT=500000
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
export PYTHONPATH="." NV=1
|
export PYTHONPATH="." NV=1
|
||||||
export MODEL="bert"
|
export MODEL="bert"
|
||||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
|
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
|
||||||
|
|
||||||
export IGNORE_OOB=1
|
export IGNORE_OOB=1
|
||||||
export REWRITE_STACK_LIMIT=500000
|
export REWRITE_STACK_LIMIT=500000
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ set -o pipefail # Make pipeline fail if any command fails
|
|||||||
export PYTHONPATH="." NV=1
|
export PYTHONPATH="." NV=1
|
||||||
export MODEL="bert"
|
export MODEL="bert"
|
||||||
export SUBMISSION_PLATFORM="tinybox_green"
|
export SUBMISSION_PLATFORM="tinybox_green"
|
||||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
|
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
|
||||||
|
|
||||||
export IGNORE_OOB=1
|
export IGNORE_OOB=1
|
||||||
export REWRITE_STACK_LIMIT=500000
|
export REWRITE_STACK_LIMIT=500000
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
export PYTHONPATH="." AMD=1
|
export PYTHONPATH="." AMD=1
|
||||||
export MODEL="bert"
|
export MODEL="bert"
|
||||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
|
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
|
||||||
|
|
||||||
export IGNORE_OOB=1
|
export IGNORE_OOB=1
|
||||||
export REWRITE_STACK_LIMIT=500000
|
export REWRITE_STACK_LIMIT=500000
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
export PYTHONPATH="." AMD=1
|
export PYTHONPATH="." AMD=1
|
||||||
export MODEL="bert"
|
export MODEL="bert"
|
||||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
|
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
|
||||||
|
|
||||||
export IGNORE_OOB=1
|
export IGNORE_OOB=1
|
||||||
export REWRITE_STACK_LIMIT=500000
|
export REWRITE_STACK_LIMIT=500000
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ set -o pipefail # Make pipeline fail if any command fails
|
|||||||
export PYTHONPATH="." AMD=1
|
export PYTHONPATH="." AMD=1
|
||||||
export MODEL="bert"
|
export MODEL="bert"
|
||||||
export SUBMISSION_PLATFORM="tinybox_red"
|
export SUBMISSION_PLATFORM="tinybox_red"
|
||||||
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=96 EVAL_BS=96
|
export DEFAULT_FLOAT="HALF" SUM_DTYPE="HALF" GPUS=6 BS=90 EVAL_BS=90
|
||||||
|
|
||||||
export IGNORE_OOB=1
|
export IGNORE_OOB=1
|
||||||
export REWRITE_STACK_LIMIT=500000
|
export REWRITE_STACK_LIMIT=500000
|
||||||
|
|||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
import subprocess, unittest, os, sys
|
|
||||||
from tinygrad.device import Device
|
|
||||||
|
|
||||||
class TestTinygradSlow(unittest.TestCase):
|
|
||||||
def test_env_overwrite_default_device(self):
|
|
||||||
subprocess.run([f'{Device.DEFAULT}=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
|
||||||
shell=True, check=True)
|
|
||||||
subprocess.run([f'DISK=1 {Device.DEFAULT}=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
|
||||||
shell=True, check=True)
|
|
||||||
subprocess.run([f'NPY=1 {Device.DEFAULT}=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
|
||||||
shell=True, check=True)
|
|
||||||
|
|
||||||
if Device.DEFAULT != "CPU":
|
|
||||||
# setting multiple devices fail
|
|
||||||
with self.assertRaises(subprocess.CalledProcessError):
|
|
||||||
subprocess.run([f'{Device.DEFAULT}=1 CPU=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
|
||||||
shell=True, check=True)
|
|
||||||
|
|
||||||
# setting device via DEV
|
|
||||||
subprocess.run([f'DEV={Device.DEFAULT.capitalize()} python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
|
||||||
shell=True, check=True)
|
|
||||||
subprocess.run([f'DEV={Device.DEFAULT.lower()} python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
|
||||||
shell=True, check=True)
|
|
||||||
subprocess.run([f'DEV={Device.DEFAULT.upper()} python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
|
||||||
shell=True, check=True)
|
|
||||||
|
|
||||||
with self.assertRaises(subprocess.CalledProcessError):
|
|
||||||
subprocess.run([f'DEV={Device.DEFAULT} CPU=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
|
||||||
shell=True, check=True)
|
|
||||||
|
|
||||||
class TestRunAsModule(unittest.TestCase):
|
|
||||||
def test_module_runs(self):
|
|
||||||
p = subprocess.run([sys.executable, "-m", "tinygrad.device"],stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
||||||
env={**os.environ, "DEBUG": "1"}, timeout=40,)
|
|
||||||
out = (p.stdout + p.stderr).decode()
|
|
||||||
self.assertEqual(p.returncode, 0, msg=out)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
||||||
+2
-2
@@ -58,8 +58,8 @@ class TestExample(unittest.TestCase):
|
|||||||
print(f"WARNING: {device} test isn't running")
|
print(f"WARNING: {device} test isn't running")
|
||||||
return
|
return
|
||||||
|
|
||||||
x = Tensor.eye(8, device=device, requires_grad=True)
|
x = Tensor.eye(64, device=device, requires_grad=True)
|
||||||
y = Tensor.eye(8, device=device, requires_grad=True)
|
y = Tensor.eye(64, device=device, requires_grad=True)
|
||||||
z = y.matmul(x).sum()
|
z = y.matmul(x).sum()
|
||||||
z.backward()
|
z.backward()
|
||||||
|
|
||||||
|
|||||||
@@ -658,7 +658,7 @@ class TestMultiTensor(unittest.TestCase):
|
|||||||
|
|
||||||
# it doesn't work like this anymore
|
# it doesn't work like this anymore
|
||||||
# NOTE: this never failed in assign_multi, it failed tensor spec because MULTI was never pushed in the graph
|
# NOTE: this never failed in assign_multi, it failed tensor spec because MULTI was never pushed in the graph
|
||||||
@unittest.skip("this test is broken")
|
@unittest.expectedFailure
|
||||||
def test_mlb_assign_change_axis(self):
|
def test_mlb_assign_change_axis(self):
|
||||||
t_none = Tensor.zeros((16, 16)).shard(devices_2).contiguous().realize()
|
t_none = Tensor.zeros((16, 16)).shard(devices_2).contiguous().realize()
|
||||||
t_zero = Tensor.ones((16, 16)).shard(devices_2, axis=0)
|
t_zero = Tensor.ones((16, 16)).shard(devices_2, axis=0)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import subprocess
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
import unittest, copy, mmap, random, math, array
|
import unittest, copy, mmap, random, math, array
|
||||||
@@ -514,6 +515,32 @@ class TestTinygrad(unittest.TestCase):
|
|||||||
print(a)
|
print(a)
|
||||||
print(c)
|
print(c)
|
||||||
|
|
||||||
|
def test_env_overwrite_default_device(self):
|
||||||
|
subprocess.run([f'{Device.DEFAULT}=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||||
|
shell=True, check=True)
|
||||||
|
subprocess.run([f'DISK=1 {Device.DEFAULT}=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||||
|
shell=True, check=True)
|
||||||
|
subprocess.run([f'NPY=1 {Device.DEFAULT}=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||||
|
shell=True, check=True)
|
||||||
|
|
||||||
|
if Device.DEFAULT != "CPU":
|
||||||
|
# setting multiple devices fail
|
||||||
|
with self.assertRaises(subprocess.CalledProcessError):
|
||||||
|
subprocess.run([f'{Device.DEFAULT}=1 CPU=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||||
|
shell=True, check=True)
|
||||||
|
|
||||||
|
# setting device via DEV
|
||||||
|
subprocess.run([f'DEV={Device.DEFAULT.capitalize()} python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||||
|
shell=True, check=True)
|
||||||
|
subprocess.run([f'DEV={Device.DEFAULT.lower()} python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||||
|
shell=True, check=True)
|
||||||
|
subprocess.run([f'DEV={Device.DEFAULT.upper()} python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||||
|
shell=True, check=True)
|
||||||
|
|
||||||
|
with self.assertRaises(subprocess.CalledProcessError):
|
||||||
|
subprocess.run([f'DEV={Device.DEFAULT} CPU=1 python3 -c "from tinygrad import Device; assert Device.DEFAULT == \\"{Device.DEFAULT}\\""'],
|
||||||
|
shell=True, check=True)
|
||||||
|
|
||||||
def test_no_attributeerror_after_apply_uop_exception(self):
|
def test_no_attributeerror_after_apply_uop_exception(self):
|
||||||
try:
|
try:
|
||||||
Tensor.arange(4).reshape(3,2)
|
Tensor.arange(4).reshape(3,2)
|
||||||
|
|||||||
+3
-3
@@ -134,8 +134,8 @@ class TestTiny(unittest.TestCase):
|
|||||||
def test_mnist_backward(self):
|
def test_mnist_backward(self):
|
||||||
# NOTE: we don't have the whole model here for speed
|
# NOTE: we don't have the whole model here for speed
|
||||||
layers = [
|
layers = [
|
||||||
nn.Conv2d(1, 8, 5), Tensor.relu,
|
nn.Conv2d(1, 32, 5), Tensor.relu,
|
||||||
nn.Conv2d(8, 8, 5), Tensor.relu]
|
nn.Conv2d(32, 32, 5), Tensor.relu]
|
||||||
|
|
||||||
# replace random weights with ones
|
# replace random weights with ones
|
||||||
# TODO: there's a bug here where it's tying two of the biases together. we need UNIQUE const
|
# TODO: there's a bug here where it's tying two of the biases together. we need UNIQUE const
|
||||||
@@ -144,7 +144,7 @@ class TestTiny(unittest.TestCase):
|
|||||||
|
|
||||||
# realize gradients
|
# realize gradients
|
||||||
for x in nn.state.get_parameters(layers): x.requires_grad_()
|
for x in nn.state.get_parameters(layers): x.requires_grad_()
|
||||||
Tensor.empty(4, 1, 14, 14).sequential(layers).sum().backward()
|
Tensor.empty(4, 1, 28, 28).sequential(layers).sum().backward()
|
||||||
Tensor.realize(*[x.grad for x in nn.state.get_parameters(layers) if x.grad is not None])
|
Tensor.realize(*[x.grad for x in nn.state.get_parameters(layers) if x.grad is not None])
|
||||||
|
|
||||||
# *** image ***
|
# *** image ***
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import unittest, os, subprocess
|
import unittest, os, subprocess, sys
|
||||||
from tinygrad import Tensor
|
from tinygrad import Tensor
|
||||||
from tinygrad.device import Device, Compiler, enumerate_devices_str
|
from tinygrad.device import Device, Compiler
|
||||||
from tinygrad.helpers import diskcache_get, diskcache_put, getenv, Context, WIN, CI
|
from tinygrad.helpers import diskcache_get, diskcache_put, getenv, Context, WIN, CI
|
||||||
|
|
||||||
class TestDevice(unittest.TestCase):
|
class TestDevice(unittest.TestCase):
|
||||||
@@ -100,7 +100,10 @@ class TestCompiler(unittest.TestCase):
|
|||||||
|
|
||||||
class TestRunAsModule(unittest.TestCase):
|
class TestRunAsModule(unittest.TestCase):
|
||||||
def test_module_runs(self):
|
def test_module_runs(self):
|
||||||
out = '\n'.join(enumerate_devices_str())
|
p = subprocess.run([sys.executable, "-m", "tinygrad.device"],stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||||
|
env={**os.environ, "DEBUG": "1"}, timeout=40,)
|
||||||
|
out = (p.stdout + p.stderr).decode()
|
||||||
|
self.assertEqual(p.returncode, 0, msg=out)
|
||||||
self.assertIn("CPU", out) # for sanity check
|
self.assertIn("CPU", out) # for sanity check
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
+468
-469
@@ -180,6 +180,474 @@ class TestIndexing(unittest.TestCase):
|
|||||||
# def delitem(): del reference[0]
|
# def delitem(): del reference[0]
|
||||||
# self.assertRaises(TypeError, delitem)
|
# self.assertRaises(TypeError, delitem)
|
||||||
|
|
||||||
|
# TODO: LLVM is quite fast, why are other compiled backends slow?
|
||||||
|
@unittest.skipIf(CI and Device.DEFAULT in ["CPU", "CL", "METAL", "NV", "AMD"], "slow")
|
||||||
|
def test_advancedindex(self):
|
||||||
|
# integer array indexing
|
||||||
|
|
||||||
|
# pick a random valid indexer type
|
||||||
|
def ri(indices):
|
||||||
|
choice = random.randint(0, 2)
|
||||||
|
if choice == 0: return Tensor(indices)
|
||||||
|
if choice == 1: return list(indices)
|
||||||
|
return tuple(indices)
|
||||||
|
|
||||||
|
def validate_indexing(x):
|
||||||
|
numpy_testing_assert_equal_helper(x[[0]], consec((1,)))
|
||||||
|
numpy_testing_assert_equal_helper(x[ri([0]),], consec((1,)))
|
||||||
|
numpy_testing_assert_equal_helper(x[ri([3]),], consec((1,), 4))
|
||||||
|
numpy_testing_assert_equal_helper(x[[2, 3, 4]], consec((3,), 3))
|
||||||
|
numpy_testing_assert_equal_helper(x[ri([2, 3, 4]),], consec((3,), 3))
|
||||||
|
numpy_testing_assert_equal_helper(x[ri([0, 2, 4]),], np.array([1, 3, 5]))
|
||||||
|
|
||||||
|
def validate_setting(x):
|
||||||
|
x[[0]] = -2
|
||||||
|
numpy_testing_assert_equal_helper(x[[0]], np.array([-2]))
|
||||||
|
x[[0]] = -1
|
||||||
|
numpy_testing_assert_equal_helper(x[ri([0]), ], np.array([-1]))
|
||||||
|
x[[2, 3, 4]] = 4
|
||||||
|
numpy_testing_assert_equal_helper(x[[2, 3, 4]], np.array([4, 4, 4]))
|
||||||
|
x[ri([2, 3, 4]), ] = 3
|
||||||
|
numpy_testing_assert_equal_helper(x[ri([2, 3, 4]), ], np.array([3, 3, 3]))
|
||||||
|
x[ri([0, 2, 4]), ] = Tensor([5, 4, 3])
|
||||||
|
numpy_testing_assert_equal_helper(x[ri([0, 2, 4]), ], np.array([5, 4, 3]))
|
||||||
|
|
||||||
|
# Case 1: Purely Integer Array Indexing
|
||||||
|
reference = consec((10,))
|
||||||
|
validate_indexing(reference)
|
||||||
|
# setting values
|
||||||
|
validate_setting(reference)
|
||||||
|
|
||||||
|
# Tensor with stride != 1
|
||||||
|
# strided is [1, 3, 5, 7]
|
||||||
|
|
||||||
|
# # TODO: set stride
|
||||||
|
# reference = consec((10,))
|
||||||
|
# strided = set_(reference, (4,), (2,), 0)
|
||||||
|
|
||||||
|
# numpy_testing_assert_equal_helper(strided[[0]], np.array([1]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([0]), ], np.array([1]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([3]), ], np.array([7]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[[1, 2]], np.array([3, 5]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([1, 2]), ], np.array([3, 5]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([[2, 1], [0, 3]]), ],
|
||||||
|
# np.array([[5, 3], [1, 7]]))
|
||||||
|
|
||||||
|
# stride is [4, 8]
|
||||||
|
|
||||||
|
# strided = set_(reference, (2,), (4,), offset=4)
|
||||||
|
|
||||||
|
# numpy_testing_assert_equal_helper(strided[[0]], np.array([5]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([0]), ], np.array([5]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([1]), ], np.array([9]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[[0, 1]], np.array([5, 9]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([0, 1]), ], np.array([5, 9]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([[0, 1], [1, 0]]), ],
|
||||||
|
# np.array([[5, 9], [9, 5]]))
|
||||||
|
|
||||||
|
# reference is 1 2
|
||||||
|
# 3 4
|
||||||
|
# 5 6
|
||||||
|
reference = consec((3, 2))
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([0, 1, 2]), ri([0])], np.array([1, 3, 5]))
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([0, 1, 2]), ri([1])], np.array([2, 4, 6]))
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([0]), ri([0])], consec((1,)))
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([2]), ri([1])], consec((1,), 6))
|
||||||
|
numpy_testing_assert_equal_helper(reference[[ri([0, 0]), ri([0, 1])]], np.array([1, 2]))
|
||||||
|
numpy_testing_assert_equal_helper(reference[[ri([0, 1, 1, 0, 2]), ri([1])]], np.array([2, 4, 4, 2, 6]))
|
||||||
|
numpy_testing_assert_equal_helper(reference[[ri([0, 0, 1, 1]), ri([0, 1, 0, 0])]], np.array([1, 2, 3, 3]))
|
||||||
|
|
||||||
|
rows = ri([[0, 0],
|
||||||
|
[1, 2]])
|
||||||
|
columns = [0],
|
||||||
|
numpy_testing_assert_equal_helper(reference[rows, columns], np.array([[1, 1],
|
||||||
|
[3, 5]]))
|
||||||
|
|
||||||
|
rows = ri([[0, 0],
|
||||||
|
[1, 2]])
|
||||||
|
columns = ri([1, 0])
|
||||||
|
numpy_testing_assert_equal_helper(reference[rows, columns], np.array([[2, 1],
|
||||||
|
[4, 5]]))
|
||||||
|
rows = ri([[0, 0],
|
||||||
|
[1, 2]])
|
||||||
|
columns = ri([[0, 1],
|
||||||
|
[1, 0]])
|
||||||
|
numpy_testing_assert_equal_helper(reference[rows, columns], np.array([[1, 2],
|
||||||
|
[4, 5]]))
|
||||||
|
|
||||||
|
# setting values
|
||||||
|
reference[ri([0]), ri([1])] = -1
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([0]), ri([1])], np.array([-1]))
|
||||||
|
reference[ri([0, 1, 2]), ri([0])] = Tensor([-1, 2, -4])
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([0, 1, 2]), ri([0])],
|
||||||
|
np.array([-1, 2, -4]))
|
||||||
|
reference[rows, columns] = Tensor([[4, 6], [2, 3]])
|
||||||
|
numpy_testing_assert_equal_helper(reference[rows, columns],
|
||||||
|
np.array([[4, 6], [2, 3]]))
|
||||||
|
|
||||||
|
# Verify still works with Transposed (i.e. non-contiguous) Tensors
|
||||||
|
reference = Tensor([[0, 1, 2, 3],
|
||||||
|
[4, 5, 6, 7],
|
||||||
|
[8, 9, 10, 11]]).T
|
||||||
|
|
||||||
|
# Transposed: [[0, 4, 8],
|
||||||
|
# [1, 5, 9],
|
||||||
|
# [2, 6, 10],
|
||||||
|
# [3, 7, 11]]
|
||||||
|
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([0, 1, 2]), ri([0])], np.array([0, 1, 2]))
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([0, 1, 2]), ri([1])], np.array([4, 5, 6]))
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([0]), ri([0])], np.array([0]))
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([2]), ri([1])], np.array([6]))
|
||||||
|
numpy_testing_assert_equal_helper(reference[[ri([0, 0]), ri([0, 1])]], np.array([0, 4]))
|
||||||
|
numpy_testing_assert_equal_helper(reference[[ri([0, 1, 1, 0, 3]), ri([1])]], np.array([4, 5, 5, 4, 7]))
|
||||||
|
numpy_testing_assert_equal_helper(reference[[ri([0, 0, 1, 1]), ri([0, 1, 0, 0])]], np.array([0, 4, 1, 1]))
|
||||||
|
|
||||||
|
rows = ri([[0, 0],
|
||||||
|
[1, 2]])
|
||||||
|
columns = [0],
|
||||||
|
numpy_testing_assert_equal_helper(reference[rows, columns], np.array([[0, 0], [1, 2]]))
|
||||||
|
|
||||||
|
rows = ri([[0, 0],
|
||||||
|
[1, 2]])
|
||||||
|
columns = ri([1, 0])
|
||||||
|
numpy_testing_assert_equal_helper(reference[rows, columns], np.array([[4, 0], [5, 2]]))
|
||||||
|
rows = ri([[0, 0],
|
||||||
|
[1, 3]])
|
||||||
|
columns = ri([[0, 1],
|
||||||
|
[1, 2]])
|
||||||
|
numpy_testing_assert_equal_helper(reference[rows, columns], np.array([[0, 4], [5, 11]]))
|
||||||
|
|
||||||
|
# TODO: non contiguous setitem
|
||||||
|
'''
|
||||||
|
# setting values
|
||||||
|
reference[ri([0]), ri([1])] = -1
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([0]), ri([1])],
|
||||||
|
np.array([-1]))
|
||||||
|
reference[ri([0, 1, 2]), ri([0])] = np.array([-1, 2, -4])
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([0, 1, 2]), ri([0])],
|
||||||
|
np.array([-1, 2, -4]))
|
||||||
|
reference[rows, columns] = np.array([[4, 6], [2, 3]])
|
||||||
|
numpy_testing_assert_equal_helper(reference[rows, columns],
|
||||||
|
np.array([[4, 6], [2, 3]]))
|
||||||
|
'''
|
||||||
|
|
||||||
|
# stride != 1
|
||||||
|
|
||||||
|
# strided is [[1 3 5 7],
|
||||||
|
# [9 11 13 15]]
|
||||||
|
|
||||||
|
# # TODO: set stride
|
||||||
|
# reference = Tensor.arange(0., 24).reshape(3, 8)
|
||||||
|
# strided = set_(reference, (2,4), (8,2), 1)
|
||||||
|
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([0, 1]), ri([0])], np.array([1, 9]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([0, 1]), ri([1])], np.array([3, 11]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([0]), ri([0])], np.array([1]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([1]), ri([3])], np.array([15]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[[ri([0, 0]), ri([0, 3])]], np.array([1, 7]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[[ri([1]), ri([0, 1, 1, 0, 3])]], np.array([9, 11, 11, 9, 15]))
|
||||||
|
# numpy_testing_assert_equal_helper(strided[[ri([0, 0, 1, 1]), ri([0, 1, 0, 0])]], np.array([1, 3, 9, 9]))
|
||||||
|
|
||||||
|
# rows = ri([[0, 0],
|
||||||
|
# [1, 1]])
|
||||||
|
# columns = [0],
|
||||||
|
# numpy_testing_assert_equal_helper(strided[rows, columns], np.array([[1, 1], [9, 9]]))
|
||||||
|
|
||||||
|
# rows = ri([[0, 1],
|
||||||
|
# [1, 0]])
|
||||||
|
# columns = ri([1, 2])
|
||||||
|
# numpy_testing_assert_equal_helper(strided[rows, columns], np.array([[3, 13], [11, 5]]))
|
||||||
|
# rows = ri([[0, 0],
|
||||||
|
# [1, 1]])
|
||||||
|
# columns = ri([[0, 1],
|
||||||
|
# [1, 2]])
|
||||||
|
# numpy_testing_assert_equal_helper(strided[rows, columns], np.array([[1, 3], [11, 13]]))
|
||||||
|
|
||||||
|
# setting values
|
||||||
|
|
||||||
|
# strided is [[10, 11],
|
||||||
|
# [17, 18]]
|
||||||
|
|
||||||
|
# # TODO: set stride
|
||||||
|
# reference = Tensor.arange(0., 24).reshape(3, 8)
|
||||||
|
# strided = set_(reference, (2,2), (7,1), 10)
|
||||||
|
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([0]), ri([1])], np.array([11]))
|
||||||
|
|
||||||
|
# TODO non contiguous setitem
|
||||||
|
'''
|
||||||
|
strided[ri([0]), ri([1])] = -1
|
||||||
|
numpy_testing_assert_equal_helper(strided[ri([0]), ri([1])],
|
||||||
|
Tensor([-1]))
|
||||||
|
'''
|
||||||
|
# # TODO: set stride
|
||||||
|
# reference = Tensor.arange(0., 24).reshape(3, 8)
|
||||||
|
# strided = set_(reference, (2,2), (7,1), 10)
|
||||||
|
|
||||||
|
# numpy_testing_assert_equal_helper(strided[ri([0, 1]), ri([1, 0])], np.array([11, 17]))
|
||||||
|
|
||||||
|
# TODO non contiguous setitem
|
||||||
|
'''
|
||||||
|
strided[ri([0, 1]), ri([1, 0])] = Tensor([-1, 2])
|
||||||
|
numpy_testing_assert_equal_helper(strided[ri([0, 1]), ri([1, 0])],
|
||||||
|
Tensor([-1, 2]))
|
||||||
|
'''
|
||||||
|
|
||||||
|
# # TODO: set stride
|
||||||
|
# reference = Tensor.arange(0., 24).realize().reshape(3, 8)
|
||||||
|
# strided = set_(reference, (2,2), (7,1), 10)
|
||||||
|
|
||||||
|
# rows = ri([[0],
|
||||||
|
# [1]])
|
||||||
|
# columns = ri([[0, 1],
|
||||||
|
# [0, 1]])
|
||||||
|
# numpy_testing_assert_equal_helper(strided[rows, columns], np.array([[10, 11], [17, 18]]))
|
||||||
|
|
||||||
|
# TODO non contiguous setitem
|
||||||
|
'''
|
||||||
|
strided[rows, columns] = Tensor([[4, 6], [2, 3]])
|
||||||
|
numpy_testing_assert_equal_helper(strided[rows, columns],
|
||||||
|
Tensor([[4, 6], [2, 3]]))
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Tests using less than the number of dims, and ellipsis
|
||||||
|
|
||||||
|
# reference is 1 2
|
||||||
|
# 3 4
|
||||||
|
# 5 6
|
||||||
|
reference = consec((3, 2))
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([0, 2]),], np.array([[1, 2], [5, 6]]))
|
||||||
|
numpy_testing_assert_equal_helper(reference[ri([1]), ...], np.array([[3, 4]]))
|
||||||
|
numpy_testing_assert_equal_helper(reference[..., ri([1])], np.array([[2], [4], [6]]))
|
||||||
|
|
||||||
|
# verify too many indices fails
|
||||||
|
with self.assertRaises(IndexError): reference[ri([1]), ri([0, 2]), ri([3])]
|
||||||
|
|
||||||
|
# test invalid index fails
|
||||||
|
reference = Tensor.empty(10)
|
||||||
|
for err_idx in (10, -11):
|
||||||
|
with self.assertRaises(IndexError):
|
||||||
|
reference[err_idx]
|
||||||
|
# NOTE cannot check for out of bounds with Tensor indexing
|
||||||
|
# see tensor.py: __getitem__ (Tiny Things)
|
||||||
|
'''
|
||||||
|
with self.assertRaises(IndexError):
|
||||||
|
reference[Tensor([err_idx], dtype=dtypes.int64)]
|
||||||
|
with self.assertRaises(IndexError):
|
||||||
|
reference[[err_idx]]
|
||||||
|
'''
|
||||||
|
|
||||||
|
def tensor_indices_to_np(tensor: Tensor, indices):
|
||||||
|
npt = tensor.numpy()
|
||||||
|
idxs = tuple(i.numpy().tolist() if isinstance(i, Tensor) and i.dtype == dtypes.int64 else
|
||||||
|
i for i in indices)
|
||||||
|
return npt, idxs
|
||||||
|
|
||||||
|
def get_numpy(tensor, indices):
|
||||||
|
npt, idxs = tensor_indices_to_np(tensor, indices)
|
||||||
|
return Tensor(npt[idxs])
|
||||||
|
|
||||||
|
def set_numpy(tensor:Tensor, indices, value):
|
||||||
|
if not isinstance(value, int):
|
||||||
|
value = value.numpy()
|
||||||
|
npt, idxs = tensor_indices_to_np(tensor, indices)
|
||||||
|
npt[idxs] = value
|
||||||
|
return npt
|
||||||
|
|
||||||
|
def assert_get_eq(tensor, indexer):
|
||||||
|
numpy_testing_assert_equal_helper(tensor[indexer], get_numpy(tensor, indexer))
|
||||||
|
|
||||||
|
def assert_set_eq(tensor: Tensor, indexer, val):
|
||||||
|
pyt = clone(tensor)
|
||||||
|
numt = clone(tensor)
|
||||||
|
pyt[indexer] = val
|
||||||
|
numt = set_numpy(numt, indexer, val)
|
||||||
|
numpy_testing_assert_equal_helper(pyt, numt)
|
||||||
|
|
||||||
|
# NOTE: torch initiates the gradients using g0cpu (rand as gradients)
|
||||||
|
def assert_backward_eq(tensor: Tensor, indexer):
|
||||||
|
cpu = clone(tensor.float())
|
||||||
|
cpu.requires_grad = True
|
||||||
|
outcpu = cpu[indexer].sum()
|
||||||
|
outcpu.backward()
|
||||||
|
dev = cpu.detach()
|
||||||
|
dev.requires_grad = True
|
||||||
|
outdev = dev[indexer].sum()
|
||||||
|
outdev.backward()
|
||||||
|
numpy_testing_assert_equal_helper(cpu.grad, dev.grad)
|
||||||
|
|
||||||
|
def get_set_tensor(indexed: Tensor, indexer):
|
||||||
|
set_size = indexed[indexer].shape
|
||||||
|
set_count = indexed[indexer].numel()
|
||||||
|
set_tensor = Tensor.randint(set_count, high=set_count).reshape(set_size) #.cast(dtypes.float64)
|
||||||
|
return set_tensor
|
||||||
|
|
||||||
|
# Tensor is 0 1 2 3 4
|
||||||
|
# 5 6 7 8 9
|
||||||
|
# 10 11 12 13 14
|
||||||
|
# 15 16 17 18 19
|
||||||
|
reference = Tensor.arange(0., 20).reshape(4, 5)
|
||||||
|
|
||||||
|
indices_to_test = [
|
||||||
|
# grab the second, fourth columns
|
||||||
|
[slice(None), [1, 3]],
|
||||||
|
|
||||||
|
# first, third rows,
|
||||||
|
[[0, 2], slice(None)],
|
||||||
|
|
||||||
|
# weird shape
|
||||||
|
[slice(None), [[0, 1],
|
||||||
|
[2, 3]]],
|
||||||
|
# negatives
|
||||||
|
[[-1], [0]],
|
||||||
|
[[0, 2], [-1]],
|
||||||
|
[slice(None), [-1]],
|
||||||
|
]
|
||||||
|
|
||||||
|
# only test dupes on gets
|
||||||
|
get_indices_to_test = indices_to_test + [[slice(None), [0, 1, 1, 2, 2]]]
|
||||||
|
|
||||||
|
for indexer in get_indices_to_test:
|
||||||
|
assert_get_eq(reference, indexer)
|
||||||
|
assert_backward_eq(reference, indexer)
|
||||||
|
|
||||||
|
for indexer in indices_to_test:
|
||||||
|
assert_set_eq(reference, indexer, 44)
|
||||||
|
assert_set_eq(reference, indexer, get_set_tensor(reference, indexer))
|
||||||
|
|
||||||
|
reference = Tensor.arange(0., 160).reshape(4, 8, 5)
|
||||||
|
|
||||||
|
indices_to_test = [
|
||||||
|
[slice(None), slice(None), [0, 3, 4]],
|
||||||
|
[slice(None), [2, 4, 5, 7], slice(None)],
|
||||||
|
[[2, 3], slice(None), slice(None)],
|
||||||
|
[slice(None), [0, 2, 3], [1, 3, 4]],
|
||||||
|
[slice(None), [0], [1, 2, 4]],
|
||||||
|
[slice(None), [0, 1, 3], [4]],
|
||||||
|
[slice(None), [[0, 1], [1, 0]], [[2, 3]]],
|
||||||
|
[slice(None), [[0, 1], [2, 3]], [[0]]],
|
||||||
|
[slice(None), [[5, 6]], [[0, 3], [4, 4]]],
|
||||||
|
[[0, 2, 3], [1, 3, 4], slice(None)],
|
||||||
|
[[0], [1, 2, 4], slice(None)],
|
||||||
|
[[0, 1, 3], [4], slice(None)],
|
||||||
|
[[[0, 1], [1, 0]], [[2, 1], [3, 5]], slice(None)],
|
||||||
|
[[[0, 1], [1, 0]], [[2, 3]], slice(None)],
|
||||||
|
[[[0, 1], [2, 3]], [[0]], slice(None)],
|
||||||
|
[[[2, 1]], [[0, 3], [4, 4]], slice(None)],
|
||||||
|
[[[2]], [[0, 3], [4, 1]], slice(None)],
|
||||||
|
# non-contiguous indexing subspace
|
||||||
|
[[0, 2, 3], slice(None), [1, 3, 4]],
|
||||||
|
|
||||||
|
# less dim, ellipsis
|
||||||
|
[[0, 2], ],
|
||||||
|
[[0, 2], slice(None)],
|
||||||
|
[[0, 2], Ellipsis],
|
||||||
|
[[0, 2], slice(None), Ellipsis],
|
||||||
|
[[0, 2], Ellipsis, slice(None)],
|
||||||
|
[[0, 2], [1, 3]],
|
||||||
|
[[0, 2], [1, 3], Ellipsis],
|
||||||
|
[Ellipsis, [1, 3], [2, 3]],
|
||||||
|
[Ellipsis, [2, 3, 4]],
|
||||||
|
[Ellipsis, slice(None), [2, 3, 4]],
|
||||||
|
[slice(None), Ellipsis, [2, 3, 4]],
|
||||||
|
|
||||||
|
# ellipsis counts for nothing
|
||||||
|
[Ellipsis, slice(None), slice(None), [0, 3, 4]],
|
||||||
|
[slice(None), Ellipsis, slice(None), [0, 3, 4]],
|
||||||
|
[slice(None), slice(None), Ellipsis, [0, 3, 4]],
|
||||||
|
[slice(None), slice(None), [0, 3, 4], Ellipsis],
|
||||||
|
[Ellipsis, [[0, 1], [1, 0]], [[2, 1], [3, 5]], slice(None)],
|
||||||
|
[[[0, 1], [1, 0]], [[2, 1], [3, 5]], Ellipsis, slice(None)],
|
||||||
|
[[[0, 1], [1, 0]], [[2, 1], [3, 5]], slice(None), Ellipsis],
|
||||||
|
]
|
||||||
|
|
||||||
|
for indexer in indices_to_test:
|
||||||
|
assert_get_eq(reference, indexer)
|
||||||
|
|
||||||
|
assert_set_eq(reference, indexer, 212)
|
||||||
|
assert_set_eq(reference, indexer, get_set_tensor(reference, indexer))
|
||||||
|
assert_backward_eq(reference, indexer)
|
||||||
|
|
||||||
|
reference = Tensor.arange(0., 1296).reshape(3, 9, 8, 6)
|
||||||
|
|
||||||
|
indices_to_test = [
|
||||||
|
[slice(None), slice(None), slice(None), [0, 3, 4]],
|
||||||
|
[slice(None), slice(None), [2, 4, 5, 7], slice(None)],
|
||||||
|
[slice(None), [2, 3], slice(None), slice(None)],
|
||||||
|
[[1, 2], slice(None), slice(None), slice(None)],
|
||||||
|
[slice(None), slice(None), [0, 2, 3], [1, 3, 4]],
|
||||||
|
[slice(None), slice(None), [0], [1, 2, 4]],
|
||||||
|
[slice(None), slice(None), [0, 1, 3], [4]],
|
||||||
|
[slice(None), slice(None), [[0, 1], [1, 0]], [[2, 3]]],
|
||||||
|
[slice(None), slice(None), [[0, 1], [2, 3]], [[0]]],
|
||||||
|
[slice(None), slice(None), [[5, 6]], [[0, 3], [4, 4]]],
|
||||||
|
[slice(None), [0, 2, 3], [1, 3, 4], slice(None)],
|
||||||
|
[slice(None), [0], [1, 2, 4], slice(None)],
|
||||||
|
[slice(None), [0, 1, 3], [4], slice(None)],
|
||||||
|
[slice(None), [[0, 1], [3, 4]], [[2, 3], [0, 1]], slice(None)],
|
||||||
|
[slice(None), [[0, 1], [3, 4]], [[2, 3]], slice(None)],
|
||||||
|
[slice(None), [[0, 1], [3, 2]], [[0]], slice(None)],
|
||||||
|
[slice(None), [[2, 1]], [[0, 3], [6, 4]], slice(None)],
|
||||||
|
[slice(None), [[2]], [[0, 3], [4, 2]], slice(None)],
|
||||||
|
[[0, 1, 2], [1, 3, 4], slice(None), slice(None)],
|
||||||
|
[[0], [1, 2, 4], slice(None), slice(None)],
|
||||||
|
[[0, 1, 2], [4], slice(None), slice(None)],
|
||||||
|
[[[0, 1], [0, 2]], [[2, 4], [1, 5]], slice(None), slice(None)],
|
||||||
|
[[[0, 1], [1, 2]], [[2, 0]], slice(None), slice(None)],
|
||||||
|
[[[2, 2]], [[0, 3], [4, 5]], slice(None), slice(None)],
|
||||||
|
[[[2]], [[0, 3], [4, 5]], slice(None), slice(None)],
|
||||||
|
[slice(None), [3, 4, 6], [0, 2, 3], [1, 3, 4]],
|
||||||
|
[slice(None), [2, 3, 4], [1, 3, 4], [4]],
|
||||||
|
[slice(None), [0, 1, 3], [4], [1, 3, 4]],
|
||||||
|
[slice(None), [6], [0, 2, 3], [1, 3, 4]],
|
||||||
|
[slice(None), [2, 3, 5], [3], [4]],
|
||||||
|
[slice(None), [0], [4], [1, 3, 4]],
|
||||||
|
[slice(None), [6], [0, 2, 3], [1]],
|
||||||
|
[slice(None), [[0, 3], [3, 6]], [[0, 1], [1, 3]], [[5, 3], [1, 2]]],
|
||||||
|
[[2, 2, 1], [0, 2, 3], [1, 3, 4], slice(None)],
|
||||||
|
[[2, 0, 1], [1, 2, 3], [4], slice(None)],
|
||||||
|
[[0, 1, 2], [4], [1, 3, 4], slice(None)],
|
||||||
|
[[0], [0, 2, 3], [1, 3, 4], slice(None)],
|
||||||
|
[[0, 2, 1], [3], [4], slice(None)],
|
||||||
|
[[0], [4], [1, 3, 4], slice(None)],
|
||||||
|
[[1], [0, 2, 3], [1], slice(None)],
|
||||||
|
[[[1, 2], [1, 2]], [[0, 1], [2, 3]], [[2, 3], [3, 5]], slice(None)],
|
||||||
|
|
||||||
|
# less dim, ellipsis
|
||||||
|
[Ellipsis, [0, 3, 4]],
|
||||||
|
[Ellipsis, slice(None), [0, 3, 4]],
|
||||||
|
[Ellipsis, slice(None), slice(None), [0, 3, 4]],
|
||||||
|
[slice(None), Ellipsis, [0, 3, 4]],
|
||||||
|
[slice(None), slice(None), Ellipsis, [0, 3, 4]],
|
||||||
|
[slice(None), [0, 2, 3], [1, 3, 4]],
|
||||||
|
[slice(None), [0, 2, 3], [1, 3, 4], Ellipsis],
|
||||||
|
[Ellipsis, [0, 2, 3], [1, 3, 4], slice(None)],
|
||||||
|
[[0], [1, 2, 4]],
|
||||||
|
[[0], [1, 2, 4], slice(None)],
|
||||||
|
[[0], [1, 2, 4], Ellipsis],
|
||||||
|
[[0], [1, 2, 4], Ellipsis, slice(None)],
|
||||||
|
[[1], ],
|
||||||
|
[[0, 2, 1], [3], [4]],
|
||||||
|
[[0, 2, 1], [3], [4], slice(None)],
|
||||||
|
[[0, 2, 1], [3], [4], Ellipsis],
|
||||||
|
[Ellipsis, [0, 2, 1], [3], [4]],
|
||||||
|
]
|
||||||
|
|
||||||
|
for indexer in indices_to_test:
|
||||||
|
assert_get_eq(reference, indexer)
|
||||||
|
assert_set_eq(reference, indexer, 1333)
|
||||||
|
assert_set_eq(reference, indexer, get_set_tensor(reference, indexer))
|
||||||
|
|
||||||
|
indices_to_test += [
|
||||||
|
[slice(None), slice(None), [[0, 1], [1, 0]], [[2, 3], [3, 0]]],
|
||||||
|
[slice(None), slice(None), [[2]], [[0, 3], [4, 4]]],
|
||||||
|
]
|
||||||
|
for indexer in indices_to_test:
|
||||||
|
assert_get_eq(reference, indexer)
|
||||||
|
assert_set_eq(reference, indexer, 1333)
|
||||||
|
assert_backward_eq(reference, indexer)
|
||||||
|
|
||||||
# TODO setitem backward
|
# TODO setitem backward
|
||||||
'''
|
'''
|
||||||
def test_set_item_to_scalar_tensor(self):
|
def test_set_item_to_scalar_tensor(self):
|
||||||
@@ -1100,474 +1568,5 @@ class TestNumpy(unittest.TestCase):
|
|||||||
numpy_testing_assert_equal_helper(kernel, kernel2)
|
numpy_testing_assert_equal_helper(kernel, kernel2)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def tensor_indices_to_np(tensor: Tensor, indices):
|
|
||||||
npt = tensor.numpy()
|
|
||||||
idxs = tuple(i.numpy().tolist() if isinstance(i, Tensor) and i.dtype == dtypes.int64 else
|
|
||||||
i for i in indices)
|
|
||||||
return npt, idxs
|
|
||||||
|
|
||||||
def get_numpy(tensor, indices):
|
|
||||||
npt, idxs = tensor_indices_to_np(tensor, indices)
|
|
||||||
return Tensor(npt[idxs])
|
|
||||||
|
|
||||||
def set_numpy(tensor:Tensor, indices, value):
|
|
||||||
if not isinstance(value, int):
|
|
||||||
value = value.numpy()
|
|
||||||
npt, idxs = tensor_indices_to_np(tensor, indices)
|
|
||||||
npt[idxs] = value
|
|
||||||
return npt
|
|
||||||
|
|
||||||
def assert_get_eq(tensor, indexer):
|
|
||||||
numpy_testing_assert_equal_helper(tensor[indexer], get_numpy(tensor, indexer))
|
|
||||||
|
|
||||||
def assert_set_eq(tensor: Tensor, indexer, val):
|
|
||||||
pyt = clone(tensor)
|
|
||||||
numt = clone(tensor)
|
|
||||||
pyt[indexer] = val
|
|
||||||
numt = set_numpy(numt, indexer, val)
|
|
||||||
numpy_testing_assert_equal_helper(pyt, numt)
|
|
||||||
|
|
||||||
# NOTE: torch initiates the gradients using g0cpu (rand as gradients)
|
|
||||||
def assert_backward_eq(tensor: Tensor, indexer):
|
|
||||||
cpu = clone(tensor.float())
|
|
||||||
cpu.requires_grad = True
|
|
||||||
outcpu = cpu[indexer].sum()
|
|
||||||
outcpu.backward()
|
|
||||||
dev = cpu.detach()
|
|
||||||
dev.requires_grad = True
|
|
||||||
outdev = dev[indexer].sum()
|
|
||||||
outdev.backward()
|
|
||||||
numpy_testing_assert_equal_helper(cpu.grad, dev.grad)
|
|
||||||
|
|
||||||
def get_set_tensor(indexed: Tensor, indexer):
|
|
||||||
set_size = indexed[indexer].shape
|
|
||||||
set_count = indexed[indexer].numel()
|
|
||||||
set_tensor = Tensor.randint(set_count, high=set_count).reshape(set_size) #.cast(dtypes.float64)
|
|
||||||
return set_tensor
|
|
||||||
|
|
||||||
@unittest.skipIf(CI and Device.DEFAULT in ["CPU", "CL", "METAL", "NV", "AMD"], "slow")
|
|
||||||
class TestAdvancedIndexing(unittest.TestCase):
|
|
||||||
def test_integer_array_indexing(self):
|
|
||||||
# pick a random valid indexer type
|
|
||||||
def ri(indices):
|
|
||||||
choice = random.randint(0, 2)
|
|
||||||
if choice == 0: return Tensor(indices)
|
|
||||||
if choice == 1: return list(indices)
|
|
||||||
return tuple(indices)
|
|
||||||
|
|
||||||
def validate_indexing(x):
|
|
||||||
numpy_testing_assert_equal_helper(x[[0]], consec((1,)))
|
|
||||||
numpy_testing_assert_equal_helper(x[ri([0]),], consec((1,)))
|
|
||||||
numpy_testing_assert_equal_helper(x[ri([3]),], consec((1,), 4))
|
|
||||||
numpy_testing_assert_equal_helper(x[[2, 3, 4]], consec((3,), 3))
|
|
||||||
numpy_testing_assert_equal_helper(x[ri([2, 3, 4]),], consec((3,), 3))
|
|
||||||
numpy_testing_assert_equal_helper(x[ri([0, 2, 4]),], np.array([1, 3, 5]))
|
|
||||||
|
|
||||||
def validate_setting(x):
|
|
||||||
x[[0]] = -2
|
|
||||||
numpy_testing_assert_equal_helper(x[[0]], np.array([-2]))
|
|
||||||
x[[0]] = -1
|
|
||||||
numpy_testing_assert_equal_helper(x[ri([0]), ], np.array([-1]))
|
|
||||||
x[[2, 3, 4]] = 4
|
|
||||||
numpy_testing_assert_equal_helper(x[[2, 3, 4]], np.array([4, 4, 4]))
|
|
||||||
x[ri([2, 3, 4]), ] = 3
|
|
||||||
numpy_testing_assert_equal_helper(x[ri([2, 3, 4]), ], np.array([3, 3, 3]))
|
|
||||||
x[ri([0, 2, 4]), ] = Tensor([5, 4, 3])
|
|
||||||
numpy_testing_assert_equal_helper(x[ri([0, 2, 4]), ], np.array([5, 4, 3]))
|
|
||||||
|
|
||||||
# Case 1: Purely Integer Array Indexing
|
|
||||||
reference = consec((10,))
|
|
||||||
validate_indexing(reference)
|
|
||||||
# setting values
|
|
||||||
validate_setting(reference)
|
|
||||||
|
|
||||||
# Tensor with stride != 1
|
|
||||||
# strided is [1, 3, 5, 7]
|
|
||||||
|
|
||||||
# # TODO: set stride
|
|
||||||
# reference = consec((10,))
|
|
||||||
# strided = set_(reference, (4,), (2,), 0)
|
|
||||||
|
|
||||||
# numpy_testing_assert_equal_helper(strided[[0]], np.array([1]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([0]), ], np.array([1]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([3]), ], np.array([7]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[[1, 2]], np.array([3, 5]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([1, 2]), ], np.array([3, 5]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([[2, 1], [0, 3]]), ],
|
|
||||||
# np.array([[5, 3], [1, 7]]))
|
|
||||||
|
|
||||||
# stride is [4, 8]
|
|
||||||
|
|
||||||
# strided = set_(reference, (2,), (4,), offset=4)
|
|
||||||
|
|
||||||
# numpy_testing_assert_equal_helper(strided[[0]], np.array([5]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([0]), ], np.array([5]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([1]), ], np.array([9]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[[0, 1]], np.array([5, 9]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([0, 1]), ], np.array([5, 9]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([[0, 1], [1, 0]]), ],
|
|
||||||
# np.array([[5, 9], [9, 5]]))
|
|
||||||
|
|
||||||
# reference is 1 2
|
|
||||||
# 3 4
|
|
||||||
# 5 6
|
|
||||||
reference = consec((3, 2))
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([0, 1, 2]), ri([0])], np.array([1, 3, 5]))
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([0, 1, 2]), ri([1])], np.array([2, 4, 6]))
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([0]), ri([0])], consec((1,)))
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([2]), ri([1])], consec((1,), 6))
|
|
||||||
numpy_testing_assert_equal_helper(reference[[ri([0, 0]), ri([0, 1])]], np.array([1, 2]))
|
|
||||||
numpy_testing_assert_equal_helper(reference[[ri([0, 1, 1, 0, 2]), ri([1])]], np.array([2, 4, 4, 2, 6]))
|
|
||||||
numpy_testing_assert_equal_helper(reference[[ri([0, 0, 1, 1]), ri([0, 1, 0, 0])]], np.array([1, 2, 3, 3]))
|
|
||||||
|
|
||||||
rows = ri([[0, 0],
|
|
||||||
[1, 2]])
|
|
||||||
columns = [0],
|
|
||||||
numpy_testing_assert_equal_helper(reference[rows, columns], np.array([[1, 1],
|
|
||||||
[3, 5]]))
|
|
||||||
|
|
||||||
rows = ri([[0, 0],
|
|
||||||
[1, 2]])
|
|
||||||
columns = ri([1, 0])
|
|
||||||
numpy_testing_assert_equal_helper(reference[rows, columns], np.array([[2, 1],
|
|
||||||
[4, 5]]))
|
|
||||||
rows = ri([[0, 0],
|
|
||||||
[1, 2]])
|
|
||||||
columns = ri([[0, 1],
|
|
||||||
[1, 0]])
|
|
||||||
numpy_testing_assert_equal_helper(reference[rows, columns], np.array([[1, 2],
|
|
||||||
[4, 5]]))
|
|
||||||
|
|
||||||
# setting values
|
|
||||||
reference[ri([0]), ri([1])] = -1
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([0]), ri([1])], np.array([-1]))
|
|
||||||
reference[ri([0, 1, 2]), ri([0])] = Tensor([-1, 2, -4])
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([0, 1, 2]), ri([0])],
|
|
||||||
np.array([-1, 2, -4]))
|
|
||||||
reference[rows, columns] = Tensor([[4, 6], [2, 3]])
|
|
||||||
numpy_testing_assert_equal_helper(reference[rows, columns],
|
|
||||||
np.array([[4, 6], [2, 3]]))
|
|
||||||
|
|
||||||
# Verify still works with Transposed (i.e. non-contiguous) Tensors
|
|
||||||
reference = Tensor([[0, 1, 2, 3],
|
|
||||||
[4, 5, 6, 7],
|
|
||||||
[8, 9, 10, 11]]).T
|
|
||||||
|
|
||||||
# Transposed: [[0, 4, 8],
|
|
||||||
# [1, 5, 9],
|
|
||||||
# [2, 6, 10],
|
|
||||||
# [3, 7, 11]]
|
|
||||||
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([0, 1, 2]), ri([0])], np.array([0, 1, 2]))
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([0, 1, 2]), ri([1])], np.array([4, 5, 6]))
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([0]), ri([0])], np.array([0]))
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([2]), ri([1])], np.array([6]))
|
|
||||||
numpy_testing_assert_equal_helper(reference[[ri([0, 0]), ri([0, 1])]], np.array([0, 4]))
|
|
||||||
numpy_testing_assert_equal_helper(reference[[ri([0, 1, 1, 0, 3]), ri([1])]], np.array([4, 5, 5, 4, 7]))
|
|
||||||
numpy_testing_assert_equal_helper(reference[[ri([0, 0, 1, 1]), ri([0, 1, 0, 0])]], np.array([0, 4, 1, 1]))
|
|
||||||
|
|
||||||
rows = ri([[0, 0],
|
|
||||||
[1, 2]])
|
|
||||||
columns = [0],
|
|
||||||
numpy_testing_assert_equal_helper(reference[rows, columns], np.array([[0, 0], [1, 2]]))
|
|
||||||
|
|
||||||
rows = ri([[0, 0],
|
|
||||||
[1, 2]])
|
|
||||||
columns = ri([1, 0])
|
|
||||||
numpy_testing_assert_equal_helper(reference[rows, columns], np.array([[4, 0], [5, 2]]))
|
|
||||||
rows = ri([[0, 0],
|
|
||||||
[1, 3]])
|
|
||||||
columns = ri([[0, 1],
|
|
||||||
[1, 2]])
|
|
||||||
numpy_testing_assert_equal_helper(reference[rows, columns], np.array([[0, 4], [5, 11]]))
|
|
||||||
|
|
||||||
# TODO: non contiguous setitem
|
|
||||||
'''
|
|
||||||
# setting values
|
|
||||||
reference[ri([0]), ri([1])] = -1
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([0]), ri([1])],
|
|
||||||
np.array([-1]))
|
|
||||||
reference[ri([0, 1, 2]), ri([0])] = np.array([-1, 2, -4])
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([0, 1, 2]), ri([0])],
|
|
||||||
np.array([-1, 2, -4]))
|
|
||||||
reference[rows, columns] = np.array([[4, 6], [2, 3]])
|
|
||||||
numpy_testing_assert_equal_helper(reference[rows, columns],
|
|
||||||
np.array([[4, 6], [2, 3]]))
|
|
||||||
'''
|
|
||||||
|
|
||||||
# stride != 1
|
|
||||||
|
|
||||||
# strided is [[1 3 5 7],
|
|
||||||
# [9 11 13 15]]
|
|
||||||
|
|
||||||
# # TODO: set stride
|
|
||||||
# reference = Tensor.arange(0., 24).reshape(3, 8)
|
|
||||||
# strided = set_(reference, (2,4), (8,2), 1)
|
|
||||||
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([0, 1]), ri([0])], np.array([1, 9]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([0, 1]), ri([1])], np.array([3, 11]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([0]), ri([0])], np.array([1]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([1]), ri([3])], np.array([15]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[[ri([0, 0]), ri([0, 3])]], np.array([1, 7]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[[ri([1]), ri([0, 1, 1, 0, 3])]], np.array([9, 11, 11, 9, 15]))
|
|
||||||
# numpy_testing_assert_equal_helper(strided[[ri([0, 0, 1, 1]), ri([0, 1, 0, 0])]], np.array([1, 3, 9, 9]))
|
|
||||||
|
|
||||||
# rows = ri([[0, 0],
|
|
||||||
# [1, 1]])
|
|
||||||
# columns = [0],
|
|
||||||
# numpy_testing_assert_equal_helper(strided[rows, columns], np.array([[1, 1], [9, 9]]))
|
|
||||||
|
|
||||||
# rows = ri([[0, 1],
|
|
||||||
# [1, 0]])
|
|
||||||
# columns = ri([1, 2])
|
|
||||||
# numpy_testing_assert_equal_helper(strided[rows, columns], np.array([[3, 13], [11, 5]]))
|
|
||||||
# rows = ri([[0, 0],
|
|
||||||
# [1, 1]])
|
|
||||||
# columns = ri([[0, 1],
|
|
||||||
# [1, 2]])
|
|
||||||
# numpy_testing_assert_equal_helper(strided[rows, columns], np.array([[1, 3], [11, 13]]))
|
|
||||||
|
|
||||||
# setting values
|
|
||||||
|
|
||||||
# strided is [[10, 11],
|
|
||||||
# [17, 18]]
|
|
||||||
|
|
||||||
# # TODO: set stride
|
|
||||||
# reference = Tensor.arange(0., 24).reshape(3, 8)
|
|
||||||
# strided = set_(reference, (2,2), (7,1), 10)
|
|
||||||
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([0]), ri([1])], np.array([11]))
|
|
||||||
|
|
||||||
# TODO non contiguous setitem
|
|
||||||
'''
|
|
||||||
strided[ri([0]), ri([1])] = -1
|
|
||||||
numpy_testing_assert_equal_helper(strided[ri([0]), ri([1])],
|
|
||||||
Tensor([-1]))
|
|
||||||
'''
|
|
||||||
# # TODO: set stride
|
|
||||||
# reference = Tensor.arange(0., 24).reshape(3, 8)
|
|
||||||
# strided = set_(reference, (2,2), (7,1), 10)
|
|
||||||
|
|
||||||
# numpy_testing_assert_equal_helper(strided[ri([0, 1]), ri([1, 0])], np.array([11, 17]))
|
|
||||||
|
|
||||||
# TODO non contiguous setitem
|
|
||||||
'''
|
|
||||||
strided[ri([0, 1]), ri([1, 0])] = Tensor([-1, 2])
|
|
||||||
numpy_testing_assert_equal_helper(strided[ri([0, 1]), ri([1, 0])],
|
|
||||||
Tensor([-1, 2]))
|
|
||||||
'''
|
|
||||||
|
|
||||||
# # TODO: set stride
|
|
||||||
# reference = Tensor.arange(0., 24).realize().reshape(3, 8)
|
|
||||||
# strided = set_(reference, (2,2), (7,1), 10)
|
|
||||||
|
|
||||||
# rows = ri([[0],
|
|
||||||
# [1]])
|
|
||||||
# columns = ri([[0, 1],
|
|
||||||
# [0, 1]])
|
|
||||||
# numpy_testing_assert_equal_helper(strided[rows, columns], np.array([[10, 11], [17, 18]]))
|
|
||||||
|
|
||||||
# TODO non contiguous setitem
|
|
||||||
'''
|
|
||||||
strided[rows, columns] = Tensor([[4, 6], [2, 3]])
|
|
||||||
numpy_testing_assert_equal_helper(strided[rows, columns],
|
|
||||||
Tensor([[4, 6], [2, 3]]))
|
|
||||||
'''
|
|
||||||
|
|
||||||
# Tests using less than the number of dims, and ellipsis
|
|
||||||
|
|
||||||
# reference is 1 2
|
|
||||||
# 3 4
|
|
||||||
# 5 6
|
|
||||||
reference = consec((3, 2))
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([0, 2]),], np.array([[1, 2], [5, 6]]))
|
|
||||||
numpy_testing_assert_equal_helper(reference[ri([1]), ...], np.array([[3, 4]]))
|
|
||||||
numpy_testing_assert_equal_helper(reference[..., ri([1])], np.array([[2], [4], [6]]))
|
|
||||||
|
|
||||||
# verify too many indices fails
|
|
||||||
with self.assertRaises(IndexError): reference[ri([1]), ri([0, 2]), ri([3])]
|
|
||||||
|
|
||||||
# test invalid index fails
|
|
||||||
reference = Tensor.empty(10)
|
|
||||||
for err_idx in (10, -11):
|
|
||||||
with self.assertRaises(IndexError):
|
|
||||||
reference[err_idx]
|
|
||||||
# NOTE cannot check for out of bounds with Tensor indexing
|
|
||||||
# see tensor.py: __getitem__ (Tiny Things)
|
|
||||||
'''
|
|
||||||
with self.assertRaises(IndexError):
|
|
||||||
reference[Tensor([err_idx], dtype=dtypes.int64)]
|
|
||||||
with self.assertRaises(IndexError):
|
|
||||||
reference[[err_idx]]
|
|
||||||
'''
|
|
||||||
|
|
||||||
def test_numpy_parity_and_backward_2d(self):
|
|
||||||
# Tensor is 0 1 2 3 4
|
|
||||||
# 5 6 7 8 9
|
|
||||||
# 10 11 12 13 14
|
|
||||||
# 15 16 17 18 19
|
|
||||||
reference = Tensor.arange(0., 20).reshape(4, 5)
|
|
||||||
|
|
||||||
indices_to_test = [
|
|
||||||
# grab the second, fourth columns
|
|
||||||
[slice(None), [1, 3]],
|
|
||||||
|
|
||||||
# first, third rows,
|
|
||||||
[[0, 2], slice(None)],
|
|
||||||
|
|
||||||
# weird shape
|
|
||||||
[slice(None), [[0, 1],
|
|
||||||
[2, 3]]],
|
|
||||||
# negatives
|
|
||||||
[[-1], [0]],
|
|
||||||
[[0, 2], [-1]],
|
|
||||||
[slice(None), [-1]],
|
|
||||||
]
|
|
||||||
|
|
||||||
# only test dupes on gets
|
|
||||||
get_indices_to_test = indices_to_test + [[slice(None), [0, 1, 1, 2, 2]]]
|
|
||||||
|
|
||||||
for indexer in get_indices_to_test:
|
|
||||||
assert_get_eq(reference, indexer)
|
|
||||||
assert_backward_eq(reference, indexer)
|
|
||||||
|
|
||||||
for indexer in indices_to_test:
|
|
||||||
assert_set_eq(reference, indexer, 44)
|
|
||||||
assert_set_eq(reference, indexer, get_set_tensor(reference, indexer))
|
|
||||||
|
|
||||||
def test_numpy_parity_and_backward_3d(self):
|
|
||||||
reference = Tensor.arange(0., 160).reshape(4, 8, 5)
|
|
||||||
|
|
||||||
indices_to_test = [
|
|
||||||
[slice(None), slice(None), [0, 3, 4]],
|
|
||||||
[slice(None), [2, 4, 5, 7], slice(None)],
|
|
||||||
[[2, 3], slice(None), slice(None)],
|
|
||||||
[slice(None), [0, 2, 3], [1, 3, 4]],
|
|
||||||
[slice(None), [0], [1, 2, 4]],
|
|
||||||
[slice(None), [0, 1, 3], [4]],
|
|
||||||
[slice(None), [[0, 1], [1, 0]], [[2, 3]]],
|
|
||||||
[slice(None), [[0, 1], [2, 3]], [[0]]],
|
|
||||||
[slice(None), [[5, 6]], [[0, 3], [4, 4]]],
|
|
||||||
[[0, 2, 3], [1, 3, 4], slice(None)],
|
|
||||||
[[0], [1, 2, 4], slice(None)],
|
|
||||||
[[0, 1, 3], [4], slice(None)],
|
|
||||||
[[[0, 1], [1, 0]], [[2, 1], [3, 5]], slice(None)],
|
|
||||||
[[[0, 1], [1, 0]], [[2, 3]], slice(None)],
|
|
||||||
[[[0, 1], [2, 3]], [[0]], slice(None)],
|
|
||||||
[[[2, 1]], [[0, 3], [4, 4]], slice(None)],
|
|
||||||
[[[2]], [[0, 3], [4, 1]], slice(None)],
|
|
||||||
# non-contiguous indexing subspace
|
|
||||||
[[0, 2, 3], slice(None), [1, 3, 4]],
|
|
||||||
|
|
||||||
# less dim, ellipsis
|
|
||||||
[[0, 2], ],
|
|
||||||
[[0, 2], slice(None)],
|
|
||||||
[[0, 2], Ellipsis],
|
|
||||||
[[0, 2], slice(None), Ellipsis],
|
|
||||||
[[0, 2], Ellipsis, slice(None)],
|
|
||||||
[[0, 2], [1, 3]],
|
|
||||||
[[0, 2], [1, 3], Ellipsis],
|
|
||||||
[Ellipsis, [1, 3], [2, 3]],
|
|
||||||
[Ellipsis, [2, 3, 4]],
|
|
||||||
[Ellipsis, slice(None), [2, 3, 4]],
|
|
||||||
[slice(None), Ellipsis, [2, 3, 4]],
|
|
||||||
|
|
||||||
# ellipsis counts for nothing
|
|
||||||
[Ellipsis, slice(None), slice(None), [0, 3, 4]],
|
|
||||||
[slice(None), Ellipsis, slice(None), [0, 3, 4]],
|
|
||||||
[slice(None), slice(None), Ellipsis, [0, 3, 4]],
|
|
||||||
[slice(None), slice(None), [0, 3, 4], Ellipsis],
|
|
||||||
[Ellipsis, [[0, 1], [1, 0]], [[2, 1], [3, 5]], slice(None)],
|
|
||||||
[[[0, 1], [1, 0]], [[2, 1], [3, 5]], Ellipsis, slice(None)],
|
|
||||||
[[[0, 1], [1, 0]], [[2, 1], [3, 5]], slice(None), Ellipsis],
|
|
||||||
]
|
|
||||||
|
|
||||||
for indexer in indices_to_test:
|
|
||||||
assert_get_eq(reference, indexer)
|
|
||||||
|
|
||||||
assert_set_eq(reference, indexer, 212)
|
|
||||||
assert_set_eq(reference, indexer, get_set_tensor(reference, indexer))
|
|
||||||
assert_backward_eq(reference, indexer)
|
|
||||||
|
|
||||||
def test_numpy_parity_and_backward_4d(self):
|
|
||||||
reference = Tensor.arange(0., 1296).reshape(3, 9, 8, 6)
|
|
||||||
|
|
||||||
indices_to_test = [
|
|
||||||
[slice(None), slice(None), slice(None), [0, 3, 4]],
|
|
||||||
[slice(None), slice(None), [2, 4, 5, 7], slice(None)],
|
|
||||||
[slice(None), [2, 3], slice(None), slice(None)],
|
|
||||||
[[1, 2], slice(None), slice(None), slice(None)],
|
|
||||||
[slice(None), slice(None), [0, 2, 3], [1, 3, 4]],
|
|
||||||
[slice(None), slice(None), [0], [1, 2, 4]],
|
|
||||||
[slice(None), slice(None), [0, 1, 3], [4]],
|
|
||||||
[slice(None), slice(None), [[0, 1], [1, 0]], [[2, 3]]],
|
|
||||||
[slice(None), slice(None), [[0, 1], [2, 3]], [[0]]],
|
|
||||||
[slice(None), slice(None), [[5, 6]], [[0, 3], [4, 4]]],
|
|
||||||
[slice(None), [0, 2, 3], [1, 3, 4], slice(None)],
|
|
||||||
[slice(None), [0], [1, 2, 4], slice(None)],
|
|
||||||
[slice(None), [0, 1, 3], [4], slice(None)],
|
|
||||||
[slice(None), [[0, 1], [3, 4]], [[2, 3], [0, 1]], slice(None)],
|
|
||||||
[slice(None), [[0, 1], [3, 4]], [[2, 3]], slice(None)],
|
|
||||||
[slice(None), [[0, 1], [3, 2]], [[0]], slice(None)],
|
|
||||||
[slice(None), [[2, 1]], [[0, 3], [6, 4]], slice(None)],
|
|
||||||
[slice(None), [[2]], [[0, 3], [4, 2]], slice(None)],
|
|
||||||
[[0, 1, 2], [1, 3, 4], slice(None), slice(None)],
|
|
||||||
[[0], [1, 2, 4], slice(None), slice(None)],
|
|
||||||
[[0, 1, 2], [4], slice(None), slice(None)],
|
|
||||||
[[[0, 1], [0, 2]], [[2, 4], [1, 5]], slice(None), slice(None)],
|
|
||||||
[[[0, 1], [1, 2]], [[2, 0]], slice(None), slice(None)],
|
|
||||||
[[[2, 2]], [[0, 3], [4, 5]], slice(None), slice(None)],
|
|
||||||
[[[2]], [[0, 3], [4, 5]], slice(None), slice(None)],
|
|
||||||
[slice(None), [3, 4, 6], [0, 2, 3], [1, 3, 4]],
|
|
||||||
[slice(None), [2, 3, 4], [1, 3, 4], [4]],
|
|
||||||
[slice(None), [0, 1, 3], [4], [1, 3, 4]],
|
|
||||||
[slice(None), [6], [0, 2, 3], [1, 3, 4]],
|
|
||||||
[slice(None), [2, 3, 5], [3], [4]],
|
|
||||||
[slice(None), [0], [4], [1, 3, 4]],
|
|
||||||
[slice(None), [6], [0, 2, 3], [1]],
|
|
||||||
[slice(None), [[0, 3], [3, 6]], [[0, 1], [1, 3]], [[5, 3], [1, 2]]],
|
|
||||||
[[2, 2, 1], [0, 2, 3], [1, 3, 4], slice(None)],
|
|
||||||
[[2, 0, 1], [1, 2, 3], [4], slice(None)],
|
|
||||||
[[0, 1, 2], [4], [1, 3, 4], slice(None)],
|
|
||||||
[[0], [0, 2, 3], [1, 3, 4], slice(None)],
|
|
||||||
[[0, 2, 1], [3], [4], slice(None)],
|
|
||||||
[[0], [4], [1, 3, 4], slice(None)],
|
|
||||||
[[1], [0, 2, 3], [1], slice(None)],
|
|
||||||
[[[1, 2], [1, 2]], [[0, 1], [2, 3]], [[2, 3], [3, 5]], slice(None)],
|
|
||||||
|
|
||||||
# less dim, ellipsis
|
|
||||||
[Ellipsis, [0, 3, 4]],
|
|
||||||
[Ellipsis, slice(None), [0, 3, 4]],
|
|
||||||
[Ellipsis, slice(None), slice(None), [0, 3, 4]],
|
|
||||||
[slice(None), Ellipsis, [0, 3, 4]],
|
|
||||||
[slice(None), slice(None), Ellipsis, [0, 3, 4]],
|
|
||||||
[slice(None), [0, 2, 3], [1, 3, 4]],
|
|
||||||
[slice(None), [0, 2, 3], [1, 3, 4], Ellipsis],
|
|
||||||
[Ellipsis, [0, 2, 3], [1, 3, 4], slice(None)],
|
|
||||||
[[0], [1, 2, 4]],
|
|
||||||
[[0], [1, 2, 4], slice(None)],
|
|
||||||
[[0], [1, 2, 4], Ellipsis],
|
|
||||||
[[0], [1, 2, 4], Ellipsis, slice(None)],
|
|
||||||
[[1], ],
|
|
||||||
[[0, 2, 1], [3], [4]],
|
|
||||||
[[0, 2, 1], [3], [4], slice(None)],
|
|
||||||
[[0, 2, 1], [3], [4], Ellipsis],
|
|
||||||
[Ellipsis, [0, 2, 1], [3], [4]],
|
|
||||||
]
|
|
||||||
|
|
||||||
for indexer in indices_to_test:
|
|
||||||
assert_get_eq(reference, indexer)
|
|
||||||
assert_set_eq(reference, indexer, 1333)
|
|
||||||
assert_set_eq(reference, indexer, get_set_tensor(reference, indexer))
|
|
||||||
|
|
||||||
indices_to_test += [
|
|
||||||
[slice(None), slice(None), [[0, 1], [1, 0]], [[2, 3], [3, 0]]],
|
|
||||||
[slice(None), slice(None), [[2]], [[0, 3], [4, 4]]],
|
|
||||||
]
|
|
||||||
for indexer in indices_to_test:
|
|
||||||
assert_get_eq(reference, indexer)
|
|
||||||
assert_set_eq(reference, indexer, 1333)
|
|
||||||
assert_backward_eq(reference, indexer)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
+12
-16
@@ -26,22 +26,18 @@ class TestLinAlg(unittest.TestCase):
|
|||||||
orthogonality_helper(V)
|
orthogonality_helper(V)
|
||||||
reconstruction_helper([U,s_diag,V],a)
|
reconstruction_helper([U,s_diag,V],a)
|
||||||
|
|
||||||
def _test_svd_nonfull(self, size):
|
def test_svd_nonfull(self):
|
||||||
a = Tensor.randn(size).realize()
|
sizes = [(2,2),(5,3),(3,5),(2,2,2,2,3)]
|
||||||
U,S,V = a.svd(full_matrices=False)
|
for size in sizes:
|
||||||
b_shape,m,n = size[0:-2],size[-2],size[-1]
|
a = Tensor.randn(size).realize()
|
||||||
k = min(m,n)
|
U,S,V = a.svd(full_matrices=False)
|
||||||
s_diag = (S.unsqueeze(-2) * Tensor.eye(k).reshape((1,) * len(b_shape) + (k,k)).expand(b_shape + (k,k)))
|
b_shape,m,n = size[0:-2],size[-2],size[-1]
|
||||||
#reduced U,V is only orthogonal along smaller dim
|
k = min(m,n)
|
||||||
if (m < n): orthogonality_helper(U),orthogonality_helper(V)
|
s_diag = (S.unsqueeze(-2) * Tensor.eye(k).reshape((1,) * len(b_shape) + (k,k)).expand(b_shape + (k,k)))
|
||||||
else: orthogonality_helper(U.transpose(-2,-1)),orthogonality_helper(V.transpose(-2,-1))
|
#reduced U,V is only orthogonal along smaller dim
|
||||||
reconstruction_helper([U,s_diag,V],a)
|
if (m < n): orthogonality_helper(U),orthogonality_helper(V)
|
||||||
|
else: orthogonality_helper(U.transpose(-2,-1)),orthogonality_helper(V.transpose(-2,-1))
|
||||||
# faster for parallel pytest
|
reconstruction_helper([U,s_diag,V],a)
|
||||||
def test_svd_nonfull_2_2(self): self._test_svd_nonfull((2,2))
|
|
||||||
def test_svd_nonfull_5_3(self): self._test_svd_nonfull((5,3))
|
|
||||||
def test_svd_nonfull_3_5(self): self._test_svd_nonfull((3,5))
|
|
||||||
def test_svd_nonfull_2_2_2_2_3(self): self._test_svd_nonfull((2,2,2,2,3))
|
|
||||||
|
|
||||||
@unittest.skip("very big. recommend wrapping with TinyJit around inner function")
|
@unittest.skip("very big. recommend wrapping with TinyJit around inner function")
|
||||||
def test_svd_large(self):
|
def test_svd_large(self):
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class TestWinograd(unittest.TestCase):
|
|||||||
out = Tensor.conv2d(x,w, padding=1)
|
out = Tensor.conv2d(x,w, padding=1)
|
||||||
out.mean().backward()
|
out.mean().backward()
|
||||||
backward_schedule = Tensor.schedule(x.grad, w.grad)
|
backward_schedule = Tensor.schedule(x.grad, w.grad)
|
||||||
self.assertEqual(len(backward_schedule), 4)
|
self.assertEqual(len(backward_schedule), 5)
|
||||||
|
|
||||||
def test_counters(self):
|
def test_counters(self):
|
||||||
IC, OC, X, Y = 4,4,9,9
|
IC, OC, X, Y = 4,4,9,9
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ def simplify_valid_load(buf:UOp, start_idx:UOp, valid:UOp) -> UOp|None:
|
|||||||
# can drop valid if idx is out of bound when valid is False
|
# can drop valid if idx is out of bound when valid is False
|
||||||
drop_stmt = []
|
drop_stmt = []
|
||||||
for stmt in valid.split_uop(Ops.AND):
|
for stmt in valid.split_uop(Ops.AND):
|
||||||
if (res:=parse_valid(stmt)) is None: continue
|
try: X, is_upper_bound, c = parse_valid(stmt)
|
||||||
X, is_upper_bound, c = res
|
except ValueError: return None
|
||||||
|
|
||||||
# for X0 + X1 + ... >= 1, check if it's out of bound when Xi = 0 for all i
|
# for X0 + X1 + ... >= 1, check if it's out of bound when Xi = 0 for all i
|
||||||
if not is_upper_bound and c == 1 and all(u.op in GroupOp.Irreducible and u.vmin == 0 for u in X.split_uop(Ops.ADD)):
|
if not is_upper_bound and c == 1 and all(u.op in GroupOp.Irreducible and u.vmin == 0 for u in X.split_uop(Ops.ADD)):
|
||||||
|
|||||||
@@ -99,10 +99,14 @@ pm_reduce_collapse = PatternMatcher([
|
|||||||
# MUL casted bool
|
# MUL casted bool
|
||||||
((UPat.var("x") * UPat.var("gate", dtype=dtypes.bool).cast().or_broadcasted(name="b")),
|
((UPat.var("x") * UPat.var("gate", dtype=dtypes.bool).cast().or_broadcasted(name="b")),
|
||||||
lambda x,gate,b=None: gate.broadcast(x.dtype.count).where(x, 0) if b is not None else gate.where(x, 0)),
|
lambda x,gate,b=None: gate.broadcast(x.dtype.count).where(x, 0) if b is not None else gate.where(x, 0)),
|
||||||
# reduce on gated load becomes can substitute the range and remove the reduce
|
# WHERE on LOAD (works on max too)
|
||||||
(UPat.var("buf").index(UPat.var("idx").eq(UPat(Ops.RANGE, name="r").or_casted()).where(UPat.var("expr"), invalid_pat)).load()
|
(UPat.var("gate").where(UPat(Ops.INDEX, src=(UPat.var("buf"), UPat.var("idx"))).load(), 0).reduce(arg=Ops.ADD, allow_any_len=True),
|
||||||
.reduce(arg=Ops.ADD, allow_any_len=True), lambda buf,r,idx,expr,i:
|
lambda buf,idx,gate: buf.index(idx.valid(gate)).load()),
|
||||||
buf.index(expr.substitute({r:idx.cast(r.dtype)}).valid((idx.cast(r.dtype) >= 0) & (idx.cast(r.dtype) < r.src[0]))).load()),
|
(UPat.var("gate").where(0, UPat(Ops.INDEX, src=(UPat.var("buf"), UPat.var("idx"))).load()).reduce(arg=Ops.ADD, allow_any_len=True),
|
||||||
|
lambda buf,idx,gate: buf.index(idx.valid(gate.logical_not())).load()),
|
||||||
|
# INDEX on RANGE / gated RANGE
|
||||||
|
(UPat.var("buf").index(UPat.var("idx").eq(UPat(Ops.RANGE, name="r").or_casted()).where(UPat.var("expr"), invalid_pat)),
|
||||||
|
lambda buf,r,idx,expr,i: buf.index(expr.substitute({r:idx.cast(r.dtype)}).valid((idx.cast(r.dtype) >= 0) & (idx.cast(r.dtype) < r.src[0])))),
|
||||||
# AND on WHERE
|
# AND on WHERE
|
||||||
((UPat.any(UPat(Ops.DEFINE_VAR, name="x"), UPat(Ops.DEFINE_VAR).gep(name="x")) & UPat.var("y")) \
|
((UPat.any(UPat(Ops.DEFINE_VAR, name="x"), UPat(Ops.DEFINE_VAR).gep(name="x")) & UPat.var("y")) \
|
||||||
.where(UPat.cvar("c"), 0).reduce(arg=Ops.ADD, allow_any_len=True, name="r"),
|
.where(UPat.cvar("c"), 0).reduce(arg=Ops.ADD, allow_any_len=True, name="r"),
|
||||||
|
|||||||
+3
-6
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from dataclasses import dataclass, replace
|
from dataclasses import dataclass, replace
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Any, Generic, TypeVar, Iterator, Sequence, cast, Generator
|
from typing import Any, Generic, TypeVar, Iterator, Sequence, cast
|
||||||
import importlib, inspect, functools, pathlib, os, platform, contextlib, sys, re, atexit, pickle, decimal
|
import importlib, inspect, functools, pathlib, os, platform, contextlib, sys, re, atexit, pickle, decimal
|
||||||
from tinygrad.helpers import CI, OSX, LRU, getenv, diskcache_get, diskcache_put, DEBUG, GlobalCounters, flat_mv, PROFILE, temp, colored, CPU_LLVM
|
from tinygrad.helpers import CI, OSX, LRU, getenv, diskcache_get, diskcache_put, DEBUG, GlobalCounters, flat_mv, PROFILE, temp, colored, CPU_LLVM
|
||||||
from tinygrad.helpers import Context, DISABLE_COMPILER_CACHE, ALLOW_DEVICE_USAGE, MAX_BUFFER_SIZE, cpu_events, ProfileEvent, ProfilePointEvent, dedup
|
from tinygrad.helpers import Context, DISABLE_COMPILER_CACHE, ALLOW_DEVICE_USAGE, MAX_BUFFER_SIZE, cpu_events, ProfileEvent, ProfilePointEvent, dedup
|
||||||
@@ -357,7 +357,7 @@ if PROFILE:
|
|||||||
from tinygrad.uop.ops import launch_viz
|
from tinygrad.uop.ops import launch_viz
|
||||||
launch_viz("PROFILE", fn)
|
launch_viz("PROFILE", fn)
|
||||||
|
|
||||||
def enumerate_devices_str() -> Generator[str, None, None]:
|
if __name__ == "__main__":
|
||||||
from tinygrad import Tensor, Device
|
from tinygrad import Tensor, Device
|
||||||
|
|
||||||
for device in ALL_DEVICES:
|
for device in ALL_DEVICES:
|
||||||
@@ -376,7 +376,4 @@ def enumerate_devices_str() -> Generator[str, None, None]:
|
|||||||
result = (colored('PASS', 'green') if any_works else f"{colored('FAIL', 'yellow')}") + ''.join([f'\n{" "*16} {x}' for x in compilers_results])
|
result = (colored('PASS', 'green') if any_works else f"{colored('FAIL', 'yellow')}") + ''.join([f'\n{" "*16} {x}' for x in compilers_results])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result = f"{colored('FAIL', 'red')} {e}"
|
result = f"{colored('FAIL', 'red')} {e}"
|
||||||
yield f"{'*' if device == Device.DEFAULT else ' '} {device:10s}: {result}"
|
print(f"{'*' if device == Device.DEFAULT else ' '} {device:10s}: {result}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
for s in enumerate_devices_str(): print(s)
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from tinygrad.uop.ops import Ops, UOp, sym_infer, sint, Variable, ssimplify, Gro
|
|||||||
from tinygrad.dtype import AddrSpace, PtrDType
|
from tinygrad.dtype import AddrSpace, PtrDType
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from tinygrad.codegen.opt.tc import TensorCore
|
from tinygrad.codegen.opt.tc import TensorCore
|
||||||
from tinygrad.codegen.opt import Opt
|
from tinygrad.codegen.opt.kernel import Opt
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Estimates:
|
class Estimates:
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ class AMDLLVMRenderer(LLVMRenderer):
|
|||||||
(UPat(Ops.WMMA, name="x"), lambda x: UOp(Ops.WMMA, x.dtype, (x.src[0].bitcast(dtypes.uint16.vec(16)), x.src[1].bitcast(dtypes.uint16.vec(16)),
|
(UPat(Ops.WMMA, name="x"), lambda x: UOp(Ops.WMMA, x.dtype, (x.src[0].bitcast(dtypes.uint16.vec(16)), x.src[1].bitcast(dtypes.uint16.vec(16)),
|
||||||
x.src[2]), x.arg) if x.src[0].dtype == dtypes.bfloat16.vec(16) else None),
|
x.src[2]), x.arg) if x.src[0].dtype == dtypes.bfloat16.vec(16) else None),
|
||||||
])
|
])
|
||||||
if self.arch.split(":")[0] in {"gfx1200", "gfx1201"}:
|
if self.arch.split(":")[0] == "gfx1201":
|
||||||
self.extra_matcher += PatternMatcher([
|
self.extra_matcher += PatternMatcher([
|
||||||
(UPat(Ops.WMMA, name="x", dtype=dtypes.bfloat16.vec(8)), lambda x: UOp(Ops.WMMA, dtypes.uint16.vec(8),
|
(UPat(Ops.WMMA, name="x", dtype=dtypes.bfloat16.vec(8)), lambda x: UOp(Ops.WMMA, dtypes.uint16.vec(8),
|
||||||
(x.src[0].bitcast(dtypes.uint16.vec(8)), x.src[1].bitcast(dtypes.uint16.vec(8)), x.src[2].bitcast(dtypes.uint16.vec(8))), (*x.arg,))
|
(x.src[0].bitcast(dtypes.uint16.vec(8)), x.src[1].bitcast(dtypes.uint16.vec(8)), x.src[2].bitcast(dtypes.uint16.vec(8))), (*x.arg,))
|
||||||
|
|||||||
@@ -458,7 +458,7 @@ class AMDProgram(HCQProgram):
|
|||||||
if typ == 5: image[apply_image_offset:apply_image_offset+8] = struct.pack('<q', rel_sym_offset - apply_image_offset + addent) # R_AMDGPU_REL64
|
if typ == 5: image[apply_image_offset:apply_image_offset+8] = struct.pack('<q', rel_sym_offset - apply_image_offset + addent) # R_AMDGPU_REL64
|
||||||
else: raise RuntimeError(f"unknown AMD reloc {typ}")
|
else: raise RuntimeError(f"unknown AMD reloc {typ}")
|
||||||
|
|
||||||
self.lib_gpu = self.dev.allocator.alloc(round_up(image.nbytes, 0x1000), buf_spec:=BufferSpec(nolru=True))
|
self.lib_gpu = self.dev.allocator.alloc(round_up(image.nbytes, 0x1000), buf_spec:=BufferSpec(cpu_access=True, nolru=True))
|
||||||
self.dev.allocator._copyin(self.lib_gpu, image)
|
self.dev.allocator._copyin(self.lib_gpu, image)
|
||||||
self.dev.synchronize()
|
self.dev.synchronize()
|
||||||
|
|
||||||
@@ -819,7 +819,7 @@ class AMDDevice(HCQCompiled):
|
|||||||
f"ppfeaturemask={(ppfeaturemask&~0x8000):#x} (current {ppfeaturemask=:#x} & ~PP_GFXOFF_MASK) to amdgpu module parameters\n"
|
f"ppfeaturemask={(ppfeaturemask&~0x8000):#x} (current {ppfeaturemask=:#x} & ~PP_GFXOFF_MASK) to amdgpu module parameters\n"
|
||||||
"For more information read https://github.com/tinygrad/tinygrad/blob/master/extra/sqtt/README.md")
|
"For more information read https://github.com/tinygrad/tinygrad/blob/master/extra/sqtt/README.md")
|
||||||
SQTT_BUFFER_SIZE = getenv("SQTT_BUFFER_SIZE", 256) # in mb, per shader engine
|
SQTT_BUFFER_SIZE = getenv("SQTT_BUFFER_SIZE", 256) # in mb, per shader engine
|
||||||
self.sqtt_buffers = [self.allocator.alloc(SQTT_BUFFER_SIZE*1024*1024, BufferSpec(nolru=True)) for _ in range(self.se_cnt)]
|
self.sqtt_buffers = [self.allocator.alloc(SQTT_BUFFER_SIZE*1024*1024, BufferSpec(cpu_access=True, nolru=True)) for _ in range(self.se_cnt)]
|
||||||
self.sqtt_itrace_se_mask = getenv("SQTT_ITRACE_SE_MASK", 2) # -1 enable all, 0 disable all, >0 bitmask for where to enable instruction tracing
|
self.sqtt_itrace_se_mask = getenv("SQTT_ITRACE_SE_MASK", 2) # -1 enable all, 0 disable all, >0 bitmask for where to enable instruction tracing
|
||||||
self.sqtt_next_cmd_id = itertools.count(0)
|
self.sqtt_next_cmd_id = itertools.count(0)
|
||||||
cast(AMDComputeQueue, self.hw_compute_queue_t()).sqtt_start(self.sqtt_buffers, self.sqtt_itrace_se_mask).submit(self)
|
cast(AMDComputeQueue, self.hw_compute_queue_t()).sqtt_start(self.sqtt_buffers, self.sqtt_itrace_se_mask).submit(self)
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import functools
|
import functools
|
||||||
from typing import cast
|
|
||||||
from tinygrad.device import Compiled, Compiler, Allocator
|
from tinygrad.device import Compiled, Compiler, Allocator
|
||||||
from tinygrad.engine.jit import MultiGraphRunner
|
from tinygrad.engine.jit import MultiGraphRunner
|
||||||
from tinygrad.renderer.cstyle import Renderer, CStyleLanguage
|
from tinygrad.renderer.cstyle import CStyleLanguage
|
||||||
from tinygrad.renderer.llvmir import AMDLLVMRenderer
|
|
||||||
from tinygrad.uop.ops import Ops
|
from tinygrad.uop.ops import Ops
|
||||||
from tinygrad.helpers import cpu_profile, EMULATE
|
from tinygrad.helpers import cpu_profile
|
||||||
|
|
||||||
class NullRenderer(CStyleLanguage):
|
class NullRenderer(CStyleLanguage):
|
||||||
device = "NULL"
|
device = "NULL"
|
||||||
@@ -31,11 +29,5 @@ class NullGraph(MultiGraphRunner):
|
|||||||
def __call__(self, input_rawbuffers, var_vals, wait=False) -> float|None: return 1e-3
|
def __call__(self, input_rawbuffers, var_vals, wait=False) -> float|None: return 1e-3
|
||||||
|
|
||||||
class NullDevice(Compiled):
|
class NullDevice(Compiled):
|
||||||
def __init__(self, device:str):
|
def __init__(self, device:str): super().__init__(device, NullAllocator(self), [(NullRenderer, Compiler)], functools.partial(NullProgram, device),
|
||||||
renderer:functools.partial|type[Renderer]
|
NullGraph)
|
||||||
match cast(str, EMULATE.value):
|
|
||||||
case "AMD": renderer = functools.partial(AMDLLVMRenderer, "gfx1100")
|
|
||||||
case "AMD_RDNA4": renderer = functools.partial(AMDLLVMRenderer, "gfx1201")
|
|
||||||
case "": renderer = NullRenderer
|
|
||||||
case _: raise RuntimeError(f"can't EMULATE device: {EMULATE.value}")
|
|
||||||
super().__init__(device, NullAllocator(self), [(renderer, Compiler)], functools.partial(NullProgram, device), NullGraph)
|
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ class HCQProgram(Generic[HCQDeviceType]):
|
|||||||
Returns:
|
Returns:
|
||||||
Arguments state with the given buffers and values set for the program.
|
Arguments state with the given buffers and values set for the program.
|
||||||
"""
|
"""
|
||||||
argsbuf = kernargs or self.dev.kernargs_buf.offset(offset=self.dev.kernargs_offset_allocator.alloc(self.kernargs_alloc_size, 8),
|
argsbuf = kernargs or self.dev.kernargs_buf.offset(offset=self.dev.kernargs_offset_allocator.alloc(self.kernargs_alloc_size),
|
||||||
size=self.kernargs_alloc_size)
|
size=self.kernargs_alloc_size)
|
||||||
return self.args_state_t(argsbuf, self, bufs, vals=vals)
|
return self.args_state_t(argsbuf, self, bufs, vals=vals)
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,8 @@ def apply_movement_op(op:Ops, in_shape:tuple[sint,...], arg:tuple, rngs:tuple[UO
|
|||||||
axes_out.append(combined_axes % s)
|
axes_out.append(combined_axes % s)
|
||||||
combined_axes //= s
|
combined_axes //= s
|
||||||
# this simplify is doing a lot of heavy lifting. this is the replacement for the reshape view merging code
|
# this simplify is doing a lot of heavy lifting. this is the replacement for the reshape view merging code
|
||||||
rngs = graph_rewrite(UOp.sink(*axes_out[::-1]), symbolic+pm_simplify_valid+pm_drop_and_clauses, name="reshape").src
|
rngs = graph_rewrite(graph_rewrite(UOp.sink(*axes_out[::-1]), symbolic+pm_simplify_valid, name="reshape"),
|
||||||
|
pm_drop_and_clauses, name="reshape drop ands").src
|
||||||
case _: raise RuntimeError(f"{op} is not a MovementOp")
|
case _: raise RuntimeError(f"{op} is not a MovementOp")
|
||||||
return rngs
|
return rngs
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ earliest_rewrites = PatternMatcher([
|
|||||||
lambda reduce,x: reduce.const_like(identity_element(reduce.arg[0], reduce.dtype)) if x.size == 0 and reduce.size != 0 else None),
|
lambda reduce,x: reduce.const_like(identity_element(reduce.arg[0], reduce.dtype)) if x.size == 0 and reduce.size != 0 else None),
|
||||||
|
|
||||||
# handle size 0
|
# handle size 0
|
||||||
(UPat(GroupOp.All-{Ops.SINK}, name="x"), lambda x: x.const_like(0).rtag(x.tag) if x._shape is not None and x.size == 0 else None),
|
(UPat(GroupOp.All-{Ops.SINK}, name="x"), lambda x: x.const_like(0).rtag(x.tag) if x.st is not None and x.size == 0 else None),
|
||||||
|
|
||||||
# remove contiguous on movement ops before a copy on disk
|
# remove contiguous on movement ops before a copy on disk
|
||||||
(UPat(GroupOp.Movement-{Ops.SHRINK, Ops.RESHAPE}, name="x").f(Ops.CONTIGUOUS).f(Ops.COPY, allow_any_len=True, name="copy"),
|
(UPat(GroupOp.Movement-{Ops.SHRINK, Ops.RESHAPE}, name="x").f(Ops.CONTIGUOUS).f(Ops.COPY, allow_any_len=True, name="copy"),
|
||||||
|
|||||||
+23
-27
@@ -19,9 +19,6 @@ from tinygrad.engine.schedule import ScheduleItem, create_schedule_with_vars
|
|||||||
from tinygrad.schedule.rangeify import get_rangeify_map
|
from tinygrad.schedule.rangeify import get_rangeify_map
|
||||||
from tinygrad.schedule.multi import get_multi_map
|
from tinygrad.schedule.multi import get_multi_map
|
||||||
|
|
||||||
# TODO: this should be the only usage of Device
|
|
||||||
def canonicalize_device(device:str|None) -> str: return Device.canonicalize(device)
|
|
||||||
|
|
||||||
# *** all in scope Tensors are here. this gets relevant UOps ***
|
# *** all in scope Tensors are here. this gets relevant UOps ***
|
||||||
|
|
||||||
all_tensors: dict[weakref.ref[Tensor], None] = {}
|
all_tensors: dict[weakref.ref[Tensor], None] = {}
|
||||||
@@ -116,10 +113,9 @@ class Tensor(MathTrait):
|
|||||||
|
|
||||||
def __init__(self, data:ConstType|bytes|list|tuple|UOp|'np.ndarray'|pathlib.Path|None, # type: ignore [name-defined] # noqa: F821
|
def __init__(self, data:ConstType|bytes|list|tuple|UOp|'np.ndarray'|pathlib.Path|None, # type: ignore [name-defined] # noqa: F821
|
||||||
device:str|tuple|list|None=None, dtype:DTypeLike|None=None, requires_grad:bool|None=None):
|
device:str|tuple|list|None=None, dtype:DTypeLike|None=None, requires_grad:bool|None=None):
|
||||||
|
if dtype is not None: dtype = to_dtype(dtype)
|
||||||
if device is None and isinstance(data, pathlib.Path): device = f"DISK:{data.resolve()}" # keep it on the disk if device is None
|
if device is None and isinstance(data, pathlib.Path): device = f"DISK:{data.resolve()}" # keep it on the disk if device is None
|
||||||
_dtype:DType|None = to_dtype(dtype) if dtype is not None else None
|
device = tuple(Device.canonicalize(x) for x in device) if isinstance(device, (tuple, list)) else Device.canonicalize(device)
|
||||||
_device:str|tuple[str, ...] = tuple(canonicalize_device(x) for x in device) if isinstance(device, (tuple, list)) else canonicalize_device(device)
|
|
||||||
del device, dtype
|
|
||||||
|
|
||||||
# tensors can have gradients if you have called .backward
|
# tensors can have gradients if you have called .backward
|
||||||
self.grad:Tensor|None = None
|
self.grad:Tensor|None = None
|
||||||
@@ -130,41 +126,41 @@ class Tensor(MathTrait):
|
|||||||
|
|
||||||
# create a UOp from the different types of inputs
|
# create a UOp from the different types of inputs
|
||||||
if isinstance(data, UOp):
|
if isinstance(data, UOp):
|
||||||
assert _dtype is None or _dtype==data.dtype, "dtype doesn't match, and casting isn't supported"
|
assert dtype is None or dtype==data.dtype, "dtype doesn't match, and casting isn't supported"
|
||||||
# if data is dtype.index that means that this is a symbolic int and we need to lower it to something we can make a Tensor out of
|
# if data is dtype.index that means that this is a symbolic int and we need to lower it to something we can make a Tensor out of
|
||||||
if data.dtype==dtypes.index: data = _index_to_concrete_int(data)
|
if data.dtype==dtypes.index: data = _index_to_concrete_int(data)
|
||||||
if data.op is Ops.BIND: # type: ignore # mypy type narrowing is bugged here
|
if data.op is Ops.BIND: # type: ignore # mypy type narrowing is bugged here
|
||||||
var, val = data.unbind() # type: ignore
|
var, val = data.unbind() # type: ignore
|
||||||
# give the bound constant a device
|
# give the bound constant a device
|
||||||
const = UOp.const(var.dtype, val, _device, ())
|
const = UOp.const(var.dtype, val, device, ())
|
||||||
data = data.replace(src=(var.replace(src=const.src), const)) # type: ignore
|
data = data.replace(src=(var.replace(src=const.src), const)) # type: ignore
|
||||||
elif data is None: data = UOp.const(_dtype or dtypes.default_float, 0, _device, ())
|
elif data is None: data = UOp.const(dtype or dtypes.default_float, 0, device, ())
|
||||||
elif isinstance(data, get_args(ConstType)): data = UOp.const(_dtype or dtypes.from_py(data), data, _device, ())
|
elif isinstance(data, get_args(ConstType)): data = UOp.const(dtype or dtypes.from_py(data), data, device, ())
|
||||||
elif isinstance(data, bytes): data = _frompy(data, dtypes.uint8 if _dtype is None else _dtype)
|
elif isinstance(data, bytes): data = _frompy(data, dtypes.uint8 if dtype is None else dtype)
|
||||||
elif isinstance(data, (list, tuple)):
|
elif isinstance(data, (list, tuple)):
|
||||||
if _dtype is None:
|
if dtype is None:
|
||||||
if (d := fully_flatten(data)) and all(isinstance(s, bool) for s in d): _dtype = dtypes.bool
|
if (d := fully_flatten(data)) and all(isinstance(s, bool) for s in d): dtype = dtypes.bool
|
||||||
else: _dtype = dtypes.default_int if d and all_int(d) else dtypes.default_float # NOTE: this works because all_int([True, False]) is True
|
else: dtype = dtypes.default_int if d and all_int(d) else dtypes.default_float # NOTE: this works because all_int([True, False]) is True
|
||||||
if _dtype in [dtypes.bfloat16, *dtypes.fp8s]: data = Tensor(_frompy(data, dtypes.float32), device=_device).cast(_dtype).uop
|
if dtype in [dtypes.bfloat16, *dtypes.fp8s]: data = Tensor(_frompy(data, dtypes.float32), device=device).cast(dtype).uop
|
||||||
else: data = _frompy(data, _dtype)
|
else: data = _frompy(data, dtype)
|
||||||
elif is_numpy_ndarray(data):
|
elif is_numpy_ndarray(data):
|
||||||
import numpy as np
|
import numpy as np
|
||||||
assert isinstance(data, np.ndarray), f"expected np.ndarray, got {data}"
|
assert isinstance(data, np.ndarray), f"expected np.ndarray, got {data}"
|
||||||
if data.shape == (): data = UOp.const(_dtype or _from_np_dtype(data.dtype), data.item(), _device, ())
|
if data.shape == (): data = UOp.const(dtype or _from_np_dtype(data.dtype), data.item(), device, ())
|
||||||
else: data = _fromnp(data.astype(npdtype) if _dtype is not None and (npdtype:=_to_np_dtype(_dtype)) is not None else data) # type: ignore [name-defined]
|
else: data = _fromnp(data.astype(npdtype) if dtype is not None and (npdtype:=_to_np_dtype(dtype)) is not None else data) # type: ignore [name-defined]
|
||||||
elif isinstance(data, pathlib.Path):
|
elif isinstance(data, pathlib.Path):
|
||||||
_dtype = _dtype or dtypes.uint8
|
dtype = dtype or dtypes.uint8
|
||||||
data = UOp.new_buffer(f"DISK:{data.resolve()}", data.stat().st_size // _dtype.itemsize, _dtype)
|
data = UOp.new_buffer(f"DISK:{data.resolve()}", data.stat().st_size // dtype.itemsize, dtype)
|
||||||
|
|
||||||
# by this point, it has to be a UOp
|
# by this point, it has to be a UOp
|
||||||
if not isinstance(data, UOp): raise RuntimeError(f"can't create Tensor from {data!r} with type {type(data)}")
|
if not isinstance(data, UOp): raise RuntimeError(f"can't create Tensor from {data!r} with type {type(data)}")
|
||||||
|
|
||||||
# data might be on a different device
|
# data might be on a different device
|
||||||
if isinstance(_device, str): self.uop:UOp = data if data.device == _device else data.copy_to_device(_device)
|
if isinstance(device, str): self.uop:UOp = data if data.device == device else data.copy_to_device(device)
|
||||||
# if device is a tuple, we should have/construct a MultiLazyBuffer
|
# if device is a tuple, we should have/construct a MultiLazyBuffer
|
||||||
elif isinstance(data.device, str): self.uop = Tensor(data).shard(_device).uop
|
elif isinstance(data.device, str): self.uop = Tensor(data).shard(device).uop
|
||||||
else:
|
else:
|
||||||
assert data.device == _device, f"MultiLazyBuffer device mismatch, {data.device} != {_device}"
|
assert data.device == device, f"MultiLazyBuffer device mismatch, {data.device} != {device}"
|
||||||
self.uop = data
|
self.uop = data
|
||||||
|
|
||||||
# add to all_tensors after construction succeeds
|
# add to all_tensors after construction succeeds
|
||||||
@@ -380,7 +376,7 @@ class Tensor(MathTrait):
|
|||||||
"""
|
"""
|
||||||
Moves the tensor to the given device.
|
Moves the tensor to the given device.
|
||||||
"""
|
"""
|
||||||
device = tuple(canonicalize_device(x) for x in device) if isinstance(device, (tuple, list)) else canonicalize_device(device)
|
device = tuple(Device.canonicalize(x) for x in device) if isinstance(device, (tuple, list)) else Device.canonicalize(device)
|
||||||
if device == self.device: return self
|
if device == self.device: return self
|
||||||
if not isinstance(device, str): return self.shard(device)
|
if not isinstance(device, str): return self.shard(device)
|
||||||
ret = Tensor(self.uop, device, requires_grad=self.requires_grad)
|
ret = Tensor(self.uop, device, requires_grad=self.requires_grad)
|
||||||
@@ -405,7 +401,7 @@ class Tensor(MathTrait):
|
|||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
assert isinstance(self.device, str), "can't shard a MultiLazyBuffer"
|
assert isinstance(self.device, str), "can't shard a MultiLazyBuffer"
|
||||||
devices = tuple(canonicalize_device(x) for x in devices)
|
devices = tuple(Device.canonicalize(x) for x in devices)
|
||||||
mlb = self.uop.shard(devices, self._resolve_dim(axis)) if axis is not None else self.uop.copy_to_device(devices)
|
mlb = self.uop.shard(devices, self._resolve_dim(axis)) if axis is not None else self.uop.copy_to_device(devices)
|
||||||
return Tensor(mlb, device=devices, requires_grad=self.requires_grad)
|
return Tensor(mlb, device=devices, requires_grad=self.requires_grad)
|
||||||
|
|
||||||
@@ -494,7 +490,7 @@ class Tensor(MathTrait):
|
|||||||
dtype, shape = to_dtype(dtype) if dtype is not None else dtypes.default_float, argfix(*shape)
|
dtype, shape = to_dtype(dtype) if dtype is not None else dtypes.default_float, argfix(*shape)
|
||||||
if not isinstance(size:=prod([x.vmax if isinstance(x, UOp) else x for x in shape]), int): raise ValueError(f"size must be int {size}")
|
if not isinstance(size:=prod([x.vmax if isinstance(x, UOp) else x for x in shape]), int): raise ValueError(f"size must be int {size}")
|
||||||
# TODO: add test for multidevice tensor
|
# TODO: add test for multidevice tensor
|
||||||
device = tuple(canonicalize_device(d) for d in device) if isinstance(device, tuple) else canonicalize_device(device)
|
device = tuple(Device.canonicalize(d) for d in device) if isinstance(device, tuple) else Device.canonicalize(device)
|
||||||
return Tensor(UOp.new_buffer(device, size, dtype), device, dtype, **kwargs).shrink(((0,prod(shape)),)).reshape(shape)
|
return Tensor(UOp.new_buffer(device, size, dtype), device, dtype, **kwargs).shrink(((0,prod(shape)),)).reshape(shape)
|
||||||
|
|
||||||
def empty_like(self, **kwargs) -> Tensor:
|
def empty_like(self, **kwargs) -> Tensor:
|
||||||
@@ -576,7 +572,7 @@ class Tensor(MathTrait):
|
|||||||
if not dtypes.is_float(dtype := to_dtype(dtype or dtypes.default_float)): raise ValueError(f"rand only supports float dtypes, got {dtype}")
|
if not dtypes.is_float(dtype := to_dtype(dtype or dtypes.default_float)): raise ValueError(f"rand only supports float dtypes, got {dtype}")
|
||||||
if not all_int(shape:=argfix(*shape)) or not all(s >= 0 for s in shape): raise ValueError(f"invalid input {shape=}")
|
if not all_int(shape:=argfix(*shape)) or not all(s >= 0 for s in shape): raise ValueError(f"invalid input {shape=}")
|
||||||
if device is not None and not isinstance(device, str): raise ValueError(f"rand only supports single device, got {device=}")
|
if device is not None and not isinstance(device, str): raise ValueError(f"rand only supports single device, got {device=}")
|
||||||
device = canonicalize_device(device)
|
device = Device.canonicalize(device)
|
||||||
|
|
||||||
# if shape has 0, return zero tensor
|
# if shape has 0, return zero tensor
|
||||||
if (numel := prod(shape)) == 0: return Tensor.zeros(shape, device=device, dtype=dtype, **kwargs)
|
if (numel := prod(shape)) == 0: return Tensor.zeros(shape, device=device, dtype=dtype, **kwargs)
|
||||||
|
|||||||
+23
-111
@@ -175,7 +175,6 @@ class UOp(MathTrait, metaclass=UOpMetaClass):
|
|||||||
|
|
||||||
# *** uop shape stuff ***
|
# *** uop shape stuff ***
|
||||||
|
|
||||||
# TODO: remove this. it's used by the jit and split_reduceop
|
|
||||||
@recursive_property
|
@recursive_property
|
||||||
def st(self) -> ShapeTracker|None:
|
def st(self) -> ShapeTracker|None:
|
||||||
if self.op is Ops.INDEX and self.src[0].op in {Ops.DEFINE_GLOBAL, Ops.DEFINE_LOCAL, Ops.DEFINE_REG, Ops.MSTACK,
|
if self.op is Ops.INDEX and self.src[0].op in {Ops.DEFINE_GLOBAL, Ops.DEFINE_LOCAL, Ops.DEFINE_REG, Ops.MSTACK,
|
||||||
@@ -224,98 +223,12 @@ class UOp(MathTrait, metaclass=UOpMetaClass):
|
|||||||
shape = tuple(1 if i in axis_arg else s for i,s in enumerate(shape))
|
shape = tuple(1 if i in axis_arg else s for i,s in enumerate(shape))
|
||||||
return ShapeTracker.from_shape(shape)
|
return ShapeTracker.from_shape(shape)
|
||||||
|
|
||||||
@recursive_property
|
|
||||||
def _shape(self) -> tuple[sint, ...]|None:
|
|
||||||
match self.op:
|
|
||||||
# late ops don't have shape
|
|
||||||
case Ops.UNIQUE | Ops.DEVICE | Ops.RANGE | Ops.INDEX | Ops.LOAD | Ops.IF | Ops.BARRIER | \
|
|
||||||
Ops.VECTORIZE | Ops.VCONST | Ops.SUBSTITUTE | Ops.GEP | Ops.SPECIAL | Ops.UNROLL | Ops.PRECAST:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# some ops init the shape
|
|
||||||
case Ops.CONST | Ops.DEFINE_VAR | Ops.BIND: return () if self._device is not None else None
|
|
||||||
case Ops.BUFFER: return (self.arg,)
|
|
||||||
case Ops.BUFFER_VIEW: return (self.arg[0],)
|
|
||||||
case Ops.BUFFERIZE: return tuple([int(r.vmax+1) for r in self.src[1:]])
|
|
||||||
case Ops.DEFINE_GLOBAL | Ops.DEFINE_LOCAL | Ops.DEFINE_REG: return (self.ptrdtype.size,)
|
|
||||||
|
|
||||||
# passthrough ops
|
|
||||||
case Ops.REDUCE | Ops.MSTACK | Ops.MSELECT | Ops.DETACH | Ops.CONTIGUOUS | Ops.CONTIGUOUS_BACKWARD | Ops.FUSE: return self.src[0]._shape
|
|
||||||
|
|
||||||
# ops with custom handling
|
|
||||||
case Ops.KERNEL: return self.arg.ast._shape
|
|
||||||
case Ops.STORE:
|
|
||||||
if isinstance(self.dtype, PtrDType): return (self.ptrdtype.size,)
|
|
||||||
if self.dtype is not dtypes.void: return self.src[0].src[0].shape
|
|
||||||
return None
|
|
||||||
|
|
||||||
# TODO: disallow shape changing bitcast
|
|
||||||
case Ops.BITCAST:
|
|
||||||
ps = self.src[0]._shape
|
|
||||||
if ps is None: return None
|
|
||||||
if (output_sz:=self.dtype.itemsize) != (input_sz:=self.src[0].dtype.itemsize): return ps[:-1]+(ssimplify((ps[-1]*input_sz) // output_sz),)
|
|
||||||
return ps
|
|
||||||
|
|
||||||
# TODO: disallow reshape from nothing. tested by TestOpenClip.test_multigpu_clip_score
|
|
||||||
case Ops.RESHAPE:
|
|
||||||
if self.src[0]._shape is None: return tuple(ssimplify(s) for s in self.arg)
|
|
||||||
|
|
||||||
# movement ops change the shape. this is the logic from the old ShapeTracker
|
|
||||||
# NOTE: ssimplify is required because the shape needs to be canonical for broadcasting and same shape checking
|
|
||||||
if self.op in GroupOp.Movement.union({Ops.MULTI, Ops.REDUCE_AXIS, Ops.WMMA}):
|
|
||||||
ps = self.src[0]._shape
|
|
||||||
# TODO: WMMA is used for both axis WMMA and op WMMA. fix this and remove this hack. tested by BERT on AMD LLVM
|
|
||||||
if ps is None and self.op is Ops.WMMA: return None
|
|
||||||
if ps is None: raise RuntimeError(f"movement op {self.op} requires shape")
|
|
||||||
match self.op:
|
|
||||||
case Ops.RESHAPE:
|
|
||||||
if not all(x >= 0 for x in self.arg): raise ValueError(f"shape can't contain negative numbers {self.arg}")
|
|
||||||
if prod(ps) != prod(self.arg): raise ValueError(f"bad reshape: {ps} -> {self.arg}")
|
|
||||||
return tuple(ssimplify(s) for s in self.arg)
|
|
||||||
case Ops.EXPAND:
|
|
||||||
if len(ps) != len(self.arg) or not all(s==ns or (s==1 and ns>=0) for s,ns in zip(ps, self.arg)):
|
|
||||||
raise ValueError(f"bad expand: {ps} -> {self.arg}")
|
|
||||||
return tuple(ssimplify(s) for s in self.arg)
|
|
||||||
case Ops.PERMUTE:
|
|
||||||
if sorted(self.arg) != list(range(len(ps))): raise ValueError(f"invalid permutation {self.arg} of len {len(ps)}")
|
|
||||||
return tuple(ps[i] for i in self.arg)
|
|
||||||
case Ops.PAD:
|
|
||||||
# TODO: why do i need resolve here?
|
|
||||||
if len(ps) != len(self.arg) or not all(resolve(b>=0) and resolve(e>=0) for b,e in self.arg): raise ValueError(f"invalid pad {self.arg}")
|
|
||||||
return tuple(ssimplify(s+b+e) for s,(b,e) in zip(ps, self.arg))
|
|
||||||
case Ops.SHRINK:
|
|
||||||
# TODO: why do i need resolve here?
|
|
||||||
if len(ps) != len(self.arg) or not all(resolve(0<=b) and resolve(b<=e) and resolve(e<=s) for s,(b,e) in zip(ps, self.arg)):
|
|
||||||
raise ValueError(f"invalid shrink {self.arg} for {ps}")
|
|
||||||
return tuple(ssimplify(e-s) for s,e in self.arg)
|
|
||||||
case Ops.FLIP:
|
|
||||||
if len(ps) != len(self.arg) or not all(isinstance(x, bool) for x in self.arg): raise ValueError(f"bad flip on {ps}, {self.arg}")
|
|
||||||
return ps
|
|
||||||
case Ops.MULTI: return tuple(s*len(self.device) if a == self.axis else s for a,s in enumerate(ps))
|
|
||||||
case Ops.REDUCE_AXIS | Ops.WMMA:
|
|
||||||
axis_arg = self.arg[1] if self.op is Ops.REDUCE_AXIS else self.arg[7]
|
|
||||||
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))
|
|
||||||
|
|
||||||
# elementwise ops keep the shape the same. all inputs with shape must match
|
|
||||||
if self.op in (GroupOp.Elementwise-{Ops.BITCAST}).union({Ops.COPY, Ops.ASSIGN, Ops.NOOP, Ops.SINK, Ops.ALLREDUCE}):
|
|
||||||
# TODO: remove this hack for 3 op assign
|
|
||||||
input_shapes = [x._shape for x in (self.src[:2] if self.op is Ops.ASSIGN else self.src) if x._shape is not None]
|
|
||||||
if len(input_shapes) == 0: return None
|
|
||||||
if not all_same(input_shapes): raise RuntimeError(f"shape mismatch at {self.op}: {input_shapes}")
|
|
||||||
return input_shapes[0]
|
|
||||||
|
|
||||||
# all Ops must be explicitly handled
|
|
||||||
raise NotImplementedError(f"no shape handling for {self.op} with {self.dtype}")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def shape(self) -> tuple[sint, ...]:
|
def shape(self) -> tuple[sint, ...]:
|
||||||
if (ret:=self._shape) is None: raise RuntimeError(f"shape requested, but {self.op} doesn't have a shape")
|
assert self.st is not None, f"{self.op} doesn't have a shape"
|
||||||
return ret
|
return unwrap(self.st).shape
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def size(self) -> int: return prod([int(x.vmax) if isinstance(x, UOp) else x for x in self.shape])
|
def size(self) -> int: return self.arg[0] if self.op is Ops.BUFFER_VIEW else self.arg if self.op is Ops.BUFFER else unwrap(self.st).size
|
||||||
|
|
||||||
# determine what ranges this is in
|
# determine what ranges this is in
|
||||||
@recursive_property
|
@recursive_property
|
||||||
@@ -377,7 +290,7 @@ class UOp(MathTrait, metaclass=UOpMetaClass):
|
|||||||
def __getitem__(self, idx): return self.index(idx)
|
def __getitem__(self, idx): return self.index(idx)
|
||||||
def const_like(self, b:ConstLike):
|
def const_like(self, b:ConstLike):
|
||||||
# constants can optionally have a DEVICE source
|
# constants can optionally have a DEVICE source
|
||||||
return UOp.const(self.dtype, b, device=self._device, shape=self._shape)
|
return UOp.const(self.dtype, b, device=self._device, shape=self.shape if self.st is not None else None)
|
||||||
def broadcast(self, count:int):
|
def broadcast(self, count:int):
|
||||||
assert self.dtype.count == 1
|
assert self.dtype.count == 1
|
||||||
if count == 1: return self
|
if count == 1: return self
|
||||||
@@ -431,7 +344,7 @@ class UOp(MathTrait, metaclass=UOpMetaClass):
|
|||||||
return ret.reshape(tuple([x if i not in axis else 1 for i,x in enumerate(self.shape)]))
|
return ret.reshape(tuple([x if i not in axis else 1 for i,x in enumerate(self.shape)]))
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def invalid(count=1): return UOp(Ops.CONST, dtypes.index.vec(count), src=(), arg=Invalid)
|
def invalid(count=1): return UOp(Ops.CONST, dtypes.index.vec(count), src=(), arg=Invalid)
|
||||||
def valid(self, cond): return self if cond.op is Ops.WHERE and cond.arg else cond.where(self, UOp.invalid(self.dtype.count))
|
def valid(self, cond): return cond.where(self, UOp.invalid(self.dtype.count))
|
||||||
def get_idx(self) -> UOp:
|
def get_idx(self) -> UOp:
|
||||||
assert self.dtype.scalar() is dtypes.index, "Can only call get_idx on index dtype"
|
assert self.dtype.scalar() is dtypes.index, "Can only call get_idx on index dtype"
|
||||||
return self.src[1] if self.op is Ops.WHERE and self.src[2].arg is Invalid else self
|
return self.src[1] if self.op is Ops.WHERE and self.src[2].arg is Invalid else self
|
||||||
@@ -515,22 +428,19 @@ class UOp(MathTrait, metaclass=UOpMetaClass):
|
|||||||
if self.op is Ops.MULTI: return self.src[0].base # MULTI is really a VIEW
|
if self.op is Ops.MULTI: return self.src[0].base # MULTI is really a VIEW
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def _mop(self, op:Ops, arg, no_reshape_is_no_op:bool=False) -> UOp:
|
def _mop(self, op:Ops, arg) -> UOp:
|
||||||
ret = UOp(op, self.dtype, (self,), arg)
|
ret = UOp(op, self.dtype, (self,), arg)
|
||||||
# for all movement ops, we check shape property
|
if self.st == ret.st: return self # ignore NOOPs, also check ret.st
|
||||||
if ret.shape == self.shape and no_reshape_is_no_op: return self
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def forced_reshape(self, arg:tuple[sint, ...], **kwargs): return UOp(Ops.RESHAPE, kwargs.pop("dtype", self.dtype), src=(self,), arg=arg)
|
def forced_reshape(self, arg:tuple[sint, ...], **kwargs): return UOp(Ops.RESHAPE, kwargs.pop("dtype", self.dtype), src=(self,), arg=arg)
|
||||||
|
|
||||||
# in these four, if the shape doesn't change we can return self
|
def reshape(self, arg:tuple[sint, ...]): return self._mop(Ops.RESHAPE, arg)
|
||||||
def reshape(self, arg:tuple[sint, ...]): return self._mop(Ops.RESHAPE, arg, no_reshape_is_no_op=True)
|
def expand(self, arg:tuple[sint, ...]): return self._mop(Ops.EXPAND, arg)
|
||||||
def expand(self, arg:tuple[sint, ...]): return self._mop(Ops.EXPAND, arg, no_reshape_is_no_op=True)
|
def shrink(self, arg:tuple[tuple[sint, sint], ...]): return self._mop(Ops.SHRINK, arg)
|
||||||
def shrink(self, arg:tuple[tuple[sint, sint], ...]): return self._mop(Ops.SHRINK, arg, no_reshape_is_no_op=True)
|
def pad(self, arg:tuple[tuple[sint, sint], ...]): return self._mop(Ops.PAD, arg)
|
||||||
def pad(self, arg:tuple[tuple[sint, sint], ...]): return self._mop(Ops.PAD, arg, no_reshape_is_no_op=True)
|
def permute(self, arg:tuple[int, ...]): return self._mop(Ops.PERMUTE, arg)
|
||||||
|
def flip(self, arg:tuple[bool, ...]): return self._mop(Ops.FLIP, arg)
|
||||||
# in these two, we have custom logic to check if they are a no-op
|
|
||||||
def permute(self, arg:tuple[int, ...]): return self._mop(Ops.PERMUTE, arg) if arg != tuple(range(len(self.shape))) else self
|
|
||||||
def flip(self, arg:tuple[bool, ...]): return self._mop(Ops.FLIP, arg) if any(arg) and len(arg) == len(self.shape) else self
|
|
||||||
|
|
||||||
# *** uop UNIQUE ***
|
# *** uop UNIQUE ***
|
||||||
|
|
||||||
@@ -935,15 +845,17 @@ class PatternMatcher:
|
|||||||
if (ret:=match(uop, ctx)) is not None and ret is not uop: return ret
|
if (ret:=match(uop, ctx)) is not None and ret is not uop: return ret
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# *** non-blocking UOp tracker ***
|
||||||
|
|
||||||
|
ucount = itertools.count()
|
||||||
|
uop_fields:dict[int, tuple] = {}
|
||||||
|
def track_uop(u:UOp): return u.trace_num
|
||||||
|
|
||||||
# *** tracking pattern matcher ***
|
# *** tracking pattern matcher ***
|
||||||
|
|
||||||
TRACK_MATCH_STATS = ContextVar("TRACK_MATCH_STATS", 2 if VIZ else 0)
|
TRACK_MATCH_STATS = ContextVar("TRACK_MATCH_STATS", 2 if VIZ else 0)
|
||||||
match_stats:dict[UPat, list[int|float]] = dict()
|
match_stats:dict[UPat, list[int|float]] = dict()
|
||||||
|
|
||||||
# TRACK_MATCH_STATS>=2 or VIZ=1 saves all matches
|
|
||||||
ucount = itertools.count()
|
|
||||||
uop_fields:dict[int, tuple] = {}
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class TrackedGraphRewrite:
|
class TrackedGraphRewrite:
|
||||||
loc:tuple[str, int] # location that called graph_rewrite
|
loc:tuple[str, int] # location that called graph_rewrite
|
||||||
@@ -1000,7 +912,7 @@ def track_matches(func):
|
|||||||
loc = ((frm:=sys._getframe(1)).f_code.co_filename, frm.f_lineno)
|
loc = ((frm:=sys._getframe(1)).f_code.co_filename, frm.f_lineno)
|
||||||
depth = len(active_rewrites)
|
depth = len(active_rewrites)
|
||||||
if not tracked_ctxs: add_trace_group(TracingKey(f"default {func.__name__}"))
|
if not tracked_ctxs: add_trace_group(TracingKey(f"default {func.__name__}"))
|
||||||
tracked_ctxs[-1].append(ctx:=TrackedGraphRewrite(loc, args[0].trace_num, [], kwargs.get("name", None), depth, kwargs.get("bottom_up", False)))
|
tracked_ctxs[-1].append(ctx:=TrackedGraphRewrite(loc, track_uop(args[0]), [], kwargs.get("name", None), depth, kwargs.get("bottom_up", False)))
|
||||||
active_rewrites.append(ctx)
|
active_rewrites.append(ctx)
|
||||||
with cpu_profile(kwargs.get("name", "<unnamed>"), "TINY", display=tracking):
|
with cpu_profile(kwargs.get("name", "<unnamed>"), "TINY", display=tracking):
|
||||||
ret = func(*args, **kwargs)
|
ret = func(*args, **kwargs)
|
||||||
@@ -1022,14 +934,14 @@ class TrackedPatternMatcher(PatternMatcher):
|
|||||||
try: ret = match(uop, ctx)
|
try: ret = match(uop, ctx)
|
||||||
except Exception:
|
except Exception:
|
||||||
if TRACK_MATCH_STATS >= 2 and active_rewrites:
|
if TRACK_MATCH_STATS >= 2 and active_rewrites:
|
||||||
active_rewrites[-1].matches.append((uop.trace_num, UOp(Ops.REWRITE_ERROR,src=uop.src,arg=str(sys.exc_info()[1])).trace_num,p.location,0))
|
active_rewrites[-1].matches.append((track_uop(uop), track_uop(UOp(Ops.REWRITE_ERROR,src=uop.src,arg=str(sys.exc_info()[1]))),p.location,0))
|
||||||
raise
|
raise
|
||||||
if ret is not None and ret is not uop:
|
if ret is not None and ret is not uop:
|
||||||
match_stats[p][0] += 1
|
match_stats[p][0] += 1
|
||||||
match_stats[p][3] += (et:=time.perf_counter()-st)
|
match_stats[p][3] += (et:=time.perf_counter()-st)
|
||||||
if TRACK_MATCH_STATS >= 3: print(f"{et*1e6:7.2f} us -- ", printable(p.location))
|
if TRACK_MATCH_STATS >= 3: print(f"{et*1e6:7.2f} us -- ", printable(p.location))
|
||||||
if TRACK_MATCH_STATS >= 2 and isinstance(ret, UOp) and active_rewrites:
|
if TRACK_MATCH_STATS >= 2 and isinstance(ret, UOp) and active_rewrites:
|
||||||
active_rewrites[-1].matches.append((uop.trace_num, ret.trace_num, p.location, et))
|
active_rewrites[-1].matches.append((track_uop(uop), track_uop(ret), p.location, et))
|
||||||
return ret
|
return ret
|
||||||
match_stats[p][2] += time.perf_counter()-st
|
match_stats[p][2] += time.perf_counter()-st
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ symbolic_flat = symbolic+PatternMatcher([
|
|||||||
|
|
||||||
# ******** we take a small aside to "simplify_valid" to rewrite valids ********
|
# ******** we take a small aside to "simplify_valid" to rewrite valids ********
|
||||||
|
|
||||||
def parse_valid(valid:UOp) -> tuple[UOp, bool, int]|None:
|
def parse_valid(valid:UOp) -> tuple[UOp, bool, int]:
|
||||||
# if it's X <= c, returns X, True, c
|
# if it's X <= c, returns X, True, c
|
||||||
# if it's X >= c, returns X, False, c
|
# if it's X >= c, returns X, False, c
|
||||||
|
|
||||||
@@ -395,7 +395,7 @@ def parse_valid(valid:UOp) -> tuple[UOp, bool, int]|None:
|
|||||||
(s0:=valid.src[0]).op is Ops.CMPLT and dtypes.is_int(s0.src[0].dtype): return s0.src[0], False, int(s0.src[1].vmin)
|
(s0:=valid.src[0]).op is Ops.CMPLT and dtypes.is_int(s0.src[0].dtype): return s0.src[0], False, int(s0.src[1].vmin)
|
||||||
# X < c -> X <= c-1
|
# X < c -> X <= c-1
|
||||||
if valid.op is Ops.CMPLT and dtypes.is_int(valid.src[0].dtype): return valid.src[0], True, int((valid.src[1]).vmax)-1
|
if valid.op is Ops.CMPLT and dtypes.is_int(valid.src[0].dtype): return valid.src[0], True, int((valid.src[1]).vmax)-1
|
||||||
return None
|
raise ValueError(f"not able to parse {valid=}")
|
||||||
|
|
||||||
def uop_given_valid(valid:UOp, uop:UOp, try_simplex=True) -> UOp:
|
def uop_given_valid(valid:UOp, uop:UOp, try_simplex=True) -> UOp:
|
||||||
# return simplified uop (might be the same as input)
|
# return simplified uop (might be the same as input)
|
||||||
@@ -403,8 +403,8 @@ def uop_given_valid(valid:UOp, uop:UOp, try_simplex=True) -> UOp:
|
|||||||
# first, parse valid into {expr: (lower_bound, upper_bound)}
|
# first, parse valid into {expr: (lower_bound, upper_bound)}
|
||||||
bounds:defaultdict[UOp, list[ConstType|None]] = defaultdict(lambda: [None, None])
|
bounds:defaultdict[UOp, list[ConstType|None]] = defaultdict(lambda: [None, None])
|
||||||
for stmt in valid.split_uop(Ops.AND):
|
for stmt in valid.split_uop(Ops.AND):
|
||||||
if (res:=parse_valid(stmt)) is None: continue
|
try: expr, is_upper, c = parse_valid(stmt)
|
||||||
expr, is_upper, c = res
|
except ValueError: continue # give up if we cannot parse the valid
|
||||||
bounds[expr][int(is_upper)] = c
|
bounds[expr][int(is_upper)] = c
|
||||||
|
|
||||||
# don't simplify any other gates, can lead to OOB, we substitute them back later
|
# don't simplify any other gates, can lead to OOB, we substitute them back later
|
||||||
@@ -444,7 +444,8 @@ def uop_given_valid(valid:UOp, uop:UOp, try_simplex=True) -> UOp:
|
|||||||
|
|
||||||
def _valid_priority(v: UOp, valids:list[UOp]):
|
def _valid_priority(v: UOp, valids:list[UOp]):
|
||||||
# we want valid that's in other valids' parents to be first, so it's more likely the other valids get simplified
|
# we want valid that's in other valids' parents to be first, so it's more likely the other valids get simplified
|
||||||
return sum(-1 if (res:=parse_valid(v)) is not None and res[0] in other.toposort() else 0 for other in valids)
|
try: return sum(-1 if parse_valid(v)[0] in other.toposort() else 0 for other in valids)
|
||||||
|
except ValueError: return 0
|
||||||
|
|
||||||
def simplify_valid(valid:UOp) -> UOp|None:
|
def simplify_valid(valid:UOp) -> UOp|None:
|
||||||
if valid.op_in_backward_slice_with_self(Ops.LOAD): return None # this should only be for indexing, skip if there's a LOAD
|
if valid.op_in_backward_slice_with_self(Ops.LOAD): return None # this should only be for indexing, skip if there's a LOAD
|
||||||
@@ -473,17 +474,6 @@ def drop_and_clauses(cond:UOp, x:UOp, i:UOp) -> UOp|None:
|
|||||||
if not (dropped_clauses:=[c for c in cond.split_uop(Ops.AND) if not any(r in x.ranges for r in c.ranges)]): return None
|
if not (dropped_clauses:=[c for c in cond.split_uop(Ops.AND) if not any(r in x.ranges for r in c.ranges)]): return None
|
||||||
return functools.reduce(operator.and_, [c for c in cond.split_uop(Ops.AND) if c not in dropped_clauses], UOp.const(dtypes.bool, True)).where(x, i)
|
return functools.reduce(operator.and_, [c for c in cond.split_uop(Ops.AND) if c not in dropped_clauses], UOp.const(dtypes.bool, True)).where(x, i)
|
||||||
pm_drop_and_clauses = PatternMatcher([(UPat.var("cond").where(UPat.var("x", dtype=dtypes.index), invalid_pat), drop_and_clauses)])
|
pm_drop_and_clauses = PatternMatcher([(UPat.var("cond").where(UPat.var("x", dtype=dtypes.index), invalid_pat), drop_and_clauses)])
|
||||||
def where_on_load(l, c1, buf, x):
|
|
||||||
c2 = x.get_valid()
|
|
||||||
duplicate_clauses = [c for c in c1.split_uop(Ops.AND) if c in c2.split_uop(Ops.AND)]
|
|
||||||
# we move the condition from the where to the load _as long as_ the condtition doesn't have some range that would place it inside of a new range
|
|
||||||
# also no data dependent loads!
|
|
||||||
moved_clauses = [c for c in c1.split_uop(Ops.AND) if c not in duplicate_clauses and all(r in x.ranges for r in c.ranges)
|
|
||||||
and not c.op_in_backward_slice_with_self(Ops.LOAD)]
|
|
||||||
if not (removed:=moved_clauses+duplicate_clauses): return None
|
|
||||||
# aditionally we can drop the clause on the where if it already exists in the load
|
|
||||||
remaining_clause = functools.reduce(operator.and_, [c for c in c1.split_uop(Ops.AND) if c not in removed], UOp.const(dtypes.bool, True))
|
|
||||||
return remaining_clause.where(UOp.load(buf.index(x.get_idx().valid(functools.reduce(operator.and_, moved_clauses, c2)), *l.src[1:])), 0)
|
|
||||||
|
|
||||||
pm_simplify_valid = PatternMatcher([
|
pm_simplify_valid = PatternMatcher([
|
||||||
# simplify valid
|
# simplify valid
|
||||||
@@ -529,9 +519,8 @@ sym = symbolic_flat+pm_simplify_valid+PatternMatcher([
|
|||||||
(UPat((Ops.LOAD, Ops.STORE), src=(UPat().index(UPat.const(dtypes.index, Invalid)).or_casted(),), allow_any_len=True, name="x"),
|
(UPat((Ops.LOAD, Ops.STORE), src=(UPat().index(UPat.const(dtypes.index, Invalid)).or_casted(),), allow_any_len=True, name="x"),
|
||||||
lambda x: UOp(Ops.NOOP) if x.op is Ops.STORE else x.const_like(0)), # invalid store does nothing. invalid load produces 0
|
lambda x: UOp(Ops.NOOP) if x.op is Ops.STORE else x.const_like(0)), # invalid store does nothing. invalid load produces 0
|
||||||
# # Where after gated load becomes alt value, TODO: this is sort of duplicated with rules in devectorizer
|
# # Where after gated load becomes alt value, TODO: this is sort of duplicated with rules in devectorizer
|
||||||
(UPat.var("c1").where(UPat(Ops.LOAD, src=(UPat.var("buf").index(UPat.var("x")),), name="l"), 0), where_on_load),
|
(UPat.var("c1").where(UPat(Ops.LOAD, src=(UPat().index(UPat.var("c2").where(UPat(), invalid_pat)).or_casted(),), name="l"), 0),
|
||||||
(UPat.var("c1").where(0, UPat(Ops.LOAD, src=(UPat.var("buf").index(UPat.var("x")),), name="l")),
|
lambda c1,c2,l,i: l.replace(src=(l.src[0],)+l.src[1:]) if all(c in list(c2.split_uop(Ops.AND)) for c in c1.split_uop(Ops.AND)) else None),
|
||||||
lambda l,c1,buf,x: where_on_load(l,c1.logical_not(),buf,x)),
|
|
||||||
# remove VECTORIZE from SINK/BARRIER. TODO: SINK/BARRIER are really the same thing at GLOBAL/LOCAL levels
|
# remove VECTORIZE from SINK/BARRIER. TODO: SINK/BARRIER are really the same thing at GLOBAL/LOCAL levels
|
||||||
(UPat(Ops.BARRIER, name="root"),
|
(UPat(Ops.BARRIER, name="root"),
|
||||||
lambda root: UOp(Ops.BARRIER, root.dtype, tuple(flatten(x.src if x.op in REMOVE_FROM_BARRIER else (x,) for x in root.src)), root.arg)
|
lambda root: UOp(Ops.BARRIER, root.dtype, tuple(flatten(x.src if x.op in REMOVE_FROM_BARRIER else (x,) for x in root.src)), root.arg)
|
||||||
|
|||||||
@@ -287,8 +287,8 @@ def reloader():
|
|||||||
os.execv(sys.executable, [sys.executable] + sys.argv)
|
os.execv(sys.executable, [sys.executable] + sys.argv)
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
def load_pickle(fp:str) -> list:
|
def load_pickle(path:pathlib.Path|None) -> list:
|
||||||
if not (path:=pathlib.Path(fp)).exists(): return []
|
if path is None or not path.exists(): return []
|
||||||
with path.open("rb") as f: return pickle.load(f)
|
with path.open("rb") as f: return pickle.load(f)
|
||||||
|
|
||||||
# NOTE: using HTTPServer forces a potentially slow socket.getfqdn
|
# NOTE: using HTTPServer forces a potentially slow socket.getfqdn
|
||||||
@@ -296,8 +296,8 @@ class TCPServerWithReuse(socketserver.TCPServer): allow_reuse_address = True
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--kernels', type=load_pickle, help='Path to kernels', default=pathlib.Path(temp("rewrites.pkl", append_user=True)))
|
parser.add_argument('--kernels', type=pathlib.Path, help='Path to kernels', default=pathlib.Path(temp("rewrites.pkl", append_user=True)))
|
||||||
parser.add_argument('--profile', type=load_pickle, help='Path to profile', default=pathlib.Path(temp("profile.pkl", append_user=True)))
|
parser.add_argument('--profile', type=pathlib.Path, help='Path to profile', default=pathlib.Path(temp("profile.pkl", append_user=True)))
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||||
@@ -308,8 +308,9 @@ if __name__ == "__main__":
|
|||||||
st = time.perf_counter()
|
st = time.perf_counter()
|
||||||
print("*** viz is starting")
|
print("*** viz is starting")
|
||||||
|
|
||||||
ctxs = get_metadata(args.kernels)
|
ctxs = get_metadata(load_pickle(args.kernels))
|
||||||
profile_ret = get_profile(args.profile)
|
|
||||||
|
profile_ret = get_profile(load_pickle(args.profile))
|
||||||
|
|
||||||
server = TCPServerWithReuse(('', PORT), Handler)
|
server = TCPServerWithReuse(('', PORT), Handler)
|
||||||
reloader_thread = threading.Thread(target=reloader)
|
reloader_thread = threading.Thread(target=reloader)
|
||||||
|
|||||||
Reference in New Issue
Block a user