def average(a, b): if type(a) is not hl.Expr: a = hl.Expr(a) if type(b) is not hl.Expr: b = hl.Expr(b) "hl.Expr average(hl.Expr a, hl.Expr b)" # Types must match. assert a.type() == b.type() # For floating point types: if (a.type().is_float()): # The '2' will be promoted to the floating point type due to # rule 3 above. return (a + b)/2 # For integer types, we must compute the intermediate value in a # wider type to avoid overflow. narrow = a.type() wider = narrow.with_bits(narrow.bits() * 2) a = hl.cast(wider, a) b = hl.cast(wider, b) return hl.cast(narrow, (a + b)/2)
def __init__(self, r, i=None): if type(r) is float and type(i) is float: self.real = hl.Expr(r) self.imag = hl.Expr(i) elif i is not None: self.real = r self.imag = i else: self.real = r[0] self.imag = r[1]
def test_all(vector_width, target): # print("target is %s " % str(target)) W = 32 H = 32 input = hl.Buffer(hl.UInt(8), [W, H]) for r in range(H): for c in range(W): input[c, r] = (c + r * W) & 0xff input_f = hl.Func() input_f[x, y] = input[x, y] tests = [ (hl.BoundaryConditions.constant_exterior, check_constant_exterior), (hl.BoundaryConditions.repeat_edge, check_repeat_edge), (hl.BoundaryConditions.repeat_image, check_repeat_image), (hl.BoundaryConditions.mirror_image, check_mirror_image), (hl.BoundaryConditions.mirror_interior, check_mirror_interior), ] for bc, checker in tests: # print(' Testing %s:%d...' % (bc.__name__, vector_width)) func_input_args = {'f': input_f, 'bounds': [(0, W), (0, H)]} image_input_args = {'f': input, 'bounds': [(0, W), (0, H)]} undef_min_args = { 'f': input, 'bounds': [(hl.Expr(), hl.Expr()), (0, H)] } undef_max_args = { 'f': input, 'bounds': [(0, W), (hl.Expr(), hl.Expr())] } implicit_bounds_args = {'f': input} if bc == hl.BoundaryConditions.constant_exterior: func_input_args['exterior'] = test_exterior image_input_args['exterior'] = test_exterior undef_min_args['exterior'] = test_exterior undef_max_args['exterior'] = test_exterior implicit_bounds_args['exterior'] = test_exterior realize_and_check(bc(**func_input_args), checker, input, test_min, test_extent, test_min, test_extent, vector_width, target) realize_and_check(bc(**image_input_args), checker, input, test_min, test_extent, test_min, test_extent, vector_width, target) realize_and_check(bc(**undef_min_args), checker, input, 0, W, test_min, test_extent, vector_width, target) realize_and_check(bc(**undef_max_args), checker, input, test_min, test_extent, 0, H, vector_width, target) realize_and_check(bc(**implicit_bounds_args), checker, input, test_min, test_extent, test_min, test_extent, vector_width, target)
def gauss(input, k, rdom, name): blur_x = hl.Func(name + "_x") output = hl.Func(name) x, y, c, xi, yi = hl.Var("x"), hl.Var("y"), hl.Var("c"), hl.Var("xi"), hl.Var("yi") val = hl.Expr("val") if input.dimensions() == 2: blur_x[x, y] = hl.sum(input[x + rdom, y] * k[rdom]) val = hl.sum(blur_x[x, y + rdom] * k[rdom]) if input.output_types()[0] == hl.UInt(16): val = hl.u16(val) output[x, y] = val else: blur_x[x, y, c] = hl.sum(input[x + rdom, y, c] * k[rdom]) val = hl.sum(blur_x[x, y + rdom, c] * k[rdom]) if input.output_types()[0] == hl.UInt(16): val = hl.u16(val) output[x, y, c] = val blur_x.compute_at(output, x).vectorize(x, 16) output.compute_root().tile(x, y, xi, yi, 256, 128).vectorize(xi, 16).parallel(y) return output
def histogram(x, y, c, img, w, h, hist_index): print("GET HIST ON: ", w, h) histogram = hl.Func("histogram") # Histogram buckets start as zero. histogram[hist_index] = 0 # Define a multi-dimensional reduction domain over the input image: r = hl.RDom([(0, w), (0, h)]) # For every point in the reduction domain, increment the # histogram bucket corresponding to the intensity of the # input image at that point. histogram[hl.Expr(img[r.x, r.y])] += 1 histogram.set_estimate(hist_index, 0, 255) # Get the sum of all histogram cells r = hl.RDom([(0,255)]) hist_sum = hl.Func('hist_sum') hist_sum[()] = 0.0 # Compute the sum as a 32-bit integer hist_sum[()] += histogram[r.x] # Return each histogram as a % of total color pct_hist = hl.Func('pct_hist') pct_hist[hist_index] = histogram[hist_index] / hist_sum[()] return histogram
def test_operator_order(): x = hl.Var('x') f = hl.Func('f') x + 1 1 + x f[x] = x**2 f[x] + 1 hl.Expr(1) + f[x] 1 + f[x]
def test_operator_order(): x = hl.Var('x') f = hl.Func('f') x + 1 1 + x print("x", x, ", x + 1", x + 1, ", 1 + x", 1 + x) f[x] = x ** 2 f[x] + 1 hl.Expr(1) + f[x] 1 + f[x] return
def test_var(): v1 = hl.Var() v2 = hl.Var() assert len(v1.name()) > 0 assert len(v2.name()) > 0 assert not v1.same_as(v2) v1 = hl.Var.implicit(1) assert v1.name() == "_1" v2 = hl.Var("_1") assert v1.same_as(v2) v3 = hl._1 assert v1.same_as(v3) v4 = hl.Var("v4") assert not v1.same_as(v4) assert v1.is_implicit() assert v2.is_implicit() assert v3.is_implicit() assert not v4.is_implicit() assert hl.Var("_1").is_implicit() assert not hl.Var("v4").is_implicit() assert v1.implicit_index() == 1 assert v2.implicit_index() == 1 assert v3.implicit_index() == 1 assert v4.implicit_index() == -1 assert hl.Var("_1").implicit_index() == 1 assert hl.Var("v4").implicit_index() == -1 ph = hl._ assert ph.name() == "_" assert ph.is_placeholder() assert hl.Var.is_placeholder(ph) assert not v1.is_placeholder() outermost = hl.Var.outermost() assert outermost.name() == "__outermost" # repr() and str() x = hl.Var('x') assert str(x) == "x" assert repr(x) == "<halide.Var 'x'>" # This verifies that halide.Var is implicitly convertible to halide.Expr r = hl.random_int(x) # This verifies that halide.Var is explicitly convertible to halide.Expr r = hl.random_int(hl.Expr(x))
def test_atomics(): x = hl.Var('x') im = hl.Func('im') f = hl.Func('f') im[x] = (x * x) % 5 r = hl.RDom([(0, 100)]) f[x] = 0 f[hl.Expr(im[r])] += 1 f.compute_root().update().atomic().parallel(r) b = f.realize(5) ref = [0, 0, 0, 0, 0] for i in range(100): idx = (i * i) % 5 ref[idx] += 1 for i in range(5): assert (b[i] == ref[i])
def test_float_or_int(): x = hl.Var('x') i, f = hl.Int(32), hl.Float(32) assert ((x//2) - 1 + 2*(x%2)).type() == i assert ((x/2) - 1 + 2*(x%2)).type() == i assert ((x/2)).type() == i assert ((x/2.0)).type() == f assert ((x//2)).type() == i assert ((x//2) - 1).type() == i assert ((x%2)).type() == i assert (2*(x%2)).type() == i assert ((x//2) - 1 + 2*(x%2)).type() == i assert type(x) == hl.Var assert (x.as_expr()).type() == i assert (hl.Expr(2.0)).type() == f assert (hl.Expr(2)).type() == i assert (x + 2).type() == i assert (2 + x).type() == i assert (hl.Expr(2) + hl.Expr(3)).type() == i assert (hl.Expr(2.0) + hl.Expr(3)).type() == f assert (hl.Expr(2) + 3.0).type() == f assert (hl.Expr(2) + 3).type() == i assert (x.as_expr() + 2).type() == i # yes this failed at some point assert (2 + x.as_expr()).type() == i assert (2 * (x + 2)).type() == i # yes this failed at some point assert (x + 0).type() == i assert (x % 2).type() == i assert (2 * x).type() == i assert (x * 2).type() == i assert (x * 2).type() == i assert ((x % 2)).type() == i assert ((x % 2) * 2).type() == i #assert (2 * (x % 2)).type() == i # yes this failed at some point assert ((x + 2) * 2).type() == i return
def test_int_promotion(): # Verify that (Exprlike op literal) correctly matches the type # of the literal to the Exprlike (rather than promoting the result to int32). # All types that use add_binary_operators() should be tested here. x = hl.Var('x') # All the binary ops are handled the same, so + is good enough # Exprlike = FuncRef f = hl.Func('f') f[x] = hl.u16(x) _check_is_u16(f[x] + 2) _check_is_u16(2 + f[x]) # Exprlike = Expr e = hl.Expr(f[x]) _check_is_u16(e + 2) _check_is_u16(2 + e) # Exprlike = Param p = hl.Param(hl.UInt(16)) _check_is_u16(p + 2) _check_is_u16(2 + p)
def test_float_or_int(): x = hl.Var('x') i32, f32 = hl.Int(32), hl.Float(32) assert hl.Expr(x).type() == i32 assert (x * 2).type() == i32 assert (x / 2).type() == i32 assert ((x // 2) - 1 + 2 * (x % 2)).type() == i32 assert ((x / 2) - 1 + 2 * (x % 2)).type() == i32 assert ((x / 2)).type() == i32 assert ((x / 2.0)).type() == f32 assert ((x // 2)).type() == i32 assert ((x // 2) - 1).type() == i32 assert ((x % 2)).type() == i32 assert (2 * (x % 2)).type() == i32 assert ((x // 2) - 1 + 2 * (x % 2)).type() == i32 assert type(x) == hl.Var assert (hl.Expr(x)).type() == i32 assert (hl.Expr(2.0)).type() == f32 assert (hl.Expr(2)).type() == i32 assert (x + 2).type() == i32 assert (2 + x).type() == i32 assert (hl.Expr(2) + hl.Expr(3)).type() == i32 assert (hl.Expr(2.0) + hl.Expr(3)).type() == f32 assert (hl.Expr(2) + 3.0).type() == f32 assert (hl.Expr(2) + 3).type() == i32 assert (hl.Expr(x) + 2).type() == i32 assert (2 + hl.Expr(x)).type() == i32 assert (2 * (x + 2)).type() == i32 assert (x + 0).type() == i32 assert (x % 2).type() == i32 assert (2 * x).type() == i32 assert (x * 2).type() == i32 assert (x * 2).type() == i32 assert ((x % 2)).type() == i32 assert ((x % 2) * 2).type() == i32 assert (2 * (x % 2)).type() == i32 assert ((x + 2) * 2).type() == i32
def main(): # All Exprs have a scalar type, and all Funcs evaluate to one or # more scalar types. The scalar types in Halide are unsigned # integers of various bit widths, signed integers of the same set # of bit widths, floating point numbers in single and double # precision, and opaque handles (equivalent to void *). The # following array contains all the legal types. valid_halide_types = [ hl.UInt(8), hl.UInt(16), hl.UInt(32), hl.UInt(64), hl.Int(8), hl.Int(16), hl.Int(32), hl.Int(64), hl.Float(32), hl.Float(64), hl.Handle() ] # Constructing and inspecting types. if True: # You can programmatically examine the properties of a Halide # type. This is useful when you write a C++ function that has # hl.Expr arguments and you wish to check their types: assert hl.UInt(8).bits() == 8 assert hl.Int(8).is_int() # You can also programmatically construct Types as a function of other Types. t = hl.UInt(8) t = t.with_bits(t.bits() * 2) assert t == hl.UInt(16) # Or construct a Type from a C++ scalar type #assert type_of<float>() == hl.Float(32) # The Type struct is also capable of representing vector types, # but this is reserved for Halide's internal use. You should # vectorize code by using hl.Func::vectorize, not by attempting to # construct vector expressions directly. You may encounter vector # types if you programmatically manipulate lowered Halide code, # but this is an advanced topic (see hl.Func::add_custom_lowering_pass). # You can query any Halide hl.Expr for its type. An hl.Expr # representing a hl.Var has type hl.Int(32): x = hl.Var("x") assert hl.Expr(x).type() == hl.Int(32) # Most transcendental functions in Halide hl.cast their inputs to a # hl.Float(32) and return a hl.Float(32): assert hl.sin(x).type() == hl.Float(32) # You can hl.cast an hl.Expr from one Type to another using the hl.cast operator: assert hl.cast(hl.UInt(8), x).type() == hl.UInt(8) # This also comes in a template form that takes a C++ type. #assert hl.cast<uint8_t>(x).type() == hl.UInt(8) # You can also query any defined hl.Func for the types it produces. f1 = hl.Func("f1") f1[x] = hl.cast(hl.UInt(8), x) assert f1.output_types()[0] == hl.UInt(8) f2 = hl.Func("f2") f2[x] = (x, hl.sin(x)) assert f2.output_types()[0] == hl.Int(32) and \ f2.output_types()[1] == hl.Float(32) # Type promotion rules. if True: # When you combine Exprs of different types (e.g. using '+', # '*', etc), Halide uses a system of type promotion # rules. These differ to C's rules. To demonstrate these # we'll make some Exprs of each type. x = hl.Var("x") u8 = hl.cast(hl.UInt(8), x) u16 = hl.cast(hl.UInt(16), x) u32 = hl.cast(hl.UInt(32), x) u64 = hl.cast(hl.UInt(64), x) s8 = hl.cast(hl.Int(8), x) s16 = hl.cast(hl.Int(16), x) s32 = hl.cast(hl.Int(32), x) s64 = hl.cast(hl.Int(64), x) f32 = hl.cast(hl.Float(32), x) f64 = hl.cast(hl.Float(64), x) # The rules are as follows, and are applied in the order they are # written below. # 1) It is an error to hl.cast or use arithmetic operators on Exprs of type hl.Handle(). # 2) If the types are the same, then no type conversions occur. for t in valid_halide_types: # Skip the handle type. if t.is_handle(): continue e = hl.cast(t, x) assert (e + e).type() == e.type() # 3) If one type is a float but the other is not, then the # non-float argument is promoted to a float (possibly causing a # loss of precision for large integers). assert (u8 + f32).type() == hl.Float(32) assert (f32 + s64).type() == hl.Float(32) assert (u16 + f64).type() == hl.Float(64) assert (f64 + s32).type() == hl.Float(64) # 4) If both types are float, then the narrower argument is # promoted to the wider bit-width. assert (f64 + f32).type() == hl.Float(64) # The rules above handle all the floating-point cases. The # following three rules handle the integer cases. # 5) If one of the expressions is an integer constant, then it is # coerced to the type of the other expression. assert (u32 + 3).type() == hl.UInt(32) assert (3 + s16).type() == hl.Int(16) # If this rule would cause the integer to overflow, then Halide # will trigger an error, e.g. uncommenting the following line # will cause this program to terminate with an error. # hl.Expr bad = u8 + 257 # 6) If both types are unsigned integers, or both types are # signed integers, then the narrower argument is promoted to # wider type. assert (u32 + u8).type() == hl.UInt(32) assert (s16 + s64).type() == hl.Int(64) # 7) If one type is signed and the other is unsigned, both # arguments are promoted to a signed integer with the greater of # the two bit widths. assert (u8 + s32).type() == hl.Int(32) assert (u32 + s8).type() == hl.Int(32) # Note that this may silently overflow the unsigned type in the # case where the bit widths are the same. assert (u32 + s32).type() == hl.Int(32) if False: # evaluate<X> not yet exposed to python # When an unsigned hl.Expr is converted to a wider signed type in # this way, it is first widened to a wider unsigned type # (zero-extended), and then reinterpreted as a signed # integer. I.e. casting the hl.UInt(8) value 255 to an hl.Int(32) # produces 255, not -1. #int32_t result32 = evaluate<int>(hl.cast<int32_t>(hl.cast<uint8_t>(255))) assert result32 == 255 # When a signed type is explicitly converted to a wider unsigned # type with the hl.cast operator (the type promotion rules will # never do this automatically), it is first converted to the # wider signed type (sign-extended), and then reinterpreted as # an unsigned integer. I.e. casting the hl.Int(8) value -1 to a # hl.UInt(16) produces 65535, not 255. #uint16_t result16 = evaluate<uint16_t>(hl.cast<uint16_t>(hl.cast<int8_t>(-1))) assert result16 == 65535 # The type hl.Handle(). if True: # hl.Handle is used to represent opaque pointers. Applying # type_of to any pointer type will return hl.Handle() #assert type_of<void *>() == hl.Handle() #assert type_of<const char * const **>() == hl.Handle() # (not clear what the proper python version would be) # Handles are always stored as 64-bit, regardless of the compilation # target. assert hl.Handle().bits() == 64 # The main use of an hl.Expr of type hl.Handle is to pass # it through Halide to other external code. # Generic code. if True: # The main explicit use of Type in Halide is to write Halide # code parameterized by a Type. In C++ you'd do this with # templates. In Halide there's no need - you can inspect and # modify the types dynamically at C++ runtime instead. The # function defined below averages two expressions of any # equal numeric type. x = hl.Var("x") assert average(hl.cast(hl.Float(32), x), 3.0).type() == hl.Float(32) assert average(x, 3).type() == hl.Int(32) assert average(hl.cast(hl.UInt(8), x), hl.cast(hl.UInt(8), 3)).type() == hl.UInt(8) print("Success!") return 0
def maybe_mux(s): '''wrap multiple Exprs in mux()''' if len(set(s)) == 1: return s[0] else: return hl.mux(hl.Expr(ru), s)