Files
StarPilot/tinygrad_repo/tinygrad/uop/divandmod.py
T
firestar5683 22a8575199 Vendor dependencies and resources for standalone builds
Keep official openpilot ancestry and dependency licenses. Store resources directly in Git, remove LFS setup requirements, and leave the downloadable big model outside the source tree.
2026-10-03 21:01:18 +00:00

110 lines
5.4 KiB
Python

import functools, itertools, math
from tinygrad.uop.ops import PatternMatcher, UPat, Ops, UOp
from tinygrad.dtype import dtypes
from tinygrad.helpers import unwrap
# NOTE: this cache is only on index UOps
@functools.cache
def fold_divmod_general(d: UOp) -> UOp|None:
x, y = d.src
if y.vmin==y.vmax==0: raise ZeroDivisionError(f"{'Division' if d.op is Ops.FLOORDIV else 'Mod'} by zero trying to rewrite {x.alu(d.op, y)}")
# x//y is constant
if (xdiv:=x//y).vmin == xdiv.vmax: return x - xdiv.vmin*y if d.op is Ops.FLOORMOD else xdiv.const_like(xdiv.vmin)
# PARAM // c is irreducible
if x.op is Ops.PARAM and y.op is Ops.CONST and x.arg.multiple_of % y.val == 0: return d.const_like(0) if d.op is Ops.FLOORMOD else None
# split uops for the rest of the processing
x_peeled, const = x.pop_const()
uops_no_const = list(x_peeled.split_uop(Ops.ADD))
# ** Constant Denominator Rules **
# these rules strictly require y to be a scalar constant > 0
if y.op is Ops.CONST and (c := y.val) > 0:
# nested_div: (x%(k*c))//c -> (x//c)%k (requires k>0); the mod case is handled by remove_nested_mod below
if d.op is Ops.FLOORDIV and x.op is Ops.FLOORMOD and (k := x.src[1].divides(c)) is not None and k.vmin > 0: return x.src[0] // y % k
# remove_nested_mod in sum: (a%4 + b)%2 -> (a+b)%2
if d.op is Ops.FLOORMOD:
new_xs, changed = [], False
for u in uops_no_const:
if u.op is Ops.FLOORMOD and u.src[1].divides(c) is not None:
u = u.src[0]
changed = True
new_xs.append(u)
if changed: return (UOp.usum(*new_xs) + const) % y
# Shared decomposition for folding rules
decomp = [(u.divides(f:=u.const_factor()),f) for u in uops_no_const]
terms, factors = zip(*decomp)
# fold_divmod_congruence: fold if a is congruent to an expression whose range is between 0 and c
# try both signs of the remainder for a lone term (covers a binary numerator that crosses one period)
# or on an exact f%c == c//2 tie; otherwise pick the smaller to keep the product over terms small
rem_choices = [(r, r-c) if (r:=f%c)*2 == c or len(terms)==1 else (min(r, r-c, key=abs),) for f in factors]
for rems in itertools.product(*rem_choices):
if (rem:=sum(r*v for r,v in zip(rems,terms))+const%c).vmin//c==rem.vmax//c:
if d.op is Ops.FLOORMOD: return rem - rem.vmin//c*c
return sum((f-r)//c * v for f,r,v in zip(factors,rems,terms)) + const//c + rem.vmin//c
# gcd_with_remainder: factor out common gcd from numerator
if (g:=math.gcd(*factors, c)) > 1:
new_x = unwrap(x_peeled.divides(g)).simplify() + (const//g)%(c//g)
if new_x.vmin >= 0:
if d.op is Ops.FLOORMOD: return new_x % (c//g) * g + const%g
return new_x // (c//g) + const//c
# nest_by_factor: x//c -> (x//f)//(c//f), x%c -> (x//f%(c//f))*f + b where b=x%f
# FLOORDIV identity holds for any sign of x; FLOORMOD reconstruction needs x.vmin>=0
results = []
for div in {abs(f) for u, f in zip(uops_no_const, factors) if u.op is not Ops.CONST and 1 < abs(f) < c and (c%f)==0}:
if (newxs := fold_divmod_general(x//div)) is not None:
if d.op is Ops.FLOORDIV:
results.append((len(newxs.backward_slice), newxs // (c // div)))
elif x.vmin >= 0 and newxs.vmin >= 0:
b_parts = [f%div*t for f, t in zip(factors, terms) if f%div]
if const % div: b_parts.append(x.const_like(const % div))
b = UOp.usum(*b_parts) if b_parts else x.const_like(0)
if 0 <= b.vmin and b.vmax < div:
results.append((len((r:=(newxs % x.ufix(c//div))*div + b).backward_slice), r))
if results: return min(results, key=lambda r: r[0])[1]
# ** Variable Denominator / Fallback Rules **
# These rules apply to variables OR constants that failed the checks above.
# Reconstruct all uops including const for these checks.
all_uops = list(x.split_uop(Ops.ADD))
# divide_by_gcd: x//y -> (x//gcd)//(y//gcd)
gcd = UOp.gcd(*all_uops, y).simplify()
if not (gcd.op is Ops.CONST and gcd.val==1):
ret = unwrap(x.divide_exact(gcd)).alu(d.op, unwrap(y.divide_exact(gcd)))
return ret*gcd if d.op is Ops.FLOORMOD else ret
# factor_remainder: (d*x+y)//d -> x+y//d
if y.vmin<0 or x.vmin<0: return None
quo, rem = [], []
for u in all_uops:
if (q:=u.divide_exact(y)) is not None: quo.append(q)
elif y.op is Ops.CONST and (c:=u.const_factor())%y.val!=c:
rem.append(u.divides(c)*(c%y.val))
quo.append(u.divides(c)*(c//y.val) if d.op is Ops.FLOORDIV else u.const_like(0))
else: rem.append(u)
if not quo: return None
new_x = sum(rem)+x.const_like(0)
if new_x.vmin<0: return None
return new_x%y if d.op is Ops.FLOORMOD else new_x//y+sum(quo)
div_and_mod_symbolic = PatternMatcher([
# ** 1. Fast Inline Rules **
# (x//c+a)//d -> (x+a*c)//(c*d) for c>0, d>0
((UPat.var("x")//UPat.cvar("c") + UPat.cvar("a"))//UPat.cvar("d"), lambda x,c,a,d: (x+a*c)//(c*d) if d.vmin>0 else None),
# (x+c)//d -> (x+c%d)//d + c//d ; (x+c)%d -> (x+c%d)%d (split the multiple of d out of the const, holds for any d!=0)
(UPat((Ops.FLOORDIV, Ops.FLOORMOD), src=(UPat.var("x", dtypes.weakint)+UPat.cvar("c"), UPat.cvar("d")), name="n"),
lambda n,x,c,d: None if d.val==0 or c.val%d.val==c.val else
(x+c.val%d.val)//d + c.val//d.val if n.op is Ops.FLOORDIV else (x+c.val%d.val)%d),
# ** 2. Slow Rules **
(UPat((Ops.FLOORDIV, Ops.FLOORMOD), dtypes.weakint, name="d"), lambda d: fold_divmod_general(d)),
])