运算符
在这里,我们介绍 Taichi 中支持的基本类型和复合类型(如矩阵)的运算符。
基本类型支持的运算符
算数运算符
| 运算 | 结果 | 
|---|---|
| -a | a取反 | 
| +a | a不变 | 
| a + b | a与b求和 | 
| a - b | a与b的差 | 
| a * b | a与b的乘积 | 
| a / b | a与b的商 | 
| a // b | a与b的商向下取整 | 
| a % b | a / b的余数 | 
| a ** b | a的b次幂 | 
note
Taichi 的 % 操作符遵循 Python 风格而不是 C 语言风格,例如:
# In Taichi-scope or Python-scope:
print(2 % 3)   # 2
print(-2 % 3)  # 1
要想使用 C 语言风格的取模运算(%),请使用 ti.raw_mod。 此函数也可以接收浮点数作为参数。
ti.raw_mod(a, b) 返回 a - b * int(float(a) / b)。
print(ti.raw_mod(2, 3))      # 2
print(ti.raw_mod(-2, 3))     # -2
print(ti.raw_mod(3.5, 1.5))  # 0.5
note
Python3 区分了 /(标准除法)和 //(整数除法),例如:1.0 / 2.0 = 0.5,1 / 2 = 0.5,1 // 2 = 0,4.2 / 2 = 2。 Taichi 遵循相同的设计:
- 标准除法首先会将整型的操作数转换为默认的浮点类型。
- 整数除法首先会将浮点类型的操作数转换为默认的整型。
为了避免这种隐式的类型转换,你可以使用 ti.cast 手动将操作数转换为需要的类型。 请参阅 默认精度 了解更多关于默认数值类型的详细信息。
Taichi 也提供 ti.raw_div 函数,如果其中一个操作数是浮点类型,则执行标准除法,如果两个操作数都是整数类型,则执行整数除法。
print(ti.raw_div(5, 2))    # 2
print(ti.raw_div(5, 2.0))  # 2.5
比较运算符
| 运算 | 结果 | 
|---|---|
| a == b | 若 a等于b,则为 True,否则为 False | 
| a != b | 若 a不等于b,则为 True,否则为 False | 
| a > b | 若 a严格大于b,则为 True,否则为 False | 
| a < b | 若 a严格小于b,则为 True,否则为 False | 
| a >= b | 若 a大于或等于b,则为 True,否则为 False | 
| a <= b | 若 a小于或等于b,则为 True,否则为 False | 
逻辑运算符
| 运算 | 结果 | 
|---|---|
| not a | 若 a为 False,则为 True,否则为 False | 
| a or b | 若 a为 False,则为b的值,否则为a的值 | 
| a and b | 若 a为 False,则为a的值,否则为b的值 | 
条件运算符
条件表达式 a if cond else b,当 cond 为 True 时,值为 a,否则值为 b。 a 和 b 必须具有相同的类型。
条件表达式进行短路求值,这意味着不会对未选定的分支进行求值。
a = ti.field(ti.i32, shape=(10,))
for i in range(10):
    a[i] = i
@ti.kernel
def cond_expr(ind: ti.i32) -> ti.i32:
    return a[ind] if ind < 10 else 0
cond_expr(3)  # returns 3
cond_expr(10)  # returns 0, a[10] is not evaluated
对于 Taichi 向量和矩阵的逐元素条件运算,Taichi 提供了 ti.select(cond, a, b) 函数,其中 不 进行短路求值。
cond = ti.Vector([1, 0])
a = ti.Vector([2, 3])
b = ti.Vector([4, 5])
ti.select(cond, a, b)  # ti.Vector([2, 5])
位运算符
| 运算 | 结果 | 
|---|---|
| ~a | a逐位取反 | 
| a & b | a和b按位与 | 
| a ^ b | a和b按位异或 | 
| a | b | a和b按位或 | 
| a << b | a左移b位 | 
| a >> b | a右移b位 | 
三角函数
ti.sin(x)
ti.cos(x)
ti.tan(x)
ti.asin(x)
ti.acos(x)
ti.atan2(x, y)
ti.tanh(x)
其他数学函数
ti.sqrt(x)
ti.rsqrt(x)  # A fast version for `1 / ti.sqrt(x)`.
ti.exp(x)
ti.log(x)
ti.round(x, dtype=None)
ti.floor(x, dtype=None)
ti.ceil(x, dtype=None)
ti.sum(x)
ti.max(x, y, ...)
ti.min(x, y, ...)
ti.abs(x)  # Same as `abs(x)`
ti.pow(x, y)  # Same as `pow(x, y)` and `x ** y`
round、floor 和 ceil 函数中的 dtype 参数指定返回值的数据类型。 默认值 None 表示返回的类型与输入 x 相同。
内置函数
abs(x)  # Same as `ti.abs(x, y)`
pow(x, y)  # Same as `ti.pow(x, y)` and `x ** y`.
随机数生成器
ti.random(dtype=float)
note
ti.random 支持 u32, i32, u64, i64 以及所有浮点型。 返回值的范围视类型而定。
| 类型 | 范围 | 
|---|---|
| i32 | -2,147,483,648 至 2,147,483,647 | 
| u32 | 0 至 4,294,967,295 | 
| i64 | -9,223,372,036,854,775,808 至 9,223,372,036,854,775,807 | 
| u64 | 0 至 18,446,744,073,709,551,615 | 
| floating point | 0.0 至 1.0 | 
支持的原子操作
在 Taichi 中,增强赋值语句(如:x[i] += 1)自动是 原子的。
warning
在并行模式下修改全局变量时,请确保使用原子操作。 例如,要计算 x 所有元素的总和:
@ti.kernel
def sum():
    for i in x:
        # Approach 1: OK
        total[None] += x[i]
        # Approach 2: OK
        ti.atomic_add(total[None], x[i])
        # Approach 3: Wrong result since the operation is not atomic.
        total[None] = total[None] + x[i]
note
当试图在局部变量上应用原子操作时,Taichi 编译器将尝试将这些操作转换为对应的非原子操作。
除了增强赋值语句以外,显式原子操作(如:ti.atomic_add)也会以原子方式执行读-改-写。 这些操作会额外返回第一个参数的 旧值。 例如,
x[i] = 3
y[i] = 4
z[i] = ti.atomic_add(x[i], y[i])
# now x[i] = 7, y[i] = 4, z[i] = 3
下面是所有显式原子操作的列表:
| 运算 | 行为 | 
|---|---|
| ti.atomic_add(x, y) | 原子地计算 x + y,将结果存储在x中,并返回x的旧值 | 
| ti.atomic_sub(x, y) | 原子地计算 x - y,将结果存储在x中,并返回x的旧值 | 
| ti.atomic_and(x, y) | 原子地计算 x & y,将结果存储在x中,并返回x的旧值 | 
| ti.atomic_or(x, y) | 原子地计算 x | y,将结果存储在x中,并返回x的旧值 | 
| ti.atomic_xor(x, y) | 原子地计算 x ^ y,将结果存储在x中,并返回x的旧值 | 
| ti.atomic_max(x, y) | 原子地计算 max(x, y),将结果存储在x中,并返回x的旧值 | 
| ti.atomic_min(x, y) | 原子地计算 min(x, y),将结果存储在x中,并返回x的旧值 | 
note
不同后端的原子操作支持情况:
| 类型 | CPU | CUDA | OpenGL | Metal | C source | 
|---|---|---|---|---|---|
| i32 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | 
| f32 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | 
| i64 | ✔️ | ✔️ | ⭕ | ❌ | ✔️ | 
| f64 | ✔️ | ✔️ | ⭕ | ❌ | ✔️ | 
(⭕:需要扩展。)
支持的矩阵运算符
上面提到的基本类型运算也适用于复合类型(如矩阵)。 在这种情形下,运算是逐元素进行的。 例如:
B = ti.Matrix([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
C = ti.Matrix([[3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
A = ti.sin(B)
# is equivalent to
for i in ti.static(range(2)):
    for j in ti.static(range(3)):
        A[i, j] = ti.sin(B[i, j])
A = B ** 2
# is equivalent to
for i in ti.static(range(2)):
    for j in ti.static(range(3)):
        A[i, j] = B[i, j] ** 2
A = B ** C
# is equivalent to
for i in ti.static(range(2)):
    for j in ti.static(range(3)):
        A[i, j] = B[i, j] ** C[i, j]
A += 2
# is equivalent to
for i in ti.static(range(2)):
    for j in ti.static(range(3)):
        A[i, j] += 2
A += B
# is equivalent to
for i in ti.static(range(2)):
    for j in ti.static(range(3)):
        A[i, j] += B[i, j]
此外,Taichi 支持以下矩阵运算:
a = ti.Matrix([[2, 3], [4, 5]])
a.transpose()   # the transposed matrix of `a`, will not effect the data in `a`.
a.trace()       # the trace of matrix `a`, the returned scalar value can be computed as `a[0, 0] + a[1, 1] + ...`.
a.determinant() # the determinant of matrix `a`.
a.inverse()     # (ti.Matrix) the inverse of matrix `a`.
a@a             # @ denotes matrix multiplication
note
For now, determinant() and inverse() only works in Taichi-scope, and the size of the matrix must be 1x1, 2x2, 3x3 or 4x4.