Taichi 语言中类的继承问题

运行环境

[Taichi] version 0.8.4, llvm 10.0.0, commit 895881b5, win, python 3.8.10

问题描述

根据第二讲里面的galaxy例子模仿的代码,发现在cpu下可以正常运行,但是把arch换成opengl就会报错,报错如下:
111

另外,在增加黑洞类时,发现不能在重写父类方法中使用super的情况,比如将planet的initial方法重写如下:

    @ti.kernel
        def initial(self,centerx:ti.f32,centery:ti.f32,size:ti.f32,init_vel:ti.f32):
            super(planet,self).initial(centerx,centery,size,init_vel)
            """
            for i in range(self.N):
                if self.N == 1:
                    self.pos[i] = ti.Vector([centerx,centery])
                    self.vel[i] = ti.Vector([0.0,0.0])
                else:
                    angle, dis = self.pickrandomlocation(i,self.N)
                    offset = ti.Vector([ti.cos(angle),ti.sin(angle)])
                    center = ti.Vector([centerx,centery])
                    self.pos[i] = center + dis*offset*size
                    self.vel[i] = ti.Vector([-ti.sin(angle),ti.cos(angle)])
                    self.vel[i] *= init_vel
            """
            self.disappearstatus[i] = ti.Vector([0])

会报错如下

Traceback (most recent call last):
  File "H:\taichi\galaxy.py", line 11, in <module>
    planets.initial(0.5,0.5,0.4,10)
  File "D:\python\lib\site-packages\taichi\lang\kernel_impl.py", line 859, in __
call__
    return self._primal(self._kernel_owner, *args, **kwargs)
  File "D:\python\lib\site-packages\taichi\lang\kernel_impl.py", line 723, in __
call__
    key = self.ensure_compiled(*args)
  File "D:\python\lib\site-packages\taichi\lang\kernel_impl.py", line 714, in en
sure_compiled
    self.materialize(key=key, args=args, arg_features=arg_features)
  File "D:\python\lib\site-packages\taichi\lang\kernel_impl.py", line 518, in ma
terialize
    taichi_kernel = _ti_core.create_kernel(taichi_ast_generator,
  File "D:\python\lib\site-packages\taichi\lang\kernel_impl.py", line 513, in ta
ichi_ast_generator
    compiled()
  File "H:\taichi\celestial.py", line 88, in initial
    super(planet,self).initial(centerx,centery,size,init_vel)
  File "D:\python\lib\site-packages\taichi\lang\kernel_impl.py", line 787, in wr
apped
    assert not hasattr(clsobj, '_data_oriented')
AssertionError

而使用注释区域的代码重写不用super函数则没有问题,请问这又是什么原因

代码链接

作业链接

具体关于那个父类重写的,代码如下

import taichi as ti


G = 1
PI = 3.1415926
disappear_dis = 0.03

@ti.data_oriented
class celestialobjest:
    def __init__(self,N,mass):
        self.N = N
        self.mass = mass
        self.pos = ti.Vector.field(2,ti.f32,self.N)
        self.vel = ti.Vector.field(2,ti.f32,self.N)
        self.force = ti.Vector.field(2,ti.f32,self.N)
        
    def visualize(self,gui,radius=2,color=0xffffff):
        gui.circles(self.pos.to_numpy(),radius=radius,color=color)
        
    @ti.func
    def count(self):
        return self.N
        
    @ti.func
    def location(self):
        return self.pos
        
    @ti.func
    def clear(self):
        for i in self.force:
            self.force[i] = ti.Vector([0.0,0.0])
    
    @ti.func
    def Mass(self):
        return self.mass
        
    @ti.kernel
    def initial(self,centerx:ti.f32,centery:ti.f32,size:ti.f32,init_vel:ti.f32):
        for i in range(self.N):
            if self.N == 1:
                self.pos[i] = ti.Vector([centerx,centery])
                self.vel[i] = ti.Vector([0.0,0.0])
            else:
                angle, dis = self.pickrandomlocation(i,self.N)
                offset = ti.Vector([ti.cos(angle),ti.sin(angle)])
                center = ti.Vector([centerx,centery])
                self.pos[i] = center + dis*offset*size
                self.vel[i] = ti.Vector([-ti.sin(angle),ti.cos(angle)])
                self.vel[i] *= init_vel
                
    @ti.kernel
    def computeforce(self):
        self.clear()
        for i in range(self.N):
            p = self.pos[i]
            for j in range(i):
                diff = self.pos[j]-p
                r = diff.norm(1e-2)
                f = G*self.Mass()*self.Mass()*(1.0/r)**3*diff
                self.force[i] += f
                self.force[j] += -f
    @ti.kernel
    def update(self,dt:ti.f32):
        for i in range(self.N):
            self.vel[i] += dt*self.force[i]/self.Mass()
            self.pos[i] += dt*self.vel[i]
            
@ti.data_oriented
class star(celestialobjest):
    def __init__(self,N,mass):
        super().__init__(N,mass)
        
    @staticmethod
    @ti.func
    def pickrandomlocation(i,n):
        angle = 2*PI*i/ti.cast(n,ti.f32)
        dis = 1
        return angle, dis
    
@ti.data_oriented
class planet(celestialobjest):
    def __init__(self,N,mass):
        super().__init__(N,mass)
        self.disappearstatus = ti.Vector.field(1,ti.i32,self.N)
        
    @ti.kernel
    def initial(self,centerx:ti.f32,centery:ti.f32,size:ti.f32,init_vel:ti.f32):
        super(planet,self).initial(centerx,centery,size,init_vel)
        """
        for i in range(self.N):
            if self.N == 1:
                self.pos[i] = ti.Vector([centerx,centery])
                self.vel[i] = ti.Vector([0.0,0.0])
            else:
                angle, dis = self.pickrandomlocation(i,self.N)
                offset = ti.Vector([ti.cos(angle),ti.sin(angle)])
                center = ti.Vector([centerx,centery])
                self.pos[i] = center + dis*offset*size
                self.vel[i] = ti.Vector([-ti.sin(angle),ti.cos(angle)])
                self.vel[i] *= init_vel
            self.disappearstatus[i] = ti.Vector([0])
            
        """
        for i in range(self.N):
            self.disappearstatus[i] = ti.Vector([0])
        
        
    @ti.func
    def disappear(self):
        return self.disappearstatus
        
    @staticmethod
    @ti.func
    def pickrandomlocation(i,n):
        angle = 2*PI*ti.random()
        dis = (ti.sqrt(ti.random()) * 0.7 + 0.3)
        return angle, dis
        
    @ti.kernel
    def computeforce(self,stars:ti.template()):
        self.clear()
        for i in range(self.N):
            if self.disappearstatus[i][0] == 0:
                p = self.pos[i]
                for j in range(i):
                    diff = self.pos[j]-p
                    r = diff.norm(1e-2)
                    f = -G*self.Mass()*self.Mass()*(1.0/r)**3*diff
                    self.force[i] += f
                    
                for k in range(stars.count()):
                    diff = stars.location()[k]-p
                    r = diff.norm(1e-2)
                    f = G*self.Mass()*stars.Mass()*(1.0/r)**3*diff
                    self.force[i] += f
                    stars.force[k] += -f
                    
    @ti.kernel
    def update(self,dt:ti.f32):
        for i in range(self.N):
            if self.disappearstatus[i][0] == 0:
                self.vel[i] += dt*self.force[i]/self.Mass()
                self.pos[i] += dt*self.vel[i]

在planet这个类里面的initial方法,如果采用super的这种写法就会报上面的错,然后如果去掉直接按照celestialobject类里面的方法贴过来反而可以正常运行

Hi @zydmtaichi. 非常欢迎来到太极论坛。提出的两个问题都特别好哈。

  1. OpenGL后端会出错的问题,我们在发布的时候就发现了,所以我们选择了cuda后端。不过具体问题,还要我们再看一下。
  2. planet 类中的 initial函数是一个kernel函数,你的实现中需要调用父类的kernel函数。这就会导致一个kernel函数调用另一个kernel函数的状况。而这是Taichi不允许的行为。

如果你真的想要实现类似的行为,可以在父类创建_initial这样的 ti.func函数,然后再在子类里调用。提供一个参考:

@ti.data_oriented
class celestialobjest:
    @ti.func
    def _initial(self,centerx:ti.f32,centery:ti.f32,size:ti.f32,init_vel:ti.f32):
    	...
                
    @ti.kernel
    def initial(self,centerx:ti.f32,centery:ti.f32,size:ti.f32,init_vel:ti.f32):
        self._initial(centerx, centery, size, init_vel)

@ti.data_oriented
class planet(celestialobjest):
    @ti.kernel
    def initial(self,centerx:ti.f32,centery:ti.f32,size:ti.f32,init_vel:ti.f32):
        super(planet,self)._initial(centerx,centery,size,init_vel)

谢谢老师