RGLPKの使い方

線形計画法ライブラリのGLPKをrubyから使えるRglpkなる物があったが, ドキュメントが全然見当たらなかったので, 使い方をメモしておこうかと.
なんか間違ってたら教えてくれると嬉しいです.

インストール

ここのread meに書いてある通り.

いきなりgem install rglpkすると怒られるので, GLPKをDLして, makeして, includeとかにファイルぶち込んだりとかする.
windowsならmsysとかからやればよい.

それが終わったら, gem install rglpkする.

使い方

基本的に, read meに書いてあったように, 適当に変数と制約式を入れていけばよい.
MIPのがread meに無かったので, サンプルとして, 0-1整数計画法でn-queenを書いてみた.
参考:ここここ

require "rglpk"

SIZE = 8

#線形計画問題を作って, 名前を付ける.
lp = Rglpk::Problem.new
lp.name = "%d-queen" % SIZE


#SIZE**2 変数
cols = lp.add_cols(SIZE ** 2)
for i in 0...SIZE
  for j in 0...SIZE
    #変数に名前付ける. 省略可能.
    cols[i * SIZE + j].name = "(%d, %d)" % [i, j]

    #変数の上界と下界を設定する. 今回はバイナリ変数なので, 省略出来る.
    #set_bounds(*, lower, upper)
    #GLP_DB: lower <= x <= upper
    #GLP_FX: lower =  x =  upper
    #GLP_FR: -Inf  <  x <  Inf
    #GLP_LO: lower <= x <  Inf
    #GLP_UP: -Inf  <  x <= upper
    #cols[i * SIZE + j].set_bounds(Rglpk::GLP_DB, 0, 1)

    #変数の種類を設定する. 何もしないとGLP_CV.
    #GLP_CV: 実数
    #GLP_IV: 整数
    #GLP_BV: バイナリ
    #バイナリ以外の時は, set_boundsする事.
    cols[i * SIZE + j].kind = Rglpk::GLP_BV
  end
end

#制約式:
# 縦, 横でSIZE本ずつ.
# 斜めで 2 * (SIZE * 2 - 1) 本
# 後でset_matrixする為に, matも作っておく.
mat = []
rows = lp.add_rows(SIZE * 2 + 2 * (SIZE * 2 - 1))
cnt = 0
#横
for i in 0...SIZE
  #名前付ける(省略可)
  rows[cnt].name = "%dth row" % i

  #変数と同様に上界と下界を設定する.
  #(GLP_FX, 1, 1)でもいいけど, 3-queenでも2個置けるのが出たりするように.
  rows[cnt].set_bounds(Rglpk::GLP_DB, 0, 1)

  #mat作り.
  vec = [0] * (SIZE ** 2)
  for j in 0...SIZE
    vec[i * SIZE + j] = 1
  end
  mat << vec
  cnt += 1
end
#縦
for j in 0...SIZE
  rows[cnt].name = "%dth col" % j
  rows[cnt].set_bounds(Rglpk::GLP_DB, 0, 1)

  vec = [0] * (SIZE ** 2)
  for i in 0...SIZE
    vec[i * SIZE + j] = 1
  end
  mat << vec
  cnt += 1
end

#斜め1
for s in 0...(SIZE * 2 - 1)
  rows[cnt].name = "(i, j) s.t. i+j = %d" % s
  rows[cnt].set_bounds(Rglpk::GLP_DB, 0, 1)

  vec = [0] * (SIZE ** 2)
  for i in 0..s
    vec[i * SIZE + (s - i)] = 1 if (0...SIZE).include?(i) && (0...SIZE).include?(s - i)
  end
  mat << vec
  cnt += 1
end

#斜め2
for s in (-SIZE + 1)..(SIZE - 1)
  rows[cnt].name = "(i, j) s.t. j-i = %d" % s
  rows[cnt].set_bounds(Rglpk::GLP_DB, 0, 1)

  vec = [0] * (SIZE ** 2)
  for i in 0...SIZE
    vec[i * SIZE + (s + i)] = 1 if (0...SIZE).include?(i) && (0...SIZE).include?(s + i)
  end
  mat << vec
  cnt += 1
end

#制約式の係数リストを渡す.
#何故か一次元配列...
lp.set_matrix(mat.flatten)

#目的関数を作る.
#制約満たすのを一つ欲しいだけなら, 省略しても大丈夫っぽい.
#dirは最大化又は最小化に従って, GLP_MAX又はGLP_MIN.
#変数に対する係数のリストの形で与える.
#coefsはcolsの後じゃないとダメっぽい.
lp.obj.name = "number of Queens"
lp.obj.dir = Rglpk::GLP_MAX
lp.obj.coefs = [1] * (SIZE ** 2)

#MIPの時はmip, LPの時はsimplex使う.
#MIPでも, simplexでLP緩和を解けるっぽい.
#MIPの時は{:presolve => Rglpk::GLP_ON}するか, 先にsimplexするかで, LP緩和問題を解いておく.
#lp.simplex
lp.mip({:presolve => Rglpk::GLP_ON})

#結果のチェック.
#OPTIMALだとRglpk::GLP_OPTになるっぽい.
#LPの時はstatus, MIPの時はmip_status.
raise unless lp.mip_status == Rglpk::GLP_OPT

#結果の取り出し.
#LPの時はget_prim, MIPの時はmip_val.
for i in 0...SIZE
  for j in 0...SIZE
    print cols[i * SIZE + j].mip_val.round
  end
  puts
end

解らなかったらrglpk/testの中を漁る.
定数とかはrglpk/ext/glpk_wrapper.cの下の方とかにあったりする.