def inverse(self): assert self.n == self.m, 'Only square matrices are invertible' if self.n == 1: return Matrix([1 / self(0, 0)]) elif self.n == 2: inv_det = impl.expr_init(1.0 / self.determinant(self)) return inv_det * Matrix([[self(1, 1), -self(0, 1)], [-self(1, 0), self(0, 0)]]) elif self.n == 3: n = 3 import taichi as ti inv_determinant = ti.expr_init(1.0 / ti.determinant(self)) entries = [[0] * n for _ in range(n)] def E(x, y): return self(x % n, y % n) for i in range(n): for j in range(n): entries[j][i] = ti.expr_init( inv_determinant * (E(i + 1, j + 1) * E(i + 2, j + 2) - E(i + 2, j + 1) * E(i + 1, j + 2))) return Matrix(entries) else: raise Exception( "Inversions of matrices with sizes >= 4 are not supported")
def build_assign_annotated(ctx, target, value, is_static_assign, annotation): """Build an annotated assginment like this: target: annotation = value. Args: ctx (ast_builder_utils.BuilderContext): The builder context. target (ast.Name): A variable name. `target.id` holds the name as a string. annotation: A type we hope to assign to the target value: A node representing the value. is_static_assign: A boolean value indicating whether this is a static assignment """ is_local = isinstance(target, ast.Name) anno = ti.expr_init(annotation) if is_static_assign: raise TaichiSyntaxError( "Static assign cannot be used on annotated assignment") if is_local and not ctx.is_var_declared(target.id): var = ti.cast(value, anno) var = ti.expr_init(var) ctx.create_variable(target.id, var) else: var = target.ptr if var.ptr.get_ret_type() != anno: raise TaichiSyntaxError( "Static assign cannot have type overloading") var.assign(value) return var
def build_grouped_ndrange_for(ctx, node): with ctx.variable_scope_guard(): ndrange_var = ti.expr_init(build_stmt(ctx, node.iter.args[0]).ptr) ndrange_begin = ti.cast(ti.Expr(0), ti.i32) ndrange_end = ti.cast( ti.Expr(ti.subscript(ndrange_var.acc_dimensions, 0)), ti.i32) ndrange_loop_var = ti.Expr(ti.core.make_id_expr('')) ti.core.begin_frontend_range_for(ndrange_loop_var.ptr, ndrange_begin.ptr, ndrange_end.ptr) targets = IRBuilder.get_for_loop_targets(node) if len(targets) != 1: raise TaichiSyntaxError( f"Group for should have 1 loop target, found {len(targets)}" ) target = targets[0] target_var = ti.expr_init( ti.Vector([0] * len(ndrange_var.dimensions), dt=ti.i32)) ctx.create_variable(target, target_var) I = ti.expr_init(ndrange_loop_var) for i in range(len(ndrange_var.dimensions)): if i + 1 < len(ndrange_var.dimensions): target_tmp = I // ndrange_var.acc_dimensions[i + 1] else: target_tmp = I ti.subscript(target_var, i).assign(target_tmp + ndrange_var.bounds[i][0]) if i + 1 < len(ndrange_var.dimensions): I.assign(I - target_tmp * ndrange_var.acc_dimensions[i + 1]) node.body = build_stmts(ctx, node.body) ti.core.end_frontend_range_for() return node
def build_ndrange_for(ctx, node): with ctx.variable_scope_guard(): ndrange_var = ti.expr_init(build_stmt(ctx, node.iter).ptr) ndrange_begin = ti.cast(ti.Expr(0), ti.i32) ndrange_end = ti.cast( ti.Expr(ti.subscript(ndrange_var.acc_dimensions, 0)), ti.i32) ndrange_loop_var = ti.Expr(ti.core.make_id_expr('')) ti.core.begin_frontend_range_for(ndrange_loop_var.ptr, ndrange_begin.ptr, ndrange_end.ptr) I = ti.expr_init(ndrange_loop_var) targets = IRBuilder.get_for_loop_targets(node) for i, target in enumerate(targets): if i + 1 < len(targets): target_tmp = ti.expr_init( I // ndrange_var.acc_dimensions[i + 1]) else: target_tmp = ti.expr_init(I) ctx.create_variable( target, ti.expr_init( target_tmp + ti.subscript(ti.subscript(ndrange_var.bounds, i), 0))) if i + 1 < len(targets): I.assign(I - target_tmp * ndrange_var.acc_dimensions[i + 1]) node.body = build_stmts(ctx, node.body) ti.core.end_frontend_range_for() return node
def svd3d(A, dt, iters=None): assert A.n == 3 and A.m == 3 inputs = tuple([e.ptr for e in A.entries]) assert dt in [ti.f32, ti.f64] if iters is None: if dt == ti.f32: iters = 5 else: iters = 8 if dt == ti.f32: rets = ti.core.sifakis_svd_f32(*inputs, iters) else: rets = ti.core.sifakis_svd_f64(*inputs, iters) assert len(rets) == 21 U_entries = rets[:9] V_entries = rets[9:18] sig_entries = rets[18:] U = ti.expr_init(ti.Matrix.zero(dt, 3, 3)) V = ti.expr_init(ti.Matrix.zero(dt, 3, 3)) sigma = ti.expr_init(ti.Matrix.zero(dt, 3, 3)) for i in range(3): for j in range(3): U(i, j).assign(U_entries[i * 3 + j]) V(i, j).assign(V_entries[i * 3 + j]) sigma(i, i).assign(sig_entries[i]) return U, sigma, V
def pow(self, power): import taichi as ti if not isinstance(power, int): return raw_pow(self, power) if power == 0: # TODO: remove the hack, use {Expr,Matrix}.dup().fill(1) # also note that this can be solved by #940 return self * 0 + Expr(1) negative = power < 0 # Why not simply use `power = abs(power)`? # Because `abs` is overrided by the `ti.abs` above. if negative: power = -power tmp = self ret = None while power: if power & 1: if ret is None: ret = tmp else: ret = ti.expr_init(ret * tmp) tmp = ti.expr_init(tmp * tmp) power >>= 1 if negative: return 1 / ret else: return ret
def inverse(self): assert self.n == self.m, 'Only square matrices are invertible' if self.n == 1: return Matrix([1 / self(0, 0)]) elif self.n == 2: inv_det = impl.expr_init(1.0 / self.determinant()) # Discussion: https://github.com/taichi-dev/taichi/pull/943#issuecomment-626344323 return inv_det * Matrix([[self(1, 1), -self(0, 1)], [-self(1, 0), self(0, 0)]]).variable() elif self.n == 3: n = 3 import taichi as ti inv_determinant = ti.expr_init(1.0 / self.determinant()) entries = [[0] * n for _ in range(n)] def E(x, y): return self(x % n, y % n) for i in range(n): for j in range(n): entries[j][i] = ti.expr_init( inv_determinant * (E(i + 1, j + 1) * E(i + 2, j + 2) - E(i + 2, j + 1) * E(i + 1, j + 2))) return Matrix(entries) elif self.n == 4: n = 4 import taichi as ti inv_determinant = ti.expr_init(1.0 / self.determinant()) entries = [[0] * n for _ in range(n)] def E(x, y): return self(x % n, y % n) for i in range(n): for j in range(n): entries[j][i] = ti.expr_init( inv_determinant * (-1)**(i + j) * ((E(i + 1, j + 1) * (E(i + 2, j + 2) * E(i + 3, j + 3) - E(i + 3, j + 2) * E(i + 2, j + 3)) - E(i + 2, j + 1) * (E(i + 1, j + 2) * E(i + 3, j + 3) - E(i + 3, j + 2) * E(i + 1, j + 3)) + E(i + 3, j + 1) * (E(i + 1, j + 2) * E(i + 2, j + 3) - E(i + 2, j + 2) * E(i + 1, j + 3))))) return Matrix(entries) else: raise Exception( "Inversions of matrices with sizes >= 5 are not supported")
def build_IfExp(ctx, node): node.test = build_stmt(ctx, node.test) node.body = build_stmt(ctx, node.body) node.orelse = build_stmt(ctx, node.orelse) if ti.is_taichi_class(node.test.ptr) or ti.is_taichi_class( node.body.ptr) or ti.is_taichi_class(node.orelse.ptr): node.ptr = ti.select(node.test.ptr, node.body.ptr, node.orelse.ptr) return node is_static_if = (IRBuilder.get_decorator(ctx, node.test) == "static") if is_static_if: if node.test.ptr: node.body = build_stmt(ctx, node.body) node.ptr = node.body.ptr else: node.orelse = build_stmt(ctx, node.orelse) node.ptr = node.orelse.ptr return node val = ti.expr_init(None) ti.begin_frontend_if(node.test.ptr) ti.core.begin_frontend_if_true() val.assign(node.body.ptr) ti.core.pop_scope() ti.core.begin_frontend_if_false() val.assign(node.orelse.ptr) ti.core.pop_scope() node.ptr = val return node
def build_IfExp(ctx, node): build_stmt(ctx, node.test) build_stmt(ctx, node.body) build_stmt(ctx, node.orelse) if ti.is_taichi_class(node.test.ptr) or ti.is_taichi_class( node.body.ptr) or ti.is_taichi_class(node.orelse.ptr): node.ptr = ti.select(node.test.ptr, node.body.ptr, node.orelse.ptr) return node.ptr is_static_if = (ASTTransformer.get_decorator(ctx, node.test) == "static") if is_static_if: if node.test.ptr: node.ptr = build_stmt(ctx, node.body) else: node.ptr = build_stmt(ctx, node.orelse) return node.ptr val = ti.expr_init(None) ti.begin_frontend_if(node.test.ptr) _ti_core.begin_frontend_if_true() val.assign(node.body.ptr) _ti_core.pop_scope() _ti_core.begin_frontend_if_false() val.assign(node.orelse.ptr) _ti_core.pop_scope() node.ptr = val return node.ptr
def _vector_getattr(self, key): ret = [] stk = [] for k in key: sgn = 0 i = 0 if k != '_': sgn = 1 i = 'xyzw'.find(k) if i == -1: sgn = -1 i = 'XYZW'.find(k) if i == -1: break stk.append((i, sgn)) else: for i, sgn in stk: if ti.inside_kernel(): t = self.subscript(i) * sgn ret.append(ti.expr_init(t)) else: t = self[i] * sgn ret.append(t) return ti.Vector(ret) _taichi_skip_traceback = 1 raise AttributeError(f"'Matrix' object has no attribute {key}")
def determinant(a): if a.n == 2 and a.m == 2: return a(0, 0) * a(1, 1) - a(0, 1) * a(1, 0) elif a.n == 3 and a.m == 3: return a(0, 0) * (a(1, 1) * a(2, 2) - a(2, 1) * a(1, 2)) - a( 1, 0) * (a(0, 1) * a(2, 2) - a(2, 1) * a(0, 2)) + a( 2, 0) * (a(0, 1) * a(1, 2) - a(1, 1) * a(0, 2)) elif a.n == 4 and a.m == 4: import taichi as ti n = 4 def E(x, y): return a(x % n, y % n) det = ti.expr_init(0.0) for i in range(4): det = det + (-1.0)**i * ( a(i, 0) * (E(i + 1, 1) * (E(i + 2, 2) * E(i + 3, 3) - E(i + 3, 2) * E(i + 2, 3)) - E(i + 2, 1) * (E(i + 1, 2) * E(i + 3, 3) - E(i + 3, 2) * E(i + 1, 3)) + E(i + 3, 1) * (E(i + 1, 2) * E(i + 2, 3) - E(i + 2, 2) * E(i + 1, 3)))) return det else: raise Exception( "Determinants of matrices with sizes >= 5 are not supported")
def __pow__(self, power, modulo=None): import taichi as ti if not isinstance(power, int) or abs(power) > 100: return Expr(taichi_lang_core.expr_pow(self.ptr, Expr(power).ptr)) if power == 0: return Expr(1) negative = power < 0 power = abs(power) tmp = self ret = None while power: if power & 1: if ret is None: ret = tmp else: ret = ti.expr_init(ret * tmp) tmp = ti.expr_init(tmp * tmp) power >>= 1 if negative: return 1 / ret else: return ret
def __pow__(self, power, modulo=None): import taichi as ti if ti.is_taichi_class(power): return power.element_wise_binary(lambda x, y: pow(y, x), self) if not isinstance(power, int) or abs(power) > 100: return Expr(taichi_lang_core.expr_pow(self.ptr, Expr(power).ptr)) if power == 0: return Expr(1) negative = power < 0 power = abs(power) tmp = self ret = None while power: if power & 1: if ret is None: ret = tmp else: ret = ti.expr_init(ret * tmp) tmp = ti.expr_init(tmp * tmp) power >>= 1 if negative: return 1 / ret else: return ret
def build_assign_basic(ctx, target, value): """Build basic assginment like this: target = value. Args: ctx (ast_builder_utils.BuilderContext): The builder context. target (ast.Name): A variable name. `target.id` holds the name as a string. value: A node representing the value. """ is_local = isinstance(target, ast.Name) if is_local and not ctx.is_var_declared(target.id): ctx.create_variable(target.id, ti.expr_init(value)) else: var = target.ptr var.assign(value)
def polygonise(self, triangles: ti.template()): numOftriangle = 0; cubeIndex = 0; vertlist = [ti.expr_init(ti.Vector.zero(float, 3)) for t in range(12)] # 保存等值面与立方体各边相交的坐标 # 确定那个顶点位于等值面内部 if (grid.val[0] < 1): cubeIndex |= 1; if (grid.val[1] < 1): cubeIndex |= 2; if (grid.val[2] < 1): cubeIndex |= 4; if (grid.val[3] < 1): cubeIndex |= 8; if (grid.val[4] < 1): cubeIndex |= 16; if (grid.val[5] < 1): cubeIndex |= 32; if (grid.val[6] < 1): cubeIndex |= 64; if (grid.val[7] < 1): cubeIndex |= 128; # 异常:立方体所有顶点都在或者都不在等值面内部 if (self.edgeTable[cubeIndex] != 0): # 确定等值面与立方体交点坐标 if (edgeTable[cubeIndex] & 1): vertlist[0] = self.vertex_interp(grid.p[0],grid.p[1],grid.val[0],grid.val[1]); if (edgeTable[cubeIndex] & 2): vertlist[1] = self.vertex_interp(grid.p[1],grid.p[2],grid.val[1],grid.val[2]); if (edgeTable[cubeIndex] & 4): vertlist[2] = self.vertex_interp(grid.p[2],grid.p[3],grid.val[2],grid.val[3]); if (edgeTable[cubeIndex] & 8): vertlist[3] = self.vertex_interp(grid.p[3],grid.p[0],grid.val[3],grid.val[0]); if (edgeTable[cubeIndex] & 16): vertlist[4] = self.vertex_interp(grid.p[4],grid.p[5],grid.val[4],grid.val[5]); if (edgeTable[cubeIndex] & 32): vertlist[5] = self.vertex_interp(grid.p[5],grid.p[6],grid.val[5],grid.val[6]); if (edgeTable[cubeIndex] & 64): vertlist[6] = self.vertex_interp(grid.p[6],grid.p[7],grid.val[6],grid.val[7]); if (edgeTable[cubeIndex] & 128): vertlist[7] = self.vertex_interp(grid.p[7],grid.p[4],grid.val[7],grid.val[4]); if (edgeTable[cubeIndex] & 256): vertlist[8] = self.vertex_interp(grid.p[0],grid.p[4],grid.val[0],grid.val[4]); if (edgeTable[cubeIndex] & 512): vertlist[9] = self.vertex_interp(grid.p[1],grid.p[5],grid.val[1],grid.val[5]); if (edgeTable[cubeIndex] & 1024): vertlist[10] = self.vertex_interp(grid.p[2],grid.p[6],grid.val[2],grid.val[6]); if (edgeTable[cubeIndex] & 2048): vertlist[11] = self.vertex_interp(grid.p[3],grid.p[7],grid.val[3],grid.val[7]); //根据交点坐标确定三角形面片,并进行保存 for (int i=0; triTable[cubeIndex][i] != -1; i+=3): tri[numOftriangle].p[0] = vertlist[ triTable[cubeIndex][i ] ]; tri[numOftriangle].p[1] = vertlist[ triTable[cubeIndex][i+1] ]; tri[numOftriangle].p[2] = vertlist[ triTable[cubeIndex][i+2] ]; numOftriangle++;
def build_short_circuit_or(operands): if len(operands) == 1: return operands[0].ptr val = ti.expr_init(None) lhs = operands[0].ptr ti.begin_frontend_if(lhs) _ti_core.begin_frontend_if_true() val.assign(1) _ti_core.pop_scope() _ti_core.begin_frontend_if_false() rhs = ASTTransformer.build_short_circuit_or(operands[1:]) val.assign(rhs) _ti_core.pop_scope() return val
def build_assign_basic(ctx, target, value, is_static_assign): """Build basic assginment like this: target = value. Args: ctx (ast_builder_utils.BuilderContext): The builder context. target (ast.Name): A variable name. `target.id` holds the name as a string. value: A node representing the value. is_static_assign: A boolean value indicating whether this is a static assignment """ is_local = isinstance(target, ast.Name) if is_static_assign: if not is_local: raise TaichiSyntaxError( "Static assign cannot be used on elements in arrays") ctx.create_variable(target.id, value) var = value elif is_local and not ctx.is_var_declared(target.id): var = ti.expr_init(value) ctx.create_variable(target.id, var) else: var = target.ptr var.assign(value) return var
def atomic_add(self, other): import taichi as ti other_ptr = ti.wrap_scalar(other).ptr return ti.expr_init(taichi_lang_core.expr_atomic_add(self.ptr, other_ptr))
def shuffle(a, *ks): ret = [] for k in ks: t = a.subscript(k) ret.append(ti.expr_init(t)) return ti.Vector(ret)
def all(self): import taichi as ti ret = self.entries[0] != ti.expr_init(0) for i in range(1, len(self.entries)): ret = ret + (self.entries[i] != ti.expr_init(0)) return -(ret == ti.expr_init(-len(self.entries)))
def append(l, indices, val): import taichi as ti a = ti.expr_init( ti_core.insert_append(l.snode.ptr, make_expr_group(indices), Expr(val).ptr)) return a
def variable(self): return self.__class__(*(ti.expr_init(e) for e in self.entries))
def __init__(self): self.p = [ti.expr_init(ti.Vector.zero(int, 3)) for t in range(8)] self.val = [ti.expr_init(0.0) for t in range(8)]
def assign(self, value): self.parent[self.name] = ti.expr_init(value)
def __isub__(self, other): # TODO: add atomic_sub() import taichi as ti ti.expr_init(taichi_lang_core.expr_atomic_sub(self.ptr, other.ptr))
def __init__(self, **kwargs): res = dict() for k, v in kwargs.items(): res[k] = ti.expr_init(v) super().__init__(res)