MEP 7: 0次元へのreduce
- Created: 2022-12-03 13:04:23
MEP 6: find_first_indexを検討して浮かび上がった項目。
現状以下の式は、概念的にはreduceに見える。
let wsum = weight.sum |x, y, val| { val }
ただ結果が0次元になるので変数になるからdefである必要も無く見える。 sumはreduceがこれまで無かったのであまり気にならなかったが、find_first_indexの実現にreduceのaccumulateを使っているものは、0次元になった瞬間に同じ処理なのに書き方が無くなるのは違和感がある。
# 多次元
def _med by reduce<_hist>.accumulate(dim=0, init=-1) |i, col, val, accm| {
ifel(accm != -1, accm, ...)
elif(val < _hist(255, col)/2, -1, i)
}
# 0次元
def i3 by reduce<wcumsum>.accumulate(dim=0, init=-1) |ind, val, accm | {
ifel(accm != -1, accm, ...)
elif(i < val, -1, ind)
}
この問題について、検討していきたい。
まず、0次元はletできるとするとどうだろう?
def i3 by reduce<wcumsum>.accumulate(dim=0, init=-1) |ind, val, accm | {
ifel(accm != -1, accm, ...)
elif(i < val, -1, ind)
}
let i3 = reduce<wcumsum>.accumulate(dim=0, init=-1) |ind, val, accm | {
ifel(accm != -1, accm, ...)
elif(i < val, -1, ind)
}
この場合、下の書き方ならこのreduceはexpressionとして扱えて欲しくなる。今の所ts.sumはexprとして扱えるようになっているので同じ事をできるようにはできるが、def byが特別なシンタックスなのに結果が0次元になった瞬間に単なる関数コールのようになるのはややこしくないのだろうか?
現状、IRとしてはts.for_eachと似たものとしているが、そもそもdefのみにして0次元だと変数になる、の方が実装は簡単になる。 使う側にどのくらい違和感があるかだが…
ようするに以下が、
let area = edge.sum |i, val| { 2*val+1 }
以下になるのがどのくらいユーザー側から見て許せるか、という話になる。
def area by reduce<edge>.sum(dim=0) |i, val| { 2*val+1 }
でもテンソルの扱いは特別であるというのが言語上からも分かっていて一貫性がある方が、むしろMFGらしいかもしれない。
関係ないが一次元のテンソルのreduceではdim=0は省略出来たらいいよなぁ。 デフォルトを0にしてしまうか、それとも引数違いのオーバーロードを定義できるようにするかで解決したい気がする。
やはりdefで0次元のケースは書けるべきに思う。
本質的には0次元のケースでletのシンタックスシュガーを実装するかどうか、という問題であって、def byをサポートすべきなのは議論の余地は無い気がする。 先にシンタックスシュガーで扱うべきものを実装してしまった為同じ機能を提供するのに違和感があったが、考え方が間違っているだろう。
現状、ts.sumとrsumだけhoistという事が必要になっている。 ts.sumがどうなんだという事を考えると、rsumも変な文法だよなぁ。
ただrsumはそのままreturnしたいのでdef byで一旦一時変数を定義するのは野暮ったい。
モザイクフィルタの以下のコードを考えよう。
@bounds( (input.extent(0)-1)/MOSAIC_WIDTH+1, (input.extent(1)-1)/MOSAIC_WIDTH+1)
def avg |x, y|{
rsum(0:MOSAIC_WIDTH, 0:MOSAIC_WIDTH) |rx, ry|{
let [a, r, g, b] = clamped( MOSAIC_WIDTH*x+rx, MOSAIC_WIDTH*y+ry )
[a, *[r, g, b]*a]
}
}
本質的にはこれは、以下のようなshapeの仮想的なテンソル
(input.extent(0)-1)/MOSAIC_WIDTH+1, (input.extent(1)-1)/MOSAIC_WIDTH+1), MOSAIC_WIDTH, MOSAIC_WIDTH)
の、中身を使わないdim=2, dim=3のreduceなんだよな。 def byで定義される一味として、以下みたいな感じになっている方がreduceとは一貫性がある。
@bounds( (input.extent(0)-1)/MOSAIC_WIDTH+1, (input.extent(1)-1)/MOSAIC_WIDTH+1)
def avg by rsum(0:MOSAIC_WIDTH, 0:MOSAIC_WIDTH)|x, y, rx, ry|{
let [a, r, g, b] = clamped( MOSAIC_WIDTH*x+rx, MOSAIC_WIDTH*y+ry )
[a, *[r, g, b]*a]
}
だが読みにくいな、これ。野暮ったいのはいいが読みにくいのはダメだな。
ts.sumは名前どうかな、と思っていたが、やはりこの名前はreduceのsumなので正しいな。 要素の和の方をsum_elemとするのがいいか。
とりあえずsumに関してはreduceで使うケースがまだ無いのでこのままにしておく。 accumulateは0次元になるケースをちゃんと対応する事にする。