大家好,请教一个问题,关于ti.deactivate函数

ti.deactivate()可以抑制某个节点,我在写程序时候,发现在pointer上是有效的,但是在dynamic上没有效果,想问一下为什么。
关于pointer节点的测试代码如下:(有一个2行2列的数据,我想删除第2行,输出第一行结果正确)

import taichi as ti
ti.init(arch=ti.cpu,cpu_max_num_threads = 1)
@ti.data_oriented
class test:  
    def __init__(self) -> None:      
        self.tB                     = ti.Vector.field(2,dtype=ti.f32)                                      
        self.IndexB             = ti.Vector.field(2,dtype=ti.i8)
        
        self.plumelistB        = ti.root.pointer(ti.i,2)
        self.plumelistB.place(self.tB,self.IndexB)
        
        self.tB[0]                 = [1,2]
        self.IndexB[0]         = [3,4]          
        self.tB[1]                 = [101,201]
        self.IndexB[1]         = [301,401]        
        
    def Functest(self): 
        #self.clear()
        print('Active, N0 = ',self.tB[0])
        self.clear()
        print('Deactivate Node 1,N[0]=',self.tB[0])  
        
    @ti.kernel
    def clear(self):        
        ti.deactivate(self.plumelistB,1) 

if __name__ == '__main__':
    test().Functest()    

输出结果:

[Taichi] Starting on arch=x64
Active, N0 =  [1. 2.]
Deactivate Node 1,N[0]= [1. 2.]

但是当我把pointer换成dynamic后,结果就不正确了,self.plumelistB= ti.root.pointer(ti.i,2)改为self.plumelistB= ti.root.dynamic(ti.i,2),结果如下:

[Taichi] Starting on arch=x64
Active, N0 =  [1. 2.]
Deactivate Node 1,N[0]= [0. 0.]

这时候ti.deactivate函数好像把整个2*2的数据都变成0了,使用ti.deactivate函数,在抑制某一行的数据时候,该注意什么,怎么实现呢?

@MierDa 非常欢迎来到太极论坛。

pointer, bitmaskeddynamicdeactivate的时候确实是不同的机制。

pointerbitmasked是element-by-element的删除。而在deactivate dynamic的结点时只需要删除它的父亲结点,接着dynamic对应的链表元素就会被删除。

我们可以通过遍历来看pointer 或者 bitmasked元素是否 active,也可以利用 ti.is_active 函数。

import taichi as ti

ti.init(arch=ti.cpu, cpu_max_num_threads=1, debug=True)


@ti.data_oriented
class test:
    def __init__(self) -> None:
        self.a = ti.Vector.field(2, dtype=ti.f32)
        self.b = ti.Vector.field(2, dtype=ti.i32)

        self.block = ti.root.pointer(ti.i, 2)
        self.block.place(self.a, self.b)

        self.a[0] = [1, 2]
        self.b[0] = [3, 4]
        self.a[1] = [101, 201]
        self.b[1] = [301, 401]

    @ti.kernel
    def p(self):
        for i in self.a:
            print(self.a[i])
        for i in self.b:
            print(self.b[i])

    @ti.kernel
    def check_activity(self):
        print(ti.is_active(self.block, [1]))

    @ti.kernel
    def clear(self):
        ti.deactivate(self.block, 1)


if __name__ == '__main__':
    t = test()
    t.p()
    print("check_activity before deactivate")
    t.check_activity()
    t.clear()
    print("check_activity after deactivate")
    t.check_activity()
    t.p()

但是 ti.is_active函数 只能作用在 pointer, hash or bitmasked 结点上。不过你可以用C++中的 std::vector来类比 dynamic结点,我们可以用过 ti.length来判断元素个数:

import taichi as ti

ti.init(arch=ti.cpu, cpu_max_num_threads=1, debug=True)


@ti.data_oriented
class test:
    def __init__(self) -> None:
        self.a = ti.Vector.field(2, dtype=ti.f32)
        self.b = ti.Vector.field(2, dtype=ti.i32)

        self.block = ti.root.dynamic(ti.i, 2)
        self.block.place(self.a, self.b)

        self.a[0] = [1, 2]
        self.b[0] = [3, 4]
        self.a[1] = [101, 201]
        self.b[1] = [301, 401]

    @ti.kernel
    def p(self):
        for i in self.a:
            print(self.a[i])
        for i in self.b:
            print(self.b[i])

    @ti.kernel
    def check_length(self):
        print(ti.length(self.block, []))

    @ti.kernel
    def clear(self):
        ti.deactivate(self.block, 1)


if __name__ == '__main__':
    t = test()
    t.p()
    print("check_length before deactivate")
    t.check_length()
    t.clear()
    print("check_length after deactivate")
    t.check_length()
    t.p()

感谢禹老师的回复。在dynamic那个code中,ti.deactivate后,self.a, self.b还是都变成了0. 还是遇到了我之前的问题。我不知道跟我的设置是否有关系。我这边运行结果不显示Taichi scope 中的print结果,如下:

[Taichi] Starting on arch=x64
check_length before deactivate
check_length after deactivate

所以我把print函数p从Taichi scope放到了python scope中运行就显示print了,p函数更改如下:

    def p(self):
        print(self.a)

去掉了@kernal,运行结果如下:

[Taichi] Starting on arch=x64
[[  1.   2.]
 [101. 201.]
 [  0.   0.]
 [  0.   0.]
 [  0.   0.]]
check_length before deactivate
check_length after deactivate
[[0. 0.]
 [0. 0.]
 [0. 0.]
 [0. 0.]
 [0. 0.]]

可以看到,我只想删除第二行[101,201]但是 ti.deactivate(self.block, 1)将第一行数据也给删除了。
不知道是不是我改了scope的原因。如果我想让dynamic节点的数据保留第一行,只删除第二行该如何修改我的代码呢?

代码分块这是怎么实现的,我感觉禹老师的回复看起来版本很整齐,我写完怎么就一坨了 ::

可以使用 markdown 格式来修饰代码。代码块之前和之后用 ```

```
hello
```

效果就是

hello
1 Like

我有点理解你的意思了,那你是想要类似:Python List pop() Method 还是 c++中 std::vectorpop_back method?

举个例子,胡老师写的那个软体俄罗斯方块程序,假如加入消除功能,就需要在dynamic组里去掉消可除掉的粒子数据,就是那种感觉。Python List pop() Method 这个方法感觉更好。可以去掉满足一定条件的某些行数据。然后呢,随着时间还会加入一些新的俄罗斯方块的数据,这个时候我也不知道Taichi里用哪个命令append 一个Vector.field数据。我试了一下ti.append(data,indices,val)这个函数,但是这个函数中对标量场有效,对矢量场会出错。我的代码如下,(1)标量append:

import taichi as ti
ti.init(arch=ti.cpu,cpu_max_num_threads = 1)

@ti.data_oriented
class Taichi_:
    def __init__(self):
        self.x   = ti.field(dtype=ti.i32)         
        self.lsx = ti.root.dense(ti.j, 2).dynamic(ti.i,10)    
        self.lsx.place(self.x)
        
        self.s   = ti.Vector.field(2,dtype=ti.i32)         
        self.lss = ti.root.dense(ti.i,5).place(self.s)
        
        self.x[0,0] = 10
        self.x[1,0] = 20 
        self.x[2,0] = 30
        self.x[3,0] = 40
        self.x[4,0] = 50
        self.x[0,1] = 11
        self.x[1,1] = 21
        self.x[2,1] = 31
        self.x[3,1] = 41
        self.x[4,1] = 51       
              
    @ti.kernel         
    def func(self):
        for i in range(4):
            ti.append(self.lsx,0,5+i)
            ti.append(self.lsx,1,6+i)                
            
    def printtt(self):
        print('origin',self.x)
        self.func()
        print('append',self.x)        
    
if __name__ == '__main__':
    Taichi_().printtt()

运行结果如下:

[Taichi] Starting on arch=x64
origin [[10 11]
 [20 21]
 [30 31]
 [40 41]
 [50 51]
 [ 0  0]
 [ 0  0]
 [ 0  0]
 [ 0  0]
 [ 0  0]]
append [[10 11]
 [20 21]
 [30 31]
 [40 41]
 [50 51]
 [ 5  6]
 [ 6  7]
 [ 7  8]
 [ 8  9]
 [ 0  0]]

可以看出,能够在dynamic节点上继续添加新的数据。但每行的数据其实是个位置信息,我想直接写成一个位置向量,所以我就写了向量数据的append,就失败了,代码如下:

import taichi as ti
ti.init(arch=ti.cpu,cpu_max_num_threads = 1)

@ti.data_oriented
class Taichi_:
    def __init__(self):
        self.x   = ti.Vector.field(2,dtype=ti.i32)         
        self.lsx = ti.root.dynamic(ti.i,10)    
        self.lsx.place(self.x)
        
        self.x[0] = [11,12]
        self.x[1] = [21,22] 
        self.x[2] = [31,32]
        self.x[3] = [41,42]
        self.x[4] = [51,52]  
         
    @ti.kernel         
    def func(self):
        for i in range(4):
            ti.append(self.lsx,0,[63,64])             
            
    def printtt(self):
        print('origin',self.x)
        self.func()
        print('append',self.x)        
    
if __name__ == '__main__':
    Taichi_().printtt()

这样会出错如“ValueError: Invalid constant scalar expression: <class ‘list’>”
如果我把append的数据改成scalar,当然也不对。如

    @ti.kernel         
    def func(self):
        for i in range(4):
            ti.append(self.lsx,63,64) 

就提示“ti.append only works on single-child dynamic nodes.”
以上操作,我都想在dynamic节点上进行,所以没搞懂如何弄。请禹老师解答一下。困惑已久,谢谢。

Hi,@MierDa

  1. 类似 List pop的方法还没有在dynamic 结点中实现,
  2. ti.append目前的要求, val也必须是标量才行。

这是两个很有意思的feature,我觉得你可以把这两个feature request写到Taichi github的Issue里面。

谢谢解答。我想用向量的主要目的是想在布置AoS排序的时候让xy一对一对的,比如位置一对对+速度一对对+加速度一对对这样。我看课程讲解的,这样访问就快一些。因为后面模拟的时候,也是一对对的访问,拆成标量设计起来要麻烦一点点。那我暂时先在python scope中处理完数据,再push到Taichi kernal中的dynamic节点中吧。希望以后可以在Taichi中能够实现,就省事多了。

1 Like