Productive, portable, and performant GPU programming in Python.
APACHE-2.0 License
Bot releases are visible (Hide)
Published by ailzhang over 2 years ago
In previous versions of Taichi, a matrix can be accessed only with a constant index. As a result, you cannot perform operations such as clamp the minimum element in a vector to 0:
@ti.kernel
def clamp():
... # assume we have a n-d vector A
min_index = 0
for i in range(n):
if A[i] < A[min_index]:
min_index = i
A[min_index] = 0
Of course, you may use the following workaround leveraging loop unrolling. It is, however, neither intuitive nor efficient:
@ti.kernel
def clamp():
... # assume we have a n-d vector A
min_index = 0
for i in ti.static(range(n)):
if A[i] < A[min_index]:
min_index = i
for i in ti.static(range(n)):
if i == min_index:
A[i] = 0
With this new experimental feature of dynamic indexing of matrices, you can now run the former code snippet smoothly. The feature can be enabled by setting ti.init(dynamic_index=True)
.
In v0.9.0, a new implicit FEM (Finite Element Method) example (https://github.com/taichi-dev/taichi/blob/master/python/taichi/examples/simulation/implicit_fem.py) is added, which also illustrates the benefit of having this feature. In this example, a huge (12 × 12) Hessian matrix is constructed for implicit time integration. Without dynamic indexing, the whole matrix construction loop needs to be unrolled, which takes 70 seconds to compile; with dynamic indexing, a traditional loop version can be applied, and the compilation time is shortened to 2.5 seconds.
Adds support for the ti.vulkan
backend on macOS 10.15+ and now you can run GGUI on your macBook. Run the following GGUI examples to try for yourself.
# prerequisites: taichi >= v0.9.0 and macOS >= 10.15
# run GGUI examples
ti example fractal3d_ggui
ti example fem128_ggui
The system would crash if you run Taichi of an earlier version in the Google Colab notebook environment (see #235 for more information). In this release, we refactored our compiler implementation so that Taichi is compatible with Google Colab.
Feel free to run !pip install taichi
to install Taichi and start your Colab journey with it.
Ensuring the developers use the right set of APIs is critical to the long-term stability of Taichi's APIs. In this release, we started to reorganize its package structure and deprecate some obsolete or internal APIs. The following table lists some critical APIs that may concern you.
Category | Deprecated API | Replaced with |
---|---|---|
Builtin | max() |
ti.max() |
Builtin | min() |
ti.min() |
Atomic operation | obj.atomic_add() |
ti.atomic_add() |
Image-specific | ti.imread() |
ti.tools.imread() |
Image-specific | ti.imwrite() |
ti.tools.imwrite() |
Image-specific | ti.imshow() |
ti.tools.imshow() |
Profiler-specific | ti.print_profile_info() |
ti.profiler.print_scoped_profiler_info() |
Profiler-specific | ti.print_kernel_profile_info() |
ti.profiler.print_kernel_profiler_info() |
For a representative list of APIs deprecated in this release, see this Google doc.
Lengthy traceback in an error report, for most of the time, can be distracting, making it hard to locate the code causing the error. In this release, we've removed the trivial traceback that does not concern developers in our error reporting to improve the debugging experience.
Taking the following code snippet as an example:
import taichi as ti
ti.init()
@ti.func
def bar(a):
a = a + 2j
@ti.kernel
def foo():
bar(1)
foo()
Before v0.9.0, the error message looks like this:
[Taichi] Starting on arch=x64
Traceback (most recent call last):
File "error.py", line 13, in <module>
foo()
File "/path_to_taichi/lang/kernel_impl.py", line 709, in wrapped
return primal(*args, **kwargs)
File "/path_to_taichi/lang/kernel_impl.py", line 636, in __call__
key = self.ensure_compiled(*args)
File "/path_to_taichi/lang/kernel_impl.py", line 627, in ensure_compiled
self.materialize(key=key, args=args, arg_features=arg_features)
File "/path_to_taichi/lang/kernel_impl.py", line 493, in materialize
taichi_kernel = _ti_core.create_kernel(taichi_ast_generator,
File "/path_to_taichi/lang/kernel_impl.py", line 488, in taichi_ast_generator
compiled()
File "error.py", line 11, in foo
bar(1)
File "/path_to_taichi/lang/kernel_impl.py", line 76, in decorated
return fun.__call__(*args)
File "/path_to_taichi/lang/kernel_impl.py", line 156, in __call__
ret = self.compiled(*args)
File "error.py", line 7, in bar
a = a + 2j
File "/path_to_taichi/lang/common_ops.py", line 16, in __add__
return ti.add(self, other)
File "/path_to_taichi/lang/ops.py", line 78, in wrapped
return imp_foo(a, b)
File "/path_to_taichi/lang/ops.py", line 63, in imp_foo
return foo(x, y)
File "/path_to_taichi/lang/ops.py", line 427, in add
return _binary_operation(_ti_core.expr_add, _bt_ops_mod.add, a, b)
File "/path_to_taichi/lang/ops.py", line 173, in _binary_operation
a, b = wrap_if_not_expr(a), wrap_if_not_expr(b)
File "/path_to_taichi/lang/ops.py", line 36, in wrap_if_not_expr
return Expr(a) if not is_taichi_expr(a) else a
File "/path_to_taichi/lang/expr.py", line 33, in __init__
self.ptr = impl.make_constant_expr(arg).ptr
File "/path_to_taichi/lang/util.py", line 196, in wrapped
return func(*args, **kwargs)
File "/path_to_taichi/lang/impl.py", line 414, in make_constant_expr
raise ValueError(f'Invalid constant scalar expression: {type(val)}')
ValueError: Invalid constant scalar expression: <class 'complex'>
In v0.9.0, the error message looks like this:
Traceback (most recent call last):
File "/path_to_test/error.py", line 13, in <module>
foo()
File "/path_to_taichi/lang/kernel_impl.py", line 732, in wrapped
raise type(e)('\n' + str(e)) from None
taichi.lang.exception.TaichiTypeError:
On line 11 of file "/path_to_test/error.py", in foo:
bar(1)
^^^^^^
On line 7 of file "/path_to_test/error.py", in bar:
a = a + 2j
^^^^^^
Invalid constant scalar data type: <class 'complex'>
To improve the readability and user-friendliness of our documentation, we restructured Taichi's documentation site and incorporated API reference into it.
We believe that our community plays a pivotal role in the development of the Taichi programming language. In that spirit, we encourage you to take an active part in our GitHub Discussions, propose potential changes, and contribute your ideas. Together, we improve the Taichi language release by release, for you and for every developer.
The following is a selected list of hot topics for you to start with:
Specifically, because beginners to Taichi sometimes get lost in different APIs such as ti.Vector
, ti.types.vector
, ti.Vector.field
, we plan to make them clearer and would like to have your opinions on these proposed practices:
ti.types.vector
to define a vector type.my_vec2i = ti.types.vector(2, ti.i32)
, use my_vec2i([5, 10])
for a vector object.ti.vector([1, 2])
as a shortcut for ti.types.vector()([1, 2])
, which automatically infers missing type information of the object.ti.field(dtype=my_vec2i, shape=100)
for a field object.See this Google doc for a representative list of APIs deprecated in this release.
Python 3.6 has reached EOL as of December 2021. The next major Taichi release (e.g. v1.0) will be the last official release for Python3.6 and we're actively working on adding support for Python3.10.
Published by ailzhang over 2 years ago
This is a bug fix release for v0.8.10.
If you have seen excessive warnings like below on windows, please upgrade to this release.
a.py:11: UserWarning: Calling non-taichi function "ti.random". Scope inside the function is not processed by the Taichi AST transformer. The function may not work as expected. Proceed with caution! Maybe you can consider turning it into a @ti.func?
a[i] = ti.pow(ti.random(), 2)
a.py:11: UserWarning: Calling non-taichi function "ti.pow". Scope inside the function is not processed by the Taichi AST transformer. The function may not work as expected. Proceed with caution! Maybe you can consider turning it into a @ti.func?
a[i] = ti.pow(ti.random(), 2)
Full changelog:
Published by yolo2themoon almost 3 years ago
Highlights:
Full changelog:
Published by qiao-bo almost 3 years ago
Highlights:
Full changelog:
Published by strongoier almost 3 years ago
Highlights:
Full changelog:
Published by lin-hitonami almost 3 years ago
Full changelog:
Published by Leonz5288 almost 3 years ago
Notes:
We added a function to periodically check version information on ti.init
to remind users if a new version has been released. However, this function is not fully tested when 0.8.6 is released, and the error handling is not well-implemented. Taichi crashes when it fails to check the version (maybe due to network issues). For this reason, 0.8.6 is removed from the official releases in PyPI. Please upgrade to a newer version if you are on this version. We are sorry for the inconvenience.
Full changelog:
Published by ailzhang almost 3 years ago
Full changelog:
Published by yolo2themoon almost 3 years ago
Full changelog:
Published by qiao-bo about 3 years ago
Full changelog:
Published by k-ye about 3 years ago
Full changelog:
ti test
work with -a cpu, cuda
. (#3066) (by Ailing)Published by strongoier about 3 years ago
Full changelog:
Published by qiao-bo about 3 years ago
Previously in Taichi, all non-power-of-two dimensions of a field were automatically padded to a power of two. For instance, a field of shape (18, 65)
would have internal shape (32, 128)
. Although the padding had many benefits such as allowing fast and convenient bitwise operations for coordinate handling, it would consume potentially much more memory than people thought.
For people indeed want smaller memory usage, we now introduce an optional packed mode. In packed mode, no more padding will be applied so a field will not have a larger internal shape when some of its dimensions are not power-of-two. The downside is that the runtime performance will regress slightly.
A switch named packed
for ti.init()
decides whether to use packed mode:
ti.init() # default: packed=False
a = ti.field(ti.i32, shape=(18, 65)) # padded to (32, 128)
ti.init(packed=True)
a = ti.field(ti.i32, shape=(18, 65)) # no padding
A new GUI system, which is codenamed GGUI, is added to Taichi. GGUI will use GPUs for rendering, which enables it to be much faster than the original ti.gui
, and to render 3d meshes and particles. It also comes with a brand new set of immediate mode widgets APIs.
Sample 3D code:
window = ti.ui.Window("Hello Taichi", (1920, 1080))
canvas = window.get_canvas()
scene = ti.ui.Scene()
camera = ti.ui.make_camera()
while window.running:
camera.position(...)
camera.lookat(...)
scene.set_camera(camera)
scene.point_light(pos=(...), color=(...))
# vertices, centers, etc. are taichi fields
scene.mesh(vertices, ...)
scene.particles(centers, radius, ...)
canvas.scene(scene)
window.show()
Sample IMGUI code:
window = ti.ui.Window("Hello Taichi", (500, 500))
canvas = window.get_canvas()
gx, gy, gz = (0, -9.8, 0)
while window.running:
window.GUI.begin("Greetings", 0.1, 0.1, 0.8, 0.15)
window.GUI.text("Welcome to TaichiCon !")
if window.GUI.button("Bye"):
window.running = False
window.GUI.end()
window.GUI.begin("Gravity", 0.1, 0.3, 0.8, 0.3)
gx = window.GUI.slider_float("x", gx, -10, 10)
gy = window.GUI.slider_float("y", gy, -10, 10)
gz = window.GUI.slider_float("z", gz, -10, 10)
window.GUI.end()
canvas.set_background_color(color)
window.show()
For more examples, please checkout examples/ggui_examples
in the taichi repo.
Previously in Taichi, we cannot allocate new fields after the kernel's execution. Now we can use a new class FieldsBuilder
to support dynamic allocation.
FieldsBuilder
has the same data structure declaration API as the previous root
, such as dense()
, pointer()
etc. After declaration, we need to call the finalize()
function to compile the FieldsBuilder
to an SNodeTree
object.
Example usage for FieldsBuilder
:
import taichi as ti
ti.init()
@ti.kernel
def func(v: ti.template()):
for I in ti.grouped(v):
v[I] += 1
fb = ti.FieldsBuilder()
x = ti.field(dtype = ti.f32)
fb.dense(ti.ij, (5, 5)).place(x)
fb_snode_tree = fb.finalize() # Finalizing the FieldsBuilder and returns a SNodeTree
func(x)
fb2 = ti.FieldsBuilder()
y = ti.field(dtype = ti.f32)
fb2.dense(ti.i, 5).place(y)
fb2_snode_tree = fb2.finalize() # Finalizing the FieldsBuilder and returns a SNodeTree
func(y)
Additionally, root
now is implemented by FieldsBuilder
implicitly, so we can allocate the fields directly under root
.
import taichi as ti
ti.init() # ti.root = ti.FieldsBuilder()
@ti.kernel
def func(v: ti.template()):
for I in ti.grouped(v):
v[I] += 1
x = ti.field(dtype = ti.f32)
ti.root.dense(ti.ij, (5, 5)).place(x)
func(x) # automatically called ti.root.finalize()
# ti.root = new ti.FieldsBuilder()
y = ti.field(dtype = ti.f32)
ti.root.dense(ti.i, 5).place(y)
func(y) # automatically called ti.root.finalize()
Furthermore, after we called the finalize()
of a FieldsBuilder
, it will return a finalized SNodeTree
object. If we do not want to use the fields under this SNodeTree
, we could call destroy()
manually to recycle the memory into the memory pool.
e.g.:
import taichi as ti
ti.init()
@ti.kernel
def func(v: ti.template()):
for I in ti.grouped(v):
v[I] += 1
fb = ti.FieldsBuilder()
x = ti.field(dtype = ti.f32)
fb.dense(ti.ij, (5, 5)).place(x)
fb_snode_tree = fb.finalize() # Finalizing the FieldsBuilder and returns a SNodeTree
func(x)
fb_snode_tree.destroy()
# func(x) cannot be used anymore
Published by strongoier about 3 years ago
Full changelog:
Full changelog:
Published by strongoier about 3 years ago
Full changelog:
Published by ailzhang about 3 years ago
Full changelog:
Published by k-ye about 3 years ago
Full changelog:
Published by k-ye over 3 years ago
Highlights
Starting from this release, you can create new ti.field
instances after invoking a kernel! 🎉 Note that this feature is currently supported on the CPU and the CUDA backend.
Full changelog:
Published by k-ye over 3 years ago
Full changelog: