结构节点 (SNodes)

在编写计算部分的代码之后,用户需要设定内部层次数据结构。包括微观和宏观两部分,宏观上设定层级数据结构组件之间的嵌套关系以及表示稀疏性的方式;微观上,描述数据如何分组(例如,SOA 或 AOS)。Taichi 提供了 结构节点 (SNodes) 以满足不同层级数据结构构建时的需求。其结构和语义具体如下所示:

  • 稠密集合(dense):固定长度的连续数组。
  • 位掩码集合(bitmasked):类似于稠密集合,但实现了通过掩码保持数据的稀疏信息。比如为稠密集合的元素分配掩码来记录稀疏信息。
  • 指针集合(pointer):存储指针而不是整个结构,以节省内存和保持稀疏性。
  • 动态集合(dynamic):可变长度数组,具有预定义的最大长度。它扮演着 C++ 中的 std::vector 或者是 Python 中的 list 这样的角色,可以用来维护包含在一个块(block)中的对象(例如粒子)。

你可以在 Advanced dense layouts 章节中了解更多详细信息。 ti.root 是层级数据结构的根结点.

snode.place(x, ...)
参数:
  • snode – (结构节点) 放置(place)操作的目标
  • x – (张量) 要放置的张量对象
返回:

(结构节点) snode 对象

以下示例代码放置了 xy 两个零维张量:

x = ti.var(dt=ti.i32)
y = ti.var(dt=ti.f32)
ti.root.place(x, y)
assert x.snode() == y.snode()
tensor.shape()
参数:tensor – (张量)
返回:(整数元组) 张量的形状

Equivalent to tensor.snode().shape.

例如,

ti.root.dense(ti.ijk, (3, 5, 4)).place(x)
x.shape # returns (3, 5, 4)
tensor.snode()
参数:tensor – (张量)
返回:(SNode) the structual node where tensor is placed
x = ti.var(dt=ti.i32)
y = ti.var(dt=ti.f32)
ti.root.place(x, y)
x.snode()
snode.shape()
参数:snode – (结构节点)
返回:(tuple) the size of node along that axis
blk1 = ti.root
blk2 = blk1.dense(ti.i,  3)
blk3 = blk2.dense(ti.jk, (5, 2))
blk4 = blk3.dense(ti.k,  2)
blk1.shape  # ()
blk2.shape  # (3, )
blk3.shape  # (3, 5, 2)
blk4.shape  # (3, 5, 4)
snode.parent(n = 1)
参数:
  • snode – (结构节点)
  • n – (optional, scalar) the number of steps, i.e. n=1 for parent, n=2 grandparent, etc.
返回:

(结构节点) snode 的父节点

blk1 = ti.root.dense(ti.i, 8)
blk2 = blk1.dense(ti.j, 4)
blk3 = blk2.bitmasked(ti.k, 6)
blk1.parent()  # ti.root
blk2.parent()  # blk1
blk3.parent()  # blk2
blk3.parent(1) # blk2
blk3.parent(2) # blk1
blk3.parent(3) # ti.root
blk3.parent(4) # None

不同类型的节点

snode.dense(indices, shape)
参数:
  • snode – (结构节点) 父节点,返回的子节点就是从该节点派生
  • indices – (索引)用于子节点上的索引
  • shape – (标量或元组)指定向量张量(tensor of vector)的形状
返回:

(结构节点)派生出来的子节点

以下示例代码放置了尺寸为 3 的一维张量:

x = ti.var(dt=ti.i32)
ti.root.dense(ti.i, 3).place(x)

以下示例代码放置了尺寸为 (3,4) 的二维张量:

x = ti.var(dt=ti.i32)
ti.root.dense(ti.ij, (3, 4)).place(x)

注解

如果给定的 shape 是一个标量,却又对应了多个索引,那么 shape 将自动扩充直至和索引数量相等。例如,

snode.dense(ti.ijk, 3)

相当于

snode.dense(ti.ijk, (3, 3, 3))
snode.dynamic(index, size, chunk_size = None)
参数:
  • snode – (结构节点) 父节点,返回的子节点就是从该节点派生
  • index – (索引) 动态集合节点(dynamic node)的索引
  • size – (标量)描述该动态集合节点的最大尺寸
  • chunk_size – (可选标量)描述动态内存分配时块(chunk)中存储的元素数目
返回:

(结构节点)派生出来的子节点

动态集合 节点就像 C++ 中的 std::vector 或者是 Python 中的 list 。Taichi 具有的动态内存分配系统可以实现自由的分配内存。

以下示例代码放置了最大尺寸为 16 的一维动态张量:

ti.root.dynamic(ti.i, 16).place(x)
snode.bitmasked()
snode.pointer()
snode.hash()

TODO: add descriptions here

动态集合节点的使用

ti.length(snode, indices)
参数:
  • snode – (动态集合节点)
  • indices – (标量或元组中标量) 动态集合 节点的索引
返回:

(int32) the current size of the dynamic node

ti.append(snode, indices, val)
参数:
  • snode – (动态集合节点)
  • indices – (标量或元组中标量) 动态集合 节点的索引
  • val – (取决于结构节点的数据类型)想要储存的值
返回:

(int32) the size of the dynamic node, before appending

使用上述函数,就能实现通过 索引(indices)常量(val) 插入到 动态集合 节点中。

Taichi 的张量尺寸

对于张量,非整二次幂的那些维度会被扩充为整二次幂,因而会占据更多虚拟地址空间。例如,一个 (18, 65) 的稠密张量在实际存储时相当于一个 (32, 128) 大小的张量。

索引

ti.i
ti.j
ti.k
ti.ij
ti.ji
ti.jk
ti.kj
ti.ik
ti.ki
ti.ijk
ti.ijkl
ti.indices(a, b, ...)

(TODO)