Alloyのイラストロジックをリファクタリングした日記

ColとRowで似たようなことを別個にやっているので、まとめられるかも、ということで挑戦してみた日記。
ColとRowで個別のnextが入っている点が難しいかもとのことでしたが、そもそもCol -> Row -> ColorのRowだけ固定してCol -> Colorを作る方法がわからなくてどん詰まり。悩んだ挙句「Col -> Color -> Rowにすればいいんだ!」という解決の仕方をしました。

ついでにColとRowを区別するのが面倒だったので両方Intにしました。

one sig Root{
  cell: Int -> Color -> Int
}{
  all x: cell.univ.univ | in_ten[x]
  all y: univ.(univ.cell) | in_ten[y]
  let ten = {x: Int | in_ten[x]}{
    all x, y: ten{
      one x.cell.y
    }
  }
}

pred in_ten(x: Int){
  one x
  0 <= x and x < 10  // notice: for 5 Int required
}

こうしたことで、rowHintとcolHintはすぐにseq Colorを作って共通の処理に入ることが出来るようになりました。

pred hint(line: seq Color, sizes: seq Int){
  some heads: seq Int {
    #heads = #sizes
    sorted[get_heads[line], heads]
    all i: sizes.inds {
      let start = heads[i], end = get_end[start, sizes[i]] {
        one line[end]
        all_black[line, start, end]
        next_is_not_black[line, end]
      }
    }
  }
}
pred colHint (i: Int, sizes: seq Int) {
  hint[getCol[i], sizes]
}
pred rowHint (i: Int, sizes: seq Int) {
  hint[getRow[i], sizes]
}

で、残念ながらこのリファクタリングしたバージョンはコードクローンはなくなったものの、オリジナルより2倍ほど遅くなっています。むむむ。これはオリジナルがfor 10でColとRowの数を10個に限定できているのに対して、こっちはIntにしてしまったから-16〜15の範囲に広がってしまっているせいか??

# Original
33381 vars. 2200 primary vars. 92568 clauses. 854ms.
... is consistent. 415ms.
# Refactored
73481 vars. 4608 primary vars. 219033 clauses. 1426ms.
...is consistent. 858ms.

うーん、そうみたい。泥臭いけどsig SmallIntを作って10個に限定したらかなり縮んだ。

30402 vars. 2848 primary vars. 75273 clauses. 888ms.
... is consistent. 1307ms.

代表的な変更箇所は下の通り。もちろん型の整合性のために何箇所か修正する必要があった。

+sig SmallInt{}
+fun from_int (i: Int): SmallInt {
+  {x: SmallInt | #(x.prevs) = i}
+}
+
+fun to_int (x: SmallInt): Int {
+  #(x.prevs)
+}
+
 pred hint(line: seq Color, sizes: seq Int){
-  some heads: seq Int {
+  some heads: seq SmallInt {

前回の僕の重すぎて動かないコードではsomeが6個ネストしてたわけなんだけども、bonotakeさんのコードでは1個になっていて、そしていまやったようにそのsomeの探索範囲を減らすと生成されるCNFも実行時間も大きく削減されるようだ。someは要注意だな。

ソースコードはこちらから: https://github.com/nishio/learning_alloy/tree/master/illust_logic