kmyk-jikka / Jikka

an automated solver for problems of competitive programming
https://kmyk-jikka.github.io/Jikka/playground
Apache License 2.0
152 stars 11 forks source link

最適化がかからないように書いたフィボナッチが正しくないコードに変換される #153

Closed riantkb closed 2 years ago

riantkb commented 2 years ago

Summary / 概要

行列累乗に変換されないように書いたフィボナッチ数列の第 N 項を求めるコードが、C++ に変換された際にフィボナッチ数列の第 N 項を求めていない

Steps to reproduce / 再現方法

environments:

comment

kmyk commented 2 years ago

「core 言語ではノンブロッキング代入みたいな挙動をする書き方になっていたのが C++ に直すタイミングでそうでなくなった結果変なことになっているのかな」という予想は正しそうで、バグってるのはたぶん src/Jikka/CPlusPlus/Convert/MoveSemantics.hssrc/Jikka/CPlusPlus/Convert/UnpackTuples.hs の中のどこかです。でも具体的にこの中のどこでどうなってるのかは書いた自分でもすぐには分からない


メモ: C++ に変換される直前の core 言語の式

fun (n$607: int) ->
    foldl((fun ($608: int * int) ($609: int) ->
        ($609 + $608.0 + $608.1, $608.0)
    ), (1, 0), iterate(n$607, (fun ($610: int list) ->
        snoc($610, 0)
    ), nil<int>)).0

@uta8a 同じバグで WA になってるものが #150 にもあるかも?

riantkb commented 2 years ago

https://github.com/kmyk/Jikka/blob/a7cafc004079bfa42256544f5cf4f033b31f71ed/src/Jikka/CPlusPlus/Convert.hs#L24 この行をコメントアウトしたら正しいコードが出力されるようになった

int64_t solve(int64_t n_0) {
    std::vector<int64_t> x1;
    for (int32_t i2 = 0; i2 < int32_t(n_0); ++ i2) {
        x1.push_back(0);
    }
    std::array<int64_t, 2> x5 = std::array<int64_t, 2>{1, 0};
    for (int64_t x6 : x1) {
        x5 = std::array<int64_t, 2>{x6 + x5[0] + x5[1], x5[0]};
    }
    return x5[0];
}
kmyk commented 2 years ago

MoveSemantics.hs が以下のような C++ について

int func(vector<int> a) {
    vector<int> b = a;
    b.push_back(10);
    vector<int> c = b;
    c.push_back(10);
    return b.size() + c.size();
}

これを間違えて

int func(vector<int> a) {
    a.push_back(10);
    a.push_back(10);
    return a.size() + a.size();
}

に変換してるぽい

kmyk commented 2 years ago

いや、これは無関係な別のバグでは?

riantkb commented 2 years ago

MoveSemantics をコメントアウトしてもコードがほぼ変わらないので、それは別のバグな気もしています 😇

(a, b) = (c, d)

a = c
b = d

に変換するのではなく状況に応じて

tmp_1 = c
tmp_2 = d
a = tmp_1
b = tmp_2

みたいにする、みたいなロジック(もしくはそれと同等な処理)ってどこかに組み込まれていたりしますか?

kmyk commented 2 years ago

core でも let (a, b) = (c, d) in ... のような書き方はできないようになってます。 C++ だと auto [a, b] = ...; は一応はあるが使われていないはず。 なので std::pair<int, int> e = make_pair(c, d); から考えはじめてよいです。

まず

std::pair<int, int> e = make_pair(c, d);
f(get<0>(e));

std::pair<int, int> e0 = get<0>(make_pair(c, d));
std::pair<int, int> e1 = get<1>(make_pair(c, d));
f(e0);

にするような変換 (UnpackTuples.hs) があります。これをさらに

int e0 = c;
int e1 = d;
f(e0);

にするような変換 (同じく UnpackTuples.hs) があります。 これ以上のことはしていません。


ここまで展開されると

int e1 = d;
f(c);

という copy を move にする変換 (MoveSemantics.hs) が動くこともありますが、今回は関係なかったようです。

kmyk commented 2 years ago

追加です。忘れてたのですが、

std::pair<int, int> e = make_pair(c, d);
f(get<0>(e));
e = make_pair(x, y);

みたいな状況だと

std::pair<int, int> e0 = get<0>(make_pair(c, d));
std::pair<int, int> e1 = get<1>(make_pair(c, d));
f(e0);
e0 = x;
e1 = y;

にするんでした。なんだかこれが悪い気がします 😇

kmyk commented 2 years ago

だから、問題の代入を

pair<int, int> tmp = make_pair(x, y);
e0 = get<0>(tmp);
e1 = get<0>(tmp);

にすればよいはず。 具体的なコードは以下のあたり

https://github.com/kmyk/Jikka/blob/41cc148426aa10eb770aaa08882d8b7268c45606/src/Jikka/CPlusPlus/Convert/UnpackTuples.hs#L164-L179


@riantkb かなり手伝ってもらったし、せっかくなので最後の部分やりませんか? 一時変数を置く処理を足す修正と今回使った a.py をテストに追加する (#141) 作業です

riantkb commented 2 years ago

haskell わからん〜〜〜、となっているんですが、せっかくなのでやりたい気持ちがあります

kmyk commented 2 years ago

ありがとう。任せます

Haskell の言語仕様パートはやればできると思う。 どちらかというと Jikka 内部での C++ の構文木の表現がどうなってるかの方が分かりにくいかもしれません。ドキュメントは https://kmyk.github.io/Jikka/haddock/Jikka-CPlusPlus-Language-Expr.html にあります。(今からもうすこしコメント追加してきます)

riantkb commented 2 years ago

最初に示したコードが(N が小さい範囲では)正しく変換できることを確認し、またその過程で遭遇した問題をこちらのコメントに記したので、この issue は閉じようと思います。 https://github.com/kmyk/Jikka/issues/154#issuecomment-894334767