【作业1】logistic_map 逻辑斯蒂映射

作业1

作业描述

我使用logistic方程,实现了一个简简单单的 logistics 映射,每帧运行屏幕列像素只会得到多个值,图像没有那么疏离时依靠taichi的并行机制补足。

效果展示

logistic_map

代码链接

Code

问题

来由:

我用如下内容定义屏幕像素

pixels = ti.Vector.field(3, ti.f32, shape=(n * 2, n))

但当我希望使用稀疏结构来定义屏幕像素时,出现了一些问题

pp = ti.types.vector(3,ti.f32)
pixels =  ti.root.pointer(ti.ij, (n*2, n)).place(p) 

报错如下

raise ValueError(f'{arg} cannot be placed')
`ValueError: <taichi.lang.matrix.MatrixType object at 0x0000029978CCBFD0> cannot be placed`

那么再次改为如下代码:

pp = ti.field(ti.f32)
pixels =  ti.root.pointer(ti.ijk, (n*2, n, 3)).place(pp)

此时确实定义成功,而且确实会影响到后续迭代代码的形式,
但该数据结构在GUI显示时就让我犯难,这样的结构再如下代码中无法渲染

gui.set_image(pixels)

问题:
那么我该如何做到既可使用pointer或bitmasked这类稀疏结构,又能够适配于GUI显示渲染呢?

应该是这样吧,ti.types.vector是定义的类型
pp = ti.Vector.field(3,ti.f32)
pixels = ti.root.pointer(ti.ij, (n*2, n)).place(pp)

首先 感谢您的回复
//=================================================================
根据您的建议,我将定义改为如下:

pp = ti.Vector.field(3,ti.f32)
pixels = ti.root.pointer(ti.ij, (n*2, n)).place(pp)

并配合清屏以及赋值部分改为

pixels[i, j] =ti.Vector([0,0,0])    #clear screen  

其在清屏代码部分出现问题如下

On line 31 of file "Z:\_PROJECT_\Python\taichi_course\example\chaos\logistics.py":
        pixels[i, j] =ti.Vector([0,0,0]) #clear screen  清屏
        ^^^^^^^^^^^^
Traceback (most recent call last):
  File "D:\_PRO\_DEVELOPER\Miniconda3\envs\taichienv\lib\site-packages\taichi\lang\ast\ast_transformer_utils.py", line 24, in __call__
    return method(ctx, node)
  File "D:\_PRO\_DEVELOPER\Miniconda3\envs\taichienv\lib\site-packages\taichi\lang\ast\ast_transformer.py", line 169, in build_Subscript
    node.ptr = impl.subscript(node.value.ptr, *node.slice.ptr)
  File "D:\_PRO\_DEVELOPER\Miniconda3\envs\taichienv\lib\site-packages\taichi\lang\util.py", line 214, in
wrapped
    return func(*args, **kwargs)
  File "D:\_PRO\_DEVELOPER\Miniconda3\envs\taichienv\lib\site-packages\taichi\lang\impl.py", line 211, in
subscript
    raise IndexError(
IndexError: Field with dim 0 accessed with indices of dim 2

此类报错就非常让我头痛了 Field 的 dim如何会是0的
但请注意!!!
可以确证该清屏赋值方式适用于原代码
pixels = ti.Vector.field(3, ti.f32, shape=(n * 3, n))

//=================================================================
那么好像问题依旧存在

随后我试了试如下赋值

pixels[i, j] =pp(0,0,0)    # clear screen  

而报错如下:

taichi.lang.exception.TaichiCompilationError:
On line 32 of file "Z:\_PROJECT_\Python\taichi_course\example\chaos\logistics.py":
        pixels[i, j] =pp(0,0,0) #clear screen  清屏
                      ^^^^^^^^^
Traceback (most recent call last):
  File "D:\_PRO\_DEVELOPER\Miniconda3\envs\taichienv\lib\site-packages\taichi\lang\ast\ast_transformer_utils.py", line 24, in __call__
    return method(ctx, node)
  File "D:\_PRO\_DEVELOPER\Miniconda3\envs\taichienv\lib\site-packages\taichi\lang\ast\ast_transformer.py", line 384, in build_Call
    node.ptr = func(*args, **keywords)
TypeError: 'MatrixField' object is not callable

//=================================================================
emmmmmm 我现在依旧怀疑是否要从定义上下手 …

pp = ti.Vector.field(3,ti.f32)
pixels = ti.root.pointer(ti.ij, (n*2, n)).place(pp)
pp[i,j]=ti.Vector([0,0,0])
应该这样,pp才是field数据,

1 个赞

啊! 非常抱歉,之前code被设为私有了 ;

我依照您的建议进行了修改 , 错误依旧 , 源代码已开放,代码很短,上述所有的尝试和建议都可用源代码跑一下可以测出来;

Hi @sky92, 我看了下你的代码,在定义稀疏数据结构的时候是不对的。这里我给出了一个例子,里面有5种方式来定义数据结构,你可以参考着来。

import taichi as ti 
ti.init(arch=ti.cuda)

n = 1024
# 最简单的定义field方式,dense的数据结构
pixels = ti.Vector.field(3, ti.f32, shape=(n,n))

# 比较高级的定义field的方式,也是dense的数据结构
# pixels = ti.Vector.field(3, ti.f32)
# block = ti.root.dense(ti.ij, (n, n)).place(pixels)

#使用pointer的稀疏数据结构
# pixels = ti.Vector.field(3, ti.f32)
# block = ti.root.pointer(ti.ij, (n, n)).place(pixels)

# 使用bitmasked的稀疏数据结构
# pixels = ti.Vector.field(3, ti.f32)
# block = ti.root.bitmasked(ti.ij, (n, n)).place(pixels)

#使用复合类型的数据结构
# vec3f = ti.types.vector(3, ti.f32)
# pixels = vec3f.field(shape=(n, n))

@ti.kernel
def paint():
    for i, j in ti.ndrange((100,400), (100, 400)):
        pixels[i, j] = ti.Vector([1, 0.5, 0])

gui=ti.GUI("Test", res=(n,n))
for i in range(100000):
    paint()
    gui.set_image(pixels)
    gui.show()

2 个赞

我看你注释掉的代码是这个吧,不是shape=3,是pixels = ti.Vector.field(3,ti.f32 ) 这里的3是指每一个向量是几维的,shape参数是指有多少个这样维度向量;

你可以看上面禹鹏老师列的比较全面,我用你代码试了下,两种都不报错,只是用稀疏数据结构就别用
for i, j in pixels:
完整如下:

import taichi as ti

ti.init(arch=ti.cuda)

n = 360

# pixels = ti.Vector.field(3, ti.f32, shape=(n * 2, n))

# pp = ti.field(ti.f32, shape=3)
# pixels =  ti.root.pointer(ti.ij, (n*2, n)).place(pp)

# pp = ti.field(ti.f32)
# pixels =  ti.root.pointer(ti.ijk, (n*2, n, 3)).place(pp)

# 该项定义无用 报错
# pp = ti.Vector.field(ti.f32,shape=3)
# pixels =  ti.root.dense(ti.ij, (n*2, n)).place(pp)
pixels =  ti.Vector.field(3,ti.f32 )
ti.root.dense(ti.ij, (n*2, n)).place(pixels)
#这种定义也是不报错的,但是for i, j in pixels:这种是进不去的,因为稀疏数据初始都是没有激活的
# ti.root.pointer(ti.ij, (n*2, n)).place(pixels)

#该项定义正常
# pixels = ti.Vector.field(3, ti.f32, shape=(n * 3, n))


@ti.kernel
def paint(t: float):
    for i, j in pixels:
    # 若用稀疏数据结构要这样了
    # for i, j in ti.ndrange( 720,  360):
        pixels[i, j] = [0, 0, 0]  #clear screen  清屏

        r = i * (2.2 / n)

        #此处不建议使用ti.cos(t*0.1) 取0-1 会出现后期高频振荡没有图像  不过动画有点好玩
        # z = 0.9 * ti.cos(t*0.1)  # interest one
        z = 0.9 * ti.cos(ti.random())

        # 同一个r尝试j次 获得j个收敛值  由于taichi机制建议不要再添加一个循环
        # for xx in range(0,j):
        #     iterations = 0
        #     while iterations < 100:
        #         z = r*z*(1-z)
        #         iterations+=1

        #     jj = ti.cast(z*500.0 , ti.i32)
        #     pixels[i,jj]=[20, 0 ,90]

        iterations = 0
        while iterations < 10000:
            z = r * z * (1 - z)
            iterations += 1
        jj = ti.cast(z * n * 0.9, ti.i32)
        pixels[i, jj] = [20, 0, 90]


gui = ti.GUI("logistic  ", pixels.shape)

for i in range(1000000):
    # gui.clear(0x000000)
    paint(i * 0.03)
    gui.set_image(pixels)
    gui.show()
3 个赞

是的,@shihuan 说的很好。如果pixels 是一个稀疏数据结构,struct-for 只会遍历 active 元素。所以在没有active元素之前,for i, j in pixels 是一个空的循环。

@YuPeng @shihuan
非常感谢两位解答,我大概理解了,就是说指针实现的特殊性,需要激活才能访问,那么在部分cell没激活前遍历就要出问题;那么只有通过ij按数字遍历,通过类似清屏的赋值方式去激活cell;

1 个赞