diff --git a/.gitignore b/.gitignore index 6e219c6c33fb2..da03592f06274 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ *.swp *.swo /.vs -/tags /.*_localrc +/tags /Debug *.sdf /x64 diff --git a/python/taichi/lang/expr.py b/python/taichi/lang/expr.py index 9ab2e0c842304..0c33262f655cd 100644 --- a/python/taichi/lang/expr.py +++ b/python/taichi/lang/expr.py @@ -36,7 +36,7 @@ def __init__(self, *args, tb=None): @python_scope def __setitem__(self, key, value): - impl.get_runtime().try_materialize() + impl.get_runtime().materialize() self.initialize_accessor() if key is None: key = () @@ -49,7 +49,7 @@ def __setitem__(self, key, value): @python_scope def __getitem__(self, key): - impl.get_runtime().try_materialize() + impl.get_runtime().materialize() self.initialize_accessor() if key is None: key = () @@ -114,7 +114,7 @@ def fill(self, val): from .meta import fill_tensor fill_tensor(self, val) - @deprecated('tensor.parent()', 'tensor.snode().parent()') + #@deprecated('tensor.parent()', 'tensor.snode().parent()') def parent(self, n=1): import taichi as ti p = self.snode().parent(n) diff --git a/python/taichi/lang/impl.py b/python/taichi/lang/impl.py index 9766ce716e262..3b5002bbfa293 100644 --- a/python/taichi/lang/impl.py +++ b/python/taichi/lang/impl.py @@ -150,7 +150,6 @@ def __init__(self, kernels=None): self.target_tape = None self.inside_complex_kernel = False self.kernels = kernels or [] - Expr.materialize_layout_callback = self.materialize def get_num_compiled_functions(self): return len(self.compiled_functions) + len(self.compiled_grad_functions) @@ -169,15 +168,10 @@ def create_program(self): if self.prog is None: self.prog = taichi_lang_core.Program() - def try_materialize(self): - if not Expr.layout_materialized: - Expr.materialize_layout_callback() - def materialize(self): if self.materialized: return self.create_program() - Expr.layout_materialized = True def layout(): for func in self.layout_functions: @@ -195,8 +189,7 @@ def clear(self): if self.prog: self.prog.finalize() self.prog = None - Expr.materialize_layout_callback = None - Expr.layout_materialized = False + self.materialized = False def get_tape(self, loss=None): from .tape import Tape @@ -297,8 +290,13 @@ def var(dt, shape=None, offset=None, needs_grad=False): assert (offset is not None and shape is None ) == False, f'The shape cannot be None when offset is being set' - assert not get_runtime( - ).materialized, 'No new variables can be declared after kernel invocations or Python-scope tensor accesses.' + if get_runtime().materialized: + raise RuntimeError( + "No new variables can be declared after materialization, i.e. kernel invocations " + "or Python-scope tensor accesses. I.e., data layouts must be specified before " + "any computation. Try appending ti.init() or ti.reset() " + "right after 'import taichi as ti' if you are using Jupyter notebook." + ) # primal x = Expr(taichi_lang_core.make_id_expr("")) diff --git a/python/taichi/lang/snode.py b/python/taichi/lang/snode.py index 3b84d328625ce..b7d36a277f6d7 100644 --- a/python/taichi/lang/snode.py +++ b/python/taichi/lang/snode.py @@ -53,7 +53,7 @@ def lazy_grad(self): self.ptr.lazy_grad() def parent(self, n=1): - impl.get_runtime().try_materialize() + impl.get_runtime().materialize() p = self.ptr while p and n > 0: p = p.parent @@ -78,7 +78,7 @@ def dim(self): @property def shape(self): - impl.get_runtime().try_materialize() + impl.get_runtime().materialize() dim = self.ptr.num_active_indices() ret = [self.ptr.get_num_elements_along_axis(i) for i in range(dim)] diff --git a/python/taichi/misc/test.py b/python/taichi/misc/test.py index 1994442110ba2..45dcbefb096f3 100644 --- a/python/taichi/misc/test.py +++ b/python/taichi/misc/test.py @@ -23,3 +23,11 @@ def __ne__(self, other): def allclose(x, y, **kwargs): return x == approx(y, **kwargs) + + +def make_temp_file(*args, **kwargs): + import os + from tempfile import mkstemp + fd, name = mkstemp(*args, **kwargs) + os.close(fd) + return name diff --git a/tests/python/test_image_io.py b/tests/python/test_image_io.py index 85e5e511e8169..b55f90c4be357 100644 --- a/tests/python/test_image_io.py +++ b/tests/python/test_image_io.py @@ -1,14 +1,8 @@ import taichi as ti import numpy as np +from taichi import make_temp_file import pytest import os -from tempfile import mkstemp - - -def make_temp(*args, **kwargs): - fd, name = mkstemp(*args, **kwargs) - os.close(fd) - return name # jpg is also supported but hard to test here since it's lossy: @@ -28,7 +22,7 @@ def test_image_io(resx, resy, comp, ext, is_tensor, dt): pixel = np.random.randint(256, size=shape, dtype=ti.to_numpy_type(dt)) if is_tensor: pixel_t.from_numpy(pixel) - fn = make_temp(suffix='.' + ext) + fn = make_temp_file(suffix='.' + ext) if is_tensor: ti.imwrite(pixel_t, fn) else: @@ -50,7 +44,7 @@ def test_image_io_vector(resx, resy, comp, ext, dt): pixel = np.random.rand(*shape, comp).astype(ti.to_numpy_type(dt)) pixel_t = ti.Vector(comp, dt, shape) pixel_t.from_numpy(pixel) - fn = make_temp(suffix='.' + ext) + fn = make_temp_file(suffix='.' + ext) ti.imwrite(pixel_t, fn) pixel_r = (ti.imread(fn).astype(ti.to_numpy_type(dt)) + 0.5) / 256.0 assert np.allclose(pixel_r, pixel, atol=2e-2) @@ -70,7 +64,7 @@ def test_image_io_uint(resx, resy, comp, ext, dt): pixel = np.random.randint(256, size=(*shape, comp), dtype=np_type) * np_max pixel_t = ti.Vector(comp, dt, shape) pixel_t.from_numpy(pixel) - fn = make_temp(suffix='.' + ext) + fn = make_temp_file(suffix='.' + ext) ti.imwrite(pixel_t, fn) pixel_r = ti.imread(fn).astype(np_type) * np_max assert (pixel_r == pixel).all() diff --git a/tests/python/test_runtime.py b/tests/python/test_runtime.py new file mode 100644 index 0000000000000..a46c5eeab14dc --- /dev/null +++ b/tests/python/test_runtime.py @@ -0,0 +1,60 @@ +import taichi as ti +from taichi import make_temp_file +import sys, os + + +def test_without_init(): + # We want to check if Taichi works well without ``ti.init()``. + # But in test ``ti.init()`` will always be called in last ``@ti.all_archs``. + # So we have to create a new Taichi instance, i.e. test in a sandbox. + content = ''' +import taichi as ti +assert ti.cfg.arch == ti.cpu + +x = ti.var(ti.i32, (2, 3)) +assert x.shape == (2, 3) + +x[1, 2] = 4 +assert x[1, 2] == 4 +''' + filename = make_temp_file() + with open(filename, 'w') as f: + f.write(content) + assert os.system(f'{sys.executable} {filename}') == 0 + + +@ti.all_archs +@ti.must_throw(RuntimeError) +def test_materialization_after_kernel(): + x = ti.var(ti.f32, (3, 4)) + + @ti.kernel + def func(): + print(x[2, 3]) + + func() + + y = ti.var(ti.f32, (2, 3)) + # ERROR: No new variable should be declared after kernel invocation! + + +@ti.all_archs +@ti.must_throw(RuntimeError) +def test_materialization_after_access(): + x = ti.var(ti.f32, (3, 4)) + + print(x[2, 3]) + + y = ti.var(ti.f32, (2, 3)) + # ERROR: No new variable should be declared after Python-scope tensor access! + + +@ti.all_archs +@ti.must_throw(RuntimeError) +def test_materialization_after_get_shape(): + x = ti.var(ti.f32, (3, 4)) + + print(x.shape) + + y = ti.var(ti.f32, (2, 3)) + # ERROR: No new variable should be declared after Python-scope tensor access!