|
5 | 5 |
|
6 | 6 | import sympy |
7 | 7 | import inspect |
| 8 | +from collections import namedtuple |
8 | 9 | from .kernel_api import _verify_kernel_params |
9 | 10 | from .kernel_api import * |
10 | 11 | from .dispatcher import create_dispatcher |
@@ -88,33 +89,139 @@ def _get_typing_module(): |
88 | 89 | return get_typing_module() |
89 | 90 |
|
90 | 91 |
|
91 | | -def _get_symbol_type(term, ceiling=False): |
92 | | - match term: |
93 | | - case sympy.Symbol(): |
94 | | - return typing.SymbolType.get(term.name) |
95 | | - |
96 | | - case sympy.Mul(): |
97 | | - lhs, rhs = term.args |
98 | | - lhs = _get_symbol_type(lhs) |
99 | | - if isinstance(rhs, sympy.Pow): |
100 | | - arg, p = rhs.args |
101 | | - assert p == -1, f"Only -1 power is supported, got {p}" |
102 | | - arg = _get_symbol_type(arg) |
103 | | - if ceiling: |
104 | | - return lhs / arg |
105 | | - else: |
106 | | - return lhs // arg |
| 92 | +_Rational = namedtuple("_Rational", ["numerator", "denominator"]) |
| 93 | + |
| 94 | + |
| 95 | +def _get_symbol_type(expr): |
| 96 | + stack = [] |
| 97 | + |
| 98 | + def addi(lhs, rhs): |
| 99 | + return lhs + rhs |
| 100 | + |
| 101 | + def muli(lhs, rhs): |
| 102 | + return lhs * rhs |
| 103 | + |
| 104 | + def _add(lhs, rhs): |
| 105 | + is_rational_lhs = isinstance(lhs, _Rational) |
| 106 | + is_rational_rhs = isinstance(rhs, _Rational) |
| 107 | + if is_rational_lhs and not is_rational_rhs: |
| 108 | + numerator = muli(lhs.denominator, rhs) |
| 109 | + numerator = addi(numerator, lhs.numerator) |
| 110 | + return _Rational(numerator, lhs.denominator) |
| 111 | + elif not is_rational_lhs and is_rational_rhs: |
| 112 | + numerator = muli(lhs, rhs.denominator) |
| 113 | + numerator = addi(numerator, rhs.numerator) |
| 114 | + return _Rational(numerator, rhs.denominator) |
| 115 | + elif is_rational_lhs and is_rational_rhs: |
| 116 | + lhs_numerator = muli(lhs.numerator, rhs.denominator) |
| 117 | + rhs_numerator = muli(rhs.numerator, lhs.denominator) |
| 118 | + numerator = addi(lhs_numerator, rhs_numerator) |
| 119 | + denominator = muli(lhs.denominator, rhs.denominator) |
| 120 | + return _Rational(numerator, denominator) |
| 121 | + else: |
| 122 | + return addi(lhs, rhs) |
| 123 | + |
| 124 | + # `x * (a/b)` transformed into `(x * a) / b` |
| 125 | + def _mul(lhs, rhs): |
| 126 | + is_rational_lhs = isinstance(lhs, _Rational) |
| 127 | + is_rational_rhs = isinstance(rhs, _Rational) |
| 128 | + if is_rational_lhs and not is_rational_rhs: |
| 129 | + numerator = muli(lhs.numerator, rhs) |
| 130 | + return _Rational(numerator, lhs.denominator) |
| 131 | + elif not is_rational_lhs and is_rational_rhs: |
| 132 | + numerator = muli(lhs, rhs.numerator) |
| 133 | + return _Rational(numerator, rhs.denominator) |
| 134 | + elif is_rational_lhs and is_rational_rhs: |
| 135 | + numerator = muli(lhs.numerator, rhs.numerator) |
| 136 | + denominator = muli(lhs.denominator, rhs.denominator) |
| 137 | + return _Rational(numerator, denominator) |
| 138 | + else: |
| 139 | + return muli(lhs, rhs) |
| 140 | + |
| 141 | + def _floor(value): |
| 142 | + if isinstance(value, _Rational): |
| 143 | + value = value.numerator // value.denominator |
| 144 | + |
| 145 | + return value |
| 146 | + |
| 147 | + def _ceiling(value): |
| 148 | + if isinstance(value, _Rational): |
| 149 | + value = value.numerator / value.denominator |
| 150 | + |
| 151 | + return value |
| 152 | + |
| 153 | + def _group_rationals(stack, count): |
| 154 | + """Group rationals and non-rationals args into 2 contiguous sets. |
| 155 | +
|
| 156 | + This allows to mul/add all non-rationals first, reducing total number of ops. |
| 157 | + """ |
| 158 | + rationals = [] |
| 159 | + non_rationals = [] |
| 160 | + for _ in range(count): |
| 161 | + val = stack.pop() |
| 162 | + if isinstance(val, _Rational): |
| 163 | + rationals.append(val) |
107 | 164 | else: |
108 | | - rhs = _get_symbol_type(rhs) |
109 | | - return lhs * rhs |
110 | | - |
111 | | - case sympy.floor(): |
112 | | - return _get_symbol_type(term.args[0]) |
113 | | - |
114 | | - case sympy.ceiling(): |
115 | | - return _get_symbol_type(term.args[0], ceiling=True) |
116 | | - |
117 | | - assert False, f"Can't convert value {term} : {type(term)}" |
| 165 | + non_rationals.append(val) |
| 166 | + |
| 167 | + return non_rationals + rationals |
| 168 | + |
| 169 | + def _apply(args, func): |
| 170 | + assert len(args) > 0 |
| 171 | + value = args[0] |
| 172 | + for val in args[1:]: |
| 173 | + value = func(value, val) |
| 174 | + |
| 175 | + return value |
| 176 | + |
| 177 | + def _enforce_non_rational(val, term): |
| 178 | + if isinstance(val, _Rational): |
| 179 | + raise ValueError(f"Rational is not supported yet in '{type(term)}'") |
| 180 | + |
| 181 | + if not isinstance(expr, sympy.Expr): |
| 182 | + expr = sympy.sympify(expr) |
| 183 | + |
| 184 | + for term in sympy.postorder_traversal(expr): |
| 185 | + match term: |
| 186 | + case sympy.Symbol(): |
| 187 | + stack.append(typing.SymbolType.get(term.name)) |
| 188 | + case sympy.Integer(): |
| 189 | + stack.append(_get_literal(int(term))) |
| 190 | + case sympy.Mul(): |
| 191 | + args = _group_rationals(stack, len(term.args)) |
| 192 | + stack.append(_apply(args, _mul)) |
| 193 | + case sympy.Add(): |
| 194 | + args = _group_rationals(stack, len(term.args)) |
| 195 | + stack.append(_apply(args, _add)) |
| 196 | + case sympy.Mod(): |
| 197 | + rhs = stack.pop() |
| 198 | + lhs = stack.pop() |
| 199 | + _enforce_non_rational(rhs, term) |
| 200 | + _enforce_non_rational(lhs, term) |
| 201 | + mod = lhs % rhs |
| 202 | + stack.append(mod) |
| 203 | + case sympy.Pow(): |
| 204 | + assert term.args[1] == -1, f"Only -1 power is supported, got {p}" |
| 205 | + p = stack.pop() |
| 206 | + val = stack.pop() |
| 207 | + stack.append(_Rational(_get_literal(1), val)) |
| 208 | + case sympy.floor(): |
| 209 | + stack.append(_floor(stack.pop())) |
| 210 | + case sympy.ceiling(): |
| 211 | + stack.append(_ceiling(stack.pop())) |
| 212 | + case sympy.Rational(): |
| 213 | + numerator = _get_literal(term.p) |
| 214 | + denominator = _get_literal(term.q) |
| 215 | + stack.append(_Rational(numerator, denominator)) |
| 216 | + case sympy.UnevaluatedExpr(): |
| 217 | + continue |
| 218 | + case _: |
| 219 | + raise ValueError(f"Can not handle {type(term)} : {term}") |
| 220 | + |
| 221 | + if len(stack) != 1 or isinstance(stack[0], _Rational): |
| 222 | + raise ValueError(f"Expected single result, got {len(stack)}: {stack}") |
| 223 | + |
| 224 | + return stack[0] |
118 | 225 |
|
119 | 226 |
|
120 | 227 | def _get_shape_attr(src): |
|
0 commit comments