Summary
we.outer(a, b) now propagates symmetry for repeated operands, but its FLOP counting is still dense.
Current behavior
import whest as we
with we.BudgetContext(flop_budget=10**8, quiet=True) as bc:
v = we.array([1., 2., 3.])
with we.namespace("outer"):
x = we.outer(v, v)
with we.namespace("einsum"):
y = we.einsum("i,j->ij", v, v)
get_flops = lambda k: bc.summary_dict(by_namespace=True)["by_namespace"][k]["flops_used"]
print(get_flops("outer")) # 9
print(get_flops("einsum")) # 6
print(type(x).__name__, x.symmetry)
print(type(y).__name__, y.symmetry)
Both paths produce the same symmetric output, but outer does not get the symmetry-aware savings.
Expected behavior
When outer can analytically infer the same symmetry as einsum("i,j->ij", ...), it should use the same FLOP accounting too.
Possible implementation directions:
- alias repeated-operand
outer to the einsum machinery, or
- reuse the same symmetry-aware cost calculation without changing the surface API
Broader note
This likely applies beyond outer itself: other linalg primitives that can cheaply and analytically infer symmetry should also expose symmetry-aware FLOP costs when possible.
Summary
we.outer(a, b)now propagates symmetry for repeated operands, but its FLOP counting is still dense.Current behavior
Both paths produce the same symmetric output, but
outerdoes not get the symmetry-aware savings.Expected behavior
When
outercan analytically infer the same symmetry aseinsum("i,j->ij", ...), it should use the same FLOP accounting too.Possible implementation directions:
outerto the einsum machinery, orBroader note
This likely applies beyond
outeritself: other linalg primitives that can cheaply and analytically infer symmetry should also expose symmetry-aware FLOP costs when possible.