[F#] 数独をF#で作る

数独をF#で解く – Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/5304

の続き。二次元トラスは別途「Visual Basicでわかる やさしい有限要素法の基礎」が届いたので、これを見てから。この本、VB6 なのかと思ったら、VB2008 で書かれた新しい本でした。これだったら、結構流用がきくかも。元ネタは Fortran らしいのですが、グラフィックな部分とか .NET で書かれているので助かる。XAMLのPATHあたりに直すと結構いいかもという感じですね。

さて、

数独の問題を作るほうは、

  1. 矛盾なくNxNを敷き詰める。
  2. ランダムに1個空白にする。
  3. 解が1つしかないことを確認する。再び2へ

を地道に実装したのが次です。やっぱり、再帰のところがうまく作れなくて、excepiton で抜けていますが、これはいずれ修正する…かも。

open System

let bsize = 3
let size = bsize * bsize
let size2 = size * size
let pazzle = Array2D.zeroCreate<int> size size

// ルールにマッチする数を取り出す
let rec rule (m:int[,]) x y v (lst:int list) =
    // 横一列をチェック
    let rule_x (m:int[,]) x y v =
        [|for i in 0..size-1 -> m.[y,i]|] |> Array.exists (fun t -> t = v)
    // 縦一列をチェック
    let rule_y (m:int[,]) x y v =
        [|for i in 0..size-1 -> m.[i,x]|] |> Array.exists (fun t -> t = v)
    // boxをチェック
    let rule_box (m:int[,]) x y v = 
        let x' = x/bsize*bsize
        let y' = y/bsize*bsize
        [| for i in 0..bsize-1 do
            for j in 0..bsize-1 -> m.[y'+j,x'+i] |] |> Array.exists (fun t -> t = v)

    if v = 0 then 
        []
    elif rule_x m x y v = false &&
        rule_y m x y v = false &&
        rule_box m x y v = false then
        v::rule m x y (v-1) lst
    else
        rule m x y (v-1) lst

// ランダムに1つ選ぶ
let rnd = new Random()

let rec remove i (l:'a list) =
     match i,l with
     | 0, x::xs -> xs
     | i, x::xs -> x::remove (i-1) xs
     | i, [] -> failwith "index out of range"

let rec shaffle (l:'a list) =
    match l with
    | x::[] -> [x]
    | x::xs -> 
        let i = rnd.Next(l.Length)
        let l' = remove i l
        l.[i]::shaffle l'
    | [] -> []

let rec quest (m:int[,]) x y =
    // printfn "%d %d" x y
    // printfn "%A" m
    if x = 9 && y = 8 then
        // 完成
        printfn "success."
        printfn "%A" m
        raise (new Exception("ok"))
    elif x = 9 then
        quest m 0 (y+1) 
    else
        let lst = rule m x y size []
        if lst.Length > 0 then
            let lst' = shaffle lst
            for i in 0..lst'.Length-1 do
                let v = lst'.[i]
                let m' = Array2D.copy m
                m'.[y,x] <- v
                quest m' (x+1) y 
        
let Quest (m:int&#91;,&#93;) =
    try 
        quest m 0 0
    with
        | _ -> printfn "ok."

// 数字の敷き詰めを作成
Quest pazzle

ごちゃごちゃしていますが、1番の数の敷き詰めを作っているところです。左上から順番に番号をいれて、ルールに沿って(縦/横/ボックス)候補の数を配置しています。配置するときにランダムに数値を選ばせるために、shaffle を使っています。単純に数え上げのところは、この部分はいらないのですが、それだと同じ問題になってしまうし。
3×3の場合には、結構なスピードで敷き詰めができあがります…が、4×4にすると途端に遅くなりますね。1分程度で終わる場合もあれば、30分やってやっと終わる場合もあります。このあたり、最適化の山を登っている感じ。達成しない準最適化の山に登ってしまうと一度降りるのに時間がかかります。将棋の枝切りとかこのあたりのロジックなのかも。そうそう、やねうらおさんの「探索の深さが強さのイコールとはならない」というのは、このあたりの話で、完全に探索ができない場合には「探索自体の深さ」は「強さ」=正解そのものとは違っていて、さらに将棋の指し手が極めて少ない(2,3手とか)のであれば、その探索の深さ(指し手の多さ)は、強さに比例しない…だろう、ってことだと思います。まあ、実装できるのがすごいんですが。なんとなく想像で。

一旦敷き詰めができた配列をコピーして、今度は問題を作っていきます。

let ans' = 
    [[7; 3; 1; 2; 6; 4; 9; 8; 5]
     [9; 4; 2; 5; 3; 8; 7; 6; 1]
     [8; 6; 5; 7; 1; 9; 3; 2; 4]
     [2; 7; 9; 8; 5; 3; 4; 1; 6]
     [6; 5; 4; 9; 7; 1; 8; 3; 2]
     [1; 8; 3; 6; 4; 2; 5; 7; 9]
     [5; 2; 8; 3; 9; 6; 1; 4; 7]
     [4; 9; 6; 1; 8; 7; 2; 5; 3]
     [3; 1; 7; 4; 2; 5; 6; 9; 8]]
let A' = Array2D.init 9 9 (fun i j -> ans'.[i].[j])

let mutable scnt = 0

let rec solve (m:int[,]) x y = 
    // printfn "%d %d" x y
    if x = 9 && y = 8 then
        // 完成
        scnt <- scnt + 1
    elif x = 9 then
        solve m 0 (y+1)
    elif m.&#91;x,y&#93; <> 0 then
        solve m (x+1) y 
    else 
        // 縦/横/boxをチェック
        let mutable v = [|0..9|]
        for i in 0..8 do v.[m.[i,y]] <- 0
        for i in 0..8 do v.&#91;m.&#91;x,i&#93;&#93; <- 0
        let x0 = x/3*3
        let y0 = y/3*3
        for i in 0..2 do
            for j in 0..2 do
                v.&#91;m.&#91;x0+i,y0+j&#93;&#93; <- 0

        let m' = Array2D.copy m
        for i in 0..9 do
            if v.&#91;i&#93; <> 0 then
                m'.[x,y] <- v.&#91;i&#93;
                solve m' (x+1) y

let make_pazzle (A0:int&#91;,&#93;) =
    let A = Array2D.copy A0 
    
    // (x,y)をランダムに決める
    let slst = 
        shaffle &#91;for y in 0..size-1 do
                    for x in 0..size-1 -> (x,y) ]
    // 一つずつ取り出して、解法が2以上になった時にやめる
    for i in 0..size2-1 do
        printfn "%d" i
        printfn "%A" A'
        let A' = Array2D.copy A
        let (x,y) = slst.[i]

        A.[x,y] <- 0
        scnt <- 0
        solve A 0 0 
        if scnt > 1 then
            printfn "%A" A'
            raise (new Exception("ok"))

make_pazzle A'

問題の手順も簡単で、ランダムに1個空白(0にする)して、それをいちいち解いていきます。このとき、解法が2つ以上あれば、そこでストップしているだけです。これも非常に(コンピュータにとって)手間がかかる方法なのですが、なんか他に思いつかなかったので、これで。
空白にするコマをランダムに決めてしまうので、いわゆる数独の難しさを考慮していません。なので、これも低い最適化の山に登ってしまうと、途端につまらない≒空白の数の少ない問題を作成してしまいます。このあたりは、一定量の空白になるまで問題作成を繰り返せばいいんでしょうが、それだといつまでやっても終わらないって感じになりそうです。これも適当な足切りが必要でしょうね。

同じものをC#で書いたらどうなるんだろう?という疑問もありますが、このぐらいだと多分似た感じで書けそうです。
F#で書いてみてわかったんですが、配列を

F(x+1) = F(x) + a

の問題で書き直すとうまくいきます…つーか、逆な話で、この形の数式が出てくる場合は F# の再帰関数を使うと便利なわけです。実はこの形って、フィードバックそのもなので、F(1)の結果がF(2)に影響して、さらにF(2)の結果がF(3)に影響して、最終的にF(n)に影響する≒結果が出る、というパターンですね。F(F(F(F(…)))) な感じ。これを再帰の終端が、N から始まって 0 にして終わるのが F# の再帰関数のコツ(F#に限らないけど)、通常の for ループの場合は、0 から始まって max で終わるけど、再帰関数の場合は max を渡してやって減算していって 0 の時に終了、ってやるとうまくいきます…ってのは何処かにあるだろうか。

たとえば、フィボナッチ数列は

let rec fibo n =
    match n with
    | 0 -> 1
    | 1 -> 1
    | x -> fibo(x-1) + fibo(x-2) 

let fibonacci n =
    for i in 0..n do
        let ans = fibo i
        printf "%d," ans

fibonacci 20

とできるわけで、計算量はさておき、数式のシンプルさをそのまま表せるのが便利ですよね。

カテゴリー: F# | [F#] 数独をF#で作る はコメントを受け付けていません

[F#] 数独をF#で解く

ふと、数独(sudoku)をF#で解く…で検索すると、

Sudoku solver in F#
http://www.ffconsultancy.com/dotnet/fsharp/sudoku/

があって、コードがええとよくわからないので、自作しました。
最初は素朴な方法を使っていて、左上から順番に数を入れて計算して、数を入れて計算してという方法をやっていたのですがいっこうに終わらないで、ちょっと考え直し。元ソースをよく見ると、左上からチェックして入れた後は、右に進むという方法をとっているので、ああ、これで十分かということで。

アルゴリズムとして、

  1. 左上(0,0)から始める。
  2. 0以外だったら、右へ進む。
  3. 1 右端に達したら次の行のはじめへ
  4. 0だったら、
  5. 横一列を調べる
  6. 縦一列を調べる
  7. 3×3のボックスを調べる
  8. 数が埋まれば、埋めて右に進んで再帰

な感じで進めればOK。そういえば、行列計算をちまちま自作していたときに思ったのですが、このあたり「アルゴリズム」≒計算機が得意とする手順は、がぜん生きていますね。普段、リスト構造とかツリー構造とかオブジェクトの構造とか、既存の.NETライブラリで賄えるものが多かったのですが、行列計算、線形代数の分野になると、高速化とか誤差も含めて数学的な解法とは違った古式ゆかしき「アルゴリズム」が生きている分野なのだな、と再確認しています。

さて、このアルゴリズムを直に実装したのが以下です。

let rec solve (m:int[,]) x y = 
    printfn &quot;%d %d&quot; x y
    if x = 9 && y = 8 then
        // 完成
        printfn &quot;%A&quot; m
        exit 0
    elif x = 9 then
        solve m 0 (y+1)
    elif m.[x,y] <> 0 then
        solve m (x+1) y 
    else 
        // 縦/横/boxをチェック
        let mutable v = [|0..9|]
        for i in 0..8 do v.[m.[i,y]] <- 0
        for i in 0..8 do v.[m.[x,i]] <- 0
        let x0 = x/3*3
        let y0 = y/3*3
        for i in 0..2 do
            for j in 0..2 do
                v.[m.[x0+i,y0+j]] <- 0

        let m' = Array2D.copy m
        for i in 0..9 do
            if v.[i] <> 0 then
                m'.[x,y] <- v.[i]
                solve m' (x+1) y

for ループの部分が「えー」とか、match を使ったほうがいいよとか、fold の使い方を知らないとか色々あるわけですが、素直に書いたのでこんな感じ。
ただし、この再帰関数 solve は元に帰ってこなくて、完成したとき(9,8 のとき)には、無理矢理 exit してプログラムを止めています。これが再帰関数を戻すと再び別の解を探し出すので無駄なんですよね。元ネタをみると、例外を発生させているので、それでもいいのですが。末尾再帰をしているわけではないので、スタックを食うのは仕方がなく(バックトラック法だし)、ただし、ツリー構造を探索していく中で各ノードはパラレルに動かせるので探索自体は深くなりそうなときは適当にスレッド化することも可能なのかなと。このあたりは、巨大な行列計算と似た感じになると思っています。

まあ、9×9の数独ならば1秒も経たないで解けてしまうのでスレッド化する意味はありません。が、10倍ぐらいの、100×100(数字は1から100まで)の数独ならばどうなんだろうというのがあります。ウィキペディアを見ると、

Mathematics of Sudoku – Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/Mathematics_of_Sudoku

ボックス(bands)が5×5の場合の計算はあるのですが、それ以上はどうなるかわからんという状態みたいです。まあ、パズルとして人間が解ける限界を超えているし、パズルとしての面白味もないので解く意味はないのですが、コンピュータに解かせるときの最適化具合を見るのにはいいかも、と。

数独を解かせるためには、配列の配列を渡してやって、Array2D に直します。

// Input puzzle
let example = [|[|0;0;0;0;6;0;4;0;0|];
                [|0;5;0;0;0;3;6;0;0|];
                [|1;0;0;0;0;5;0;0;0|];
                [|0;4;1;0;0;0;0;0;0|];
                [|0;9;0;0;0;0;0;2;0|];
                [|5;0;2;0;0;0;3;4;0|];
                [|3;0;0;7;0;0;0;0;0|];
                [|0;0;6;5;0;0;0;9;0|];
                [|0;0;4;0;1;0;0;0;0|]|]

let pazzle = Array2D.init 9 9 (fun i j -> example.[i].[j])

solve pazzle 0 0 

答えはこんな感じ

[[2; 3; 7; 1; 6; 8; 4; 5; 9]
 [4; 5; 8; 9; 7; 3; 6; 1; 2]
 [1; 6; 9; 2; 4; 5; 7; 8; 3]
 [6; 4; 1; 3; 5; 2; 9; 7; 8]
 [7; 9; 3; 4; 8; 1; 5; 2; 6]
 [5; 8; 2; 6; 9; 7; 3; 4; 1]
 [3; 1; 5; 7; 2; 9; 8; 6; 4]
 [8; 2; 6; 5; 3; 4; 1; 9; 7]
 [9; 7; 4; 8; 1; 6; 2; 3; 5]]

実は、元ネタの数独は複数回答あって、問題としてよろしくないんですよね。
それはさておき、検算をするコードが以下

let ans = 
    [[2; 3; 7; 1; 6; 8; 4; 5; 9]
     [4; 5; 8; 9; 7; 3; 6; 1; 2]
     [1; 6; 9; 2; 4; 5; 7; 8; 3]
     [6; 4; 1; 3; 5; 2; 9; 7; 8]
     [7; 9; 3; 4; 8; 1; 5; 2; 6]
     [5; 8; 2; 6; 9; 7; 3; 4; 1]
     [3; 1; 5; 7; 2; 9; 8; 6; 4]
     [8; 2; 6; 5; 3; 4; 1; 9; 7]
     [9; 7; 4; 8; 1; 6; 2; 3; 5]]

let A = Array2D.init 9 9 (fun i j -> ans.[i].[j])

let check (m:int[,]) =
    
    for y in 0..8 do
        let mutable v = [|0..9|]
        for i in 0..8 do
            v.[m.[i,y]] <- 0
        if Array.sum v <> 0 then
            printfn &quot;error y=%d&quot; y

    for x in 0..8 do
        let mutable v = [|0..9|]
        for i in 0..8 do
            v.[m.[x,i]] <- 0
        if Array.sum v <> 0 then
            printfn &quot;error x=%d&quot; x

    for x in 0..3..8 do
        for y in 0..3..8 do
            let mutable v = [|0..9|]
            for i in 0..2 do
                for j in 0..2 do
                    v.[m.[x+i,y+j]] <- 0
            if Array.sum v <> 0 then
                printfn &quot;error x=%d y=%d&quot; x y
            
    printfn &quot;OK&quot;

# ああ、そうか。MSTest を使ってもいいんだっけ。どっかにサンプルページがあったはずなので後ほど。

で、bounds が 10×10の数独(全体が100×100)は、どうなるのかなと考えてみたものの…そんな数独の問題はどこにもないわけで、ということは自分で問題を作らないとダメなのですよ。どうせならば、解がひとつしかないパターンの自動作成をしたいわけで、ここは思案のしどころ。ざっと、問題作成の手順を書き下すと、

  1. 矛盾なくNxNを敷き詰める。
  2. ランダムに1個空白にする。
  3. 解が1つしかないことを確認する。再び2へ

な感じかな。3の部分を最適化したいところですが、基本は先の解法コードを拡張するだけなので、後は計算時間の問題かな。

カテゴリー: F# | [F#] 数独をF#で解く はコメントを受け付けていません

[F#] F#らしくLU分解を書き直してみる

LU分解を F# らしく書き直してみます。
せっかくの F# なのに for ループを使っているのがもったいないし(?)、数式のΣから外れています。ここを sum 関数を使って書き直したいのですが… matrix に対しての Seq.sum とかは要素全体に掛け算してしまうので無理(だと思う)なので、

C#er のためのやさしい再帰入門: いげ太のブログ
http://igeta.cocolog-nifty.com/blog/2011/02/tailcall.html#more

を参考にして、再帰関数化しています。

/// LU分解
let LU2 ( A : matrix ) =
let n = A.NumCols
let L = Matrix.identity n
let U = Matrix.zero n n

// sum(k=0,n) L(i,k)*U(k,j) の計算を関数化
// 余計わかりづらいか?
let sum i j k =
let rec f i j k acc =
if k < 0 then 0.0 elif k > 0 then f i j (k-1) (acc + L.[i,k]*U.[k,j])
else L.[i,k]*U.[k,j]
f i j k 0.0

for j in 0..n-1 do
// Uを計算
for i in 0..j do
U.[i,j] <- A.[i,j] - sum i j (i-1) // Lを計算 for i in j+1..n-1 do L.[i,j] <- (A.[i,j] - sum i j (j-1))/U.[j,j] // 結果を返す (L,U) [/code]

LとUを計算するところで、ΣL.[i,k]*U.[k.j] が同じなので、括りだしてみたのですが、余計わかりづらいかも。最後の for ループに適用するとよいのかもしれません。まあ、この程度の簡単なものならば再帰を使うとかえってややこしいという感じですかね。

ならば、for の二重ループの中で、sum を定義して、ちょっと簡単にしてみたのが次のコードです。

/// LU分解
let LU3 ( A : matrix ) =
let n = A.NumCols
let L = Matrix.identity n
let U = Matrix.zero n n

// sum(k=0,n) L(i,k)*U(k,j) の計算を関数化
// 普通にforループで合計を計算する

for j in 0..n-1 do
// Uを計算
for i in 0..n-1 do
let sum max =
let mutable s = 0.0
for k in 0..max do
s <- s + L.[i,k]*U.[k,j] s if i <= j then // Uを計算 U.[i,j] <- A.[i,j] - sum (i-1) else // Lを計算 L.[i,j] <- (A.[i,j] - sum (j-1))/U.[j,j] // 結果を返す (L,U) [/code]

ループの中で関数が定義できるから、便利ってのがありますね。ループ変数のi,jもそのまま使えるので、sum 関数の引数が少なくて済みます。

お次は、このLU分解を使って、二次元トラスを具体的に解いてみます。

カテゴリー: F# | [F#] F#らしくLU分解を書き直してみる はコメントを受け付けていません

[F#] LU分解を作る

旧タイプなので、連立一次方程式を解くときに逆行列を使って解けばOK…と頭から思い込んでいたのですが、Gaussの消去法を使うと逆行列は出てこなくて「?」となるわけです。上三角化を使うのだから、LU分解でいいんですよね。

LU分解で行列式と逆行列の計算 その3: メモブログ
http://sssiii.seesaa.net/article/379560261.html

のところから、

連立1次方程式 I
http://nalab.mind.meiji.ac.jp/~mk/labo/text/linear-eq-1.pdf

のPDFを読んで、なるほど、LU分解で十分ですね。ってことで自前で実装してみます。

4 LU分解
http://akita-nct.jp/yamamoto/lecture/2004/5E/linear_equations/text/html/node4.html#eq:LU____________

を参考にして式を流用します。最初、手順がうまくわからなくて、自分で 4×4 の行列を作って手計算をした後に、数式に直して、元の式と一致することを確認。なるほど、行単位じゃなくて「列単位」で計算するので、計算する順番は、β11,α21,α31,α41 で計算した後に、β12,β22,α32,α42 と計算していくんですね。もう一度読み直したらそう書いてあるし…

出来上がったコードがこんな感じ。

/// LU分解
let LU ( A : matrix ) =
    let n = A.NumCols
    let L = Matrix.identity n
    let U = Matrix.zero n n
    
    for j in 0..n-1 do
        // Uを計算
        for i in 0..j do
            U.[i,j] <- A.&#91;i,j&#93;
            for k in 0..i-1 do
                U.&#91;i,j&#93; <- U.&#91;i,j&#93; - L.&#91;i,k&#93;*U.&#91;k,j&#93;
        // Lを計算
        for i in j+1..n-1 do
            L.&#91;i,j&#93; <- A.&#91;i,j&#93;
            for k in 0..j-1 do
                L.&#91;i,j&#93; <- L.&#91;i,j&#93; - L.&#91;i,k&#93;*U.&#91;k,j&#93;
            L.&#91;i,j&#93; <- L.&#91;i,j&#93; / U.&#91;j,j&#93;
    // 結果を返す
    (L,U)
&#91;/code&#93;
<p>
次のように行列を設定しておくと
</p>
[code lang="csharp"]
let A = matrix [[1.0;2.0;1.0];
                [2.0;1.0;0.0];
                [1.0;1.0;2.0]]

LU A

こんな感じで、L と U にして返してくれます。

val it : matrix * matrix =
  (matrix [[1.0; 0.0; 0.0]
           [2.0; 1.0; 0.0]
           [1.0; 0.3333333333; 1.0]], matrix [[1.0; 2.0; 1.0]
                                              [0.0; -3.0; -2.0]
                                              [0.0; 0.0; 1.666666667]])

先のページにも書いてあるのですが、Ujj で割ってしまうので、ピボット選択は必須…なのか?(ちょっとよくわからず)。浮動小数点だったら大丈夫な気もするのですが、これはあとで確認してみます。まあ、割り算のところにチェックを入れるのは数値計算の基本なので、なるべく割り算で誤差が出ないようにする方向でよいかと。

ちなみに F# MathProvider を使うと

let (p,L,U) = MathProvider.LinearAlgebra.lu A

として、次なる結果を得られます。

val p : (int -> int)
val U : matrix = matrix [[2.0; 1.0; 0.0]
                         [0.0; 1.5; 1.0]
                         [0.0; 0.0; 1.666666667]]
val L : matrix = matrix [[1.0; 0.0; 0.0]
                         [0.5; 1.0; 0.0]
                         [0.5; 0.3333333333; 1.0]]

MathProvider の LU 分解のコードはもっとシンプルで、こんな感じになっています。うまく交互に計算すると L のほうはほとんど計算せずに済みみたいです。行列数が多くなると swapR のパフォーマンスが問題になりますが、有限要素法以外だったら大丈夫かと。

for i=0 to nA-2 do
let mutable maxi = i // Find the biggest pivot element.
for k=i+1 to nA-1 do
if abs(U.[maxi,i]) < abs(U.[k,i]) then maxi <- k done //let maxi = maxIndex (i+1) (nA-1) (fun k -> abs(U.[k,i]))

if maxi <> i then
swapR U i maxi // Swap rows if needed.
swapR L i maxi // REVIEW can be made more performant.
let t = P.[i]
P.[i] <- P.[maxi] P.[maxi] <- t for j=i+1 to nA-1 do L.[j,i] <- U.[j,i] / U.[i,i] for k=i+1 to nA-1 do U.[j,k] <- U.[j,k] - L.[j,i] * U.[i,k] done U.[j,i] <- 0.0 done done [/code]

sum のところは、もっと F# らしく書き換えられないかな、と思案中です。コードが短くなるしΣを使ったほうが、数式を同じになるので。

カテゴリー: F# | [F#] LU分解を作る はコメントを受け付けていません

[F#] 連立一次方程式を解く

逆行列、行列式とできたので、連立一次方程式を解かせる。有限要素法のいわゆる「solve」というやつで、解析の手順として、

  • pre : 構造を設定、要素に分割
  • solve : 連立一次方程式を解く(それだけじゃない?)
  • post : ひずみの結果を表示する

という手順になっている。
Gauss の消去法を使って直接的に解くわけだが、これって、そのまんま LU 分解なわけで。なので、solve 自体は反復法なども含めた意味で使わないとダメっぽい。

let solve (A' : matrix) (F': vector) = 
    let A = A'.Copy()
    let F = F'.Copy()
    let n = A.NumCols-1
    // 前進消去
    for i in 0..n-1 do
        for j in i+1..n do
            let p = A.[j,i]/A.[i,i]
            F.[j] <- F.&#91;j&#93; - p * F.&#91;i&#93;
            for k in 0..n do
                A.&#91;j,k&#93; <- A.&#91;j,k&#93; - p * A.&#91;i,k&#93;
    // 後退代入
    F.&#91;n&#93; <- F.&#91;n&#93;/A.&#91;n,n&#93;
    for i in n-1..-1..0 do
        for j in i+1..n do
            F.&#91;i&#93; <- F.&#91;i&#93; - A.&#91;i,j&#93;*F.&#91;j&#93;
        F.&#91;i&#93; <- F.&#91;i&#93;/A.&#91;i,i&#93;
    F
&#91;/code&#93;
<p>
「有限要素法概説」にあった前進消去「上三角化」したあとに後退代入でひとつずつ計算する。
</p>
[code lang="csharp"]
// 2x -y = 2
// -x +2y= 5 を解く

let M = matrix[[2.;-1.];
               [-1.;2.]]
let F = vector[2.0;5.0]

solve M F 

こんな風にいれておいて、実行すると

val it : Vector<float> = vector [|3.0; 4.0|]

こんな風に結果が出る。

実は、F# MathProvider にも同じものがあって、

let M = matrix[[2.;-1.];
               [-1.;2.]]
let F = vector[2.0;5.0]

let sol = MathProvider.LinearAlgebra.solve M F

として解くと同じ事ができる。

有限要素法の場合、ひとつの節点の自由度は6になるので、節点の数の6倍の計算量になる。さらに、要素が持つ節点(頂点)は三角形二次要素場合は6点になるので、要素数の36倍の計算量なる。が、計算量が多くなるといっても、多くなっても2桁増えるぐらいだから大したことはない?ことはないか。前進消去で、n^3/2 ぐらいのオーダーになるので、n が 100倍になると、100万倍ぐらいの計算量が増える。となると、この素直な方法で解いてしまうと、メッシュ数を2倍細かくすると約8倍の計算量が必要になる。確かに、高速化が必要な分野であるかも。
このあたりは後で実測して試してみよう。

カテゴリー: F# | 1件のコメント

[F#] 行列式を計算する

F# MathProvider だと、あっさりと MathProvider.LinearAlgebra.inv なる関数があるのだが、自作してみる。

let det ( A' : matrix ) =
    let A = A'.Copy()
    let n = A.NumCols-1
    let B = Matrix.identity A.NumCols
    let mutable det = 1.0

    for k in 0..n do
        // akkを1にする
        let p = A.[k,k]
        det <- det * p
        for j in 0..n do
            A.&#91;k,j&#93; <- A.&#91;k,j&#93; / p
            B.&#91;k,j&#93; <- B.&#91;k,j&#93; / p
        // k行以外から引く
        for i in 0..n do
            if i <> k then
                let d = A.[i,k]
                for j in 0..n do
                    A.[i,j] <- A.&#91;i,j&#93; - A.&#91;k,j&#93; * d
                    B.&#91;i,j&#93; <- B.&#91;i,j&#93; - B.&#91;k,j&#93; * d
    det
&#91;/code&#93;
<p>
逆行列の計算
<a href="http://www.asahi-net.or.jp/~uc3k-ymd/Lesson/Section03/invmat.html">http://www.asahi-net.or.jp/~uc3k-ymd/Lesson/Section03/invmat.html</a>
</p>
<p>
の中にある Fortran のコードから擬似写しなんだけど、「det <- det * p」なところで、行列式を保存。他はいらないはずなんだが、うまく括りだせないのでそのままです。
数学的には det A = 0 の場合は、逆行列が計算できないので連立一次方程式が解けないことなるのだが、有限要素法の場合には拘束条件を間違えない限り、逆行列は存在するので問題なし(なはず)。この方式だと det の計算自体に逆行列の計算が含まれてしまっているので本末転倒だし、計算時間もかかってしまう。なので、実際には 0 除算にならなければ逆行列の計算を続ける、という方式でよいのかも。
</p>
[code lang="csharp"]
let A = matrix [[3.0; 1.0;]; 
                [2.0; 5.0;]]

det A

な感じで計算ができて、

val it : float = 13.0

の結果が得られる。

カテゴリー: F# | [F#] 行列式を計算する はコメントを受け付けていません

[F#] 逆行列を計算する

F#で数値・線形代数計算をするためのライブラリ紹介(F# PowerPack, F# MathProvider)
http://d.hatena.ne.jp/teramonagi/20111215/1323874810#20111215fn6

を見ると、F# PowerPack と F# MathProvider を使うと、逆行列が簡単に解けます。

#r "FSharp.PowerPack.dll";
#r @"D:toolsMathProviderNet 4.5MathProvider.dll";

let A = matrix [[3.0; 1.0;];
                [2.0; 5.0;]]

let det = MathProvider.LinearAlgebra.det A
let inv = MathProvider.LinearAlgebra.inv A

こんな風にすると、

val A : matrix = matrix [[3.0; 1.0]
                         [2.0; 5.0]]
val det : float = 13.0
val inv : matrix = matrix [[0.3846153846; -0.07692307692]
                           [-0.1538461538; 0.2307692308]]

と出ます。めでたしめでたし…なのは、後から知ったわけで、実は F# MathProvider は必ず Blas.dll と lapack.dll が必要なのかと思って躊躇してたんですよね。できることならば、F# だけで作りたい、というか、ストアアプリとか自前で高速化(有限要素法で1万節点とか)を考えると、中身を理解しておきたい、という願望があったので…車輪の再発明をしてしまいました。

let inv ( A' : matrix ) =
    let A = A'.Copy()
    let n = A.NumCols-1
    let B = Matrix.identity A.NumCols
    for k in 0..n do
        // akkを1にする
        let p = A.[k,k]
        for j in 0..n do
            A.[k,j] <- A.[k,j] / p
            B.[k,j] <- B.[k,j] / p
        // k行以外から引く
        for i in 0..n do
            if i <> k then
                let d = A.[i,k]
                for j in 0..n do
                    A.[i,j] <- A.[i,j] - A.[k,j] * d
                    B.[i,j] <- B.[i,j] - B.[k,j] * d
    B

逆行列の計算
http://www.asahi-net.or.jp/~uc3k-ymd/Lesson/Section03/invmat.html

を参考にしてべたべたな手計算の方法をF#に直しています。結果が0になるところも計算してしまって無駄が多いので、高速化のし甲斐がある…というか、Fortran とか C言語とかのコードもあちこちにあるので、それらをコンバートしたほうが良いかも。
ただし、有限要素法で使う逆行列の場合には、対称性をうまく利用したり計算量を減らすことができるので、一般的な逆行列よりも手を加えたほうが実用的かもしれません。が、ADVENTURE のように大規模な構造(1億節点など)とは違って、節点数のオーダーが違えば、最近のPCならばさして高速化せずとも結構なところまでいけるのでは?と考えています。これは先行き試してみたいところです。

実はこのF#のコード、半日ほどかかったんですよね。なかなか数値が合わなくて苦労したのは、F#の文法に馴れていなかったためもあるのですが、既存の Fortran コードとは別の形で実装して手計算そのままを移したためです。まあ、勉強になったからいいか。正月だし。

これが「できたーっ!!!」後に気付いたのが、F# MathProvider の実装です。Blas.dll を使わない場合には、自前の F# コードを使うようにできているんですね。なるほど。
以下が抜粋、LU分解と上三角を使っています。

/// Given A[n,n] find it's inverse.
/// This call may fail.
let Inverse a = 
  if HaveService() then LinearAlgebraService.inverse a
                   else LinearAlgebraManaged.Inverse a

let Inverse A =
    let (n,m) = matrixDims A
    if n <> m then invalid_arg &quot;Matrix must be square when computing its inverse.&quot; 
    let P,L,U = LU A
    (SolveTriangularLinearSystems U (SolveTriangularLinearSystems L ((Matrix.identity n).PermuteRows P) true) false)

    let LU A =
        let (nA,mA) = matrixDims A
        if nA<>mA then invalid_arg &quot;lu: not square&quot;;
        let L = Matrix.zero nA nA
        let U = Matrix.copy A
        let P = [| 0 .. nA-1 |]
        let abs (x:float) = System.Math.Abs x
        let swapR X i j =                           //  REVIEW should we make this a general method?
            let (nX,mX) = matrixDims X
            let t = X.[i .. i,0 .. ]
            for k=0 to mX-1 do
                X.[i,k] <- X.[j,k]
                X.[j,k] <- t.[0,k]
            done
            
        for i=0 to nA-2 do
            let mutable maxi = i        //  Find the biggest pivot element.
            for k=i+1 to nA-1 do
                if abs(U.[maxi,i]) < abs(U.[k,i]) then maxi <- k
            done
            //let maxi = maxIndex (i+1) (nA-1) (fun k -> abs(U.[k,i]))
            
            if maxi <> i then
                swapR U i maxi     // Swap rows if needed.
                swapR L i maxi     // REVIEW can be made more performant.
                let t = P.[i]
                P.[i] <- P.[maxi]
                P.[maxi] <- t
            
            for j=i+1 to nA-1 do
                L.[j,i] <- U.[j,i] / U.[i,i]
                for k=i+1 to nA-1 do
                    U.[j,k] <- U.[j,k] - L.[j,i] * U.[i,k]
                done
                U.[j,i] <- 0.0
            done
        done
        (((*P.Length,*)Permutation.ofArray P),L + Matrix.identity nA,U)

    let SolveTriangularLinearSystems K B isLower =
        if isLower then
            let (nK,mK) = matrixDims K
            let (nB,mB) = matrixDims B
            if nK<>mK || nB<> nK then invalid_arg &quot;Cannot do backward substitution on non-square matrices.&quot;
            let X = Matrix.zero nK mK
            for i=0 to nK-1 do
                for k=0 to mB-1 do
                    let s = ref B.[i,k]
                    for j=0 to i-1 do
                        s := !s - K.[i,j] * X.[j,k]
                    done
                    X.[i,k] <- !s / K.[i,i]
                done
            done
            X
        else
            let (nK,mK) = matrixDims K
            let (nB,mB) = matrixDims B
            if nK<>mK || nB<> nK then invalid_arg &quot;Cannot do backward substitution on non-square matrices.&quot;
            let X = Matrix.zero nK mK
            for i=0 to nK-1 do
                for k=0 to mB-1 do
                    let s = ref B.[nK-i-1,k]
                    for j=0 to i-1 do
                        s := !s - K.[nK-i-1,nK-j-1] * X.[nK-j-1,k]
                    done
                    X.[nK-i-1,k] <- !s / K.[nK-i-1,nK-i-1]
                done
            done
            X

まあ、数式は理解して使ったほうがいいので、しばらくは自前で実装ということで。

カテゴリー: F# | [F#] 逆行列を計算する はコメントを受け付けていません

年頭なので初心に帰って

去年と一昨年は何気に正月に仕事をしていたのですが、今年は年末に原稿の殴り書きをした後にプログラムを殴り書きして正月休みを作りました。妻の実家にノートパソコンを持って行ってプログラムの下書きなどをすることが多いのですが、今回はノートパソコンなし。その代りに「実践 有限要素法シミュレーション」の本を持って行って、通読…はできなくて基礎編の30ページちょいぐらいを解いていました。F#とFortranの話…のその前に – Moonmile Solutions Blog の時に書いた構造計算にF#が使えるのか?って話の続きですね。行列計算をやって逆行列を作って、連立一次方程式を解いてというのを手計算でやって大学の頃を思い出して、うーっとなっておりました。幸いにして、正月はノートパソコンを持っていかなかったので、F#で組むこともせず、Fortranのコードを見ることもせず、ストアアプリのライブラリを作ることはなかったのですが、久しぶりに鉛筆で筆算をしていると、理解は深まるんではないかな~などと思うわけですが、どうなんでしょう?ただ、コードをコンバートしているだけよりは理解が深まったのではないかなと思ったり…いやいや、車輪の再発明というパターンに陥るかもしれませんが。

さて、色々やりたいことはあるけれど、地道にやることと仕事でやることと自分の技量的にやれないことがあって、もんもんと考えることも多いのですが、ざっと正月にメモしていたものを書き下し。

  • 画像解析をしてパズルを解く。
    以前やっていた、OpenCVの再開です。テンプレートマッチングは自前で書くことができたので、汎用性を殺せば比較的楽に作れるかと。
  • マインドストームでロボットアームを作る。
    せっかく EV3 を買ったので(というかこれが目的なので)ストアアプリから制御します。
  • 有限要素法を F# で解く。
    材料工学は素人なのですが、原子力関係なので行列式にはなじみがあるので(得意とは言えないが)、似た感じでけるかなという目論見です。仕事で関わっているところもこれなので、専門用語の理解も含めて。
  • ストアアプリ用のパズルコンポーネントを作る。
    親馬鹿シリーズ(ということにして)、手軽にパズルゲームが作れるまでのコンポーネントを作れればよいかなと。ロジックを F#、画面を C#、アニメーションを XAML/storybaord、画像関係を C++/CX で組みあわせれば、そこそこのものが作れるかなという目論見

あとは、めでたく Microsoft MVP の Visual C# を再受賞させていただいたので、手元の MSDN を存分に使って、

  • Xamarinの話
  • CakePHPとWPFの仕事
  • FotranとC++の仕事

を引き続き。

引っ越しを機会にため込んでいた文庫本をどんどんと廃棄しつつあります。一度読んだ本もれば一度も読んだことがない本(=積読)もあるわけで、技術書のように古くなった本を捨てるパターンもあれば、時々めくってみる古い本もあります。まだ段ボール15箱ぐらい残っているのですが、これも整理して本棚に詰めれるだけにして、本は図書館で借りたりして(近くに図書館のある場所に引っ越しました)家で仕事をして、作りたいものが作れるようになる感じが自分にとって向いているのかな、再確認したり、そう無理に思い込んでみたり。あ、そうそう、TOC用のタスクスケジューラが欲しいところですね。TFSの作業項目と連携させるやつ。この手のやつもぼちぼちと作って行きましょう。

ひとまず、やりたい目標はいっぱい掲げておいて、自分が実現できるもの(興味が続くものというのも含めて)に手を付けていこう。「楽観的に構想を練り、悲観的に計画し、楽観的に実行する」というパターンで。

カテゴリー: 雑談 | 年頭なので初心に帰って はコメントを受け付けていません

F#とFortranの話…のその前に

F# Advent Calendar 2013 – connpass
http://connpass.com/event/3935/

の22日目…なのですが、既に23日深夜という…ええ、GMTで計算しても22日目に入らないですね。本来ならば、Fortranの関数や構造体をF#で使おう、というネタで書こうと思ったですが、もろもろの件が忙しくてkindleで「Programing F# 3.0」を眺めるだけで精一杯で先に進みませんでした。いやいや、言い訳よりもまずは手を動かさなくちゃね、ってことで、構想だけつらつらと1時間ほど書き連ねます。

■F#をFortranモジュールと連携する

構造解析をFortranで作っていると画面作成をどうするのか?で悩みますが、手元のものはC++で作ってあります。Fortranの関数や構造体をC言語レベルに書き直して、C++で読み込ませるという手順ですね。実は、Fortranにも.NETと連結できるソフトウェアもあるのですが、手元の Intel Fortran はそのままでは .NET とは繋がりません。これをやるならば、Fortran –> C言語 –> C++/CLI とするか、直接 C言語 –> C# ってスタイルになります。F# は関数言語といえ、.NET言語の仲間ですから、F#からC言語のライブラリを呼び出すことも可能(だと思う)でしょう。C言語のAPIレベルをひとつひとつ再定義するのは結構な手間ですし、ネイティブのデータとのコンバートに時間がかかってしまっていては、せっかくFortranを使っている意味がありません。

そこで、以前、画像解析の時に使っていた、パターンで、Fortran –> C++/CLI –> F# というコンバートのパターンを考えます。本来のF#の利用しどころはさておき、実際のコアな計算は、Fortranの中に入っているので、F#からアクセスしたいデータはもう少し「データを簡便に利用したい」というところです。

GUIのようなグラフィックの部分は、DirectXとWPFを使っています。旧来のDirectXなので、これ専用にライブラリ化されてしまってまったく手が出せない感じになっています。また、WPFのほうは、FortranからC言語下へのデータコンバートをしたのち、C#を使って描画させます。このあたり、専用グラフィック用のC++と、別途ツールのWPFとが混在していて、えらいことになっているんですが…まあ、先行きはわかりません。なかなか大変です。

現在のソフトウェア構造としては、

  • データを保持しているところのFortoran
  • データを高速に扱うためのFortranコード
  • データをDirectXで3D描画するためのC++
  • GUIを楽に作るためのC#とWPFの画面

というパターンになっています。

■関数を関数として扱うためのF#

さて、このソフトウェア構造の中でF#に何を期待するのか?といえば、「式を式のまま記述したい」という希望があります。一応、30年前ぐらいの関数言語の教科書(関数言語の発祥は、オブジェクト指向と同じころにあります)を通読したのですが、数式を数式のまま扱うという入口から私は入っています…つーか、まだ入り口の前なんですが。

image

たとえば、こんな式があります。私の時代の材料工学は荷重する点に対して正確な計算を行うのですが、最近の構造解析では適当なメッシュを切って膨大な計算で済ませます。適当な荷重を計算した上で、メッシュ(格子点以外に三角点のセルもあります)に対しての荷重を再計算します。

image

メッシュに対する荷重を計算した後は、それぞれの部材の相互の斥力を計算するわけですが、これはFotranのライブラリの中に入っています。かつてはFortranに直していたわけですが、このFortranのデータ構造と数式の書き方は、やっぱりコンピュータに沿った書き方なんですね。いわゆるベクトルを扱う訳ですが、これがforループします。30年ほど前はそれはそれでよかったのですが、そのforループにちょっと疑問があるわけです。

全てのメッシュ(あるは交点)に対して、同じ式を適用するのであれば話は簡単です。

do y=1, YMAX
do x=1, XMAX
  … 式を書く
enddo
enddo

とすればOKです(実は、最近のFortranはベクトルが使えるので、もっと簡単に1行で書けます)。

ですが、メッシュを使う場合、均一に式を適用するのではなくて、必ず「境界条件」というのが入ってきます。メッシュの端っこの部分です。他の部材と接するところや、ボルトで固定されているところとか、素材が異なるところですね。この境界条件あるいは特定条件を指定するために、ループの中にif文を入れます。

do y=1, YMAX
do x=1, XMAX
  if ( 特定の条件の時 ) then …
enddo
enddo

プログラミングをしているときは、これは当たり前で自然な書き方なのですが(いやいや、matchを使うと違うでしょ…って話は後ほど)、数式の場合にはこのif条件をこんな風に書きます。

y = g(x)
  y = 1    := {x|x=1}
  y = f(x) := {x|1<x,x<MAX}
  y = 1    := {x|x=MAX}

(ちょっと正確な書き方を忘れてしまった…工学部なのに orz)

1とMAXの時はy=1にして、1以上のときはf関数を適用する、というよくあるパターンですね。これをif文を使ってちまちま直すのがプログラミングだったわけですが、いやいや、それらの数式は数式のまま扱おうよ、という発想が関数言語の発祥です…と言い切っていいものかわからないのですが、最初の教科書を見るとそんな感じですね。

これをF#で書くとこんな感じになります。

let g x =
  match x with
  | 1 -> 1
  | MAX -> 1
  | _ -> f(x)

1の時が境界条件(特殊条件)で、そのほかの値の時はf関数を適用するのがわかりやすいのです。これをC言語で書くと、

if ( x == 0 ) {
  return 1;
} else if ( x == MAX ) {
  return 1;
} else {
  return f(x);
}

な感じになります。「慣れ」といえば慣れなんですが、特殊条件のときと通常の条件(メインストリーム)のときの区別がわかりやすいかな、という利点があります。

実際コーディングしたときに、F#の実行スピードとFortranの実行スピードはどうなのかも問題なのですが、複雑な数式を適用させたときに、

  • 直した数式が、プログラムのどれにあたるか即判断できるか?
  • 直した数式が、プログラムと同じであるか即判断できるか?

ってところが関数言語の利点かなと思っています。構造の数式とか画像解析の数式を修正したときに、それがプログラムのどの部分にあたるのか?が判断しやす利点は、そのままトライ&エラーがやりやすいという利点でもあります。以前、テンプレートマッチングの式を OpneCV を使って自作していたのですが、C++ で組むよりも、この手の関数言語のほうが楽ではないか?と思っています。相関式とかさらっと書けるプログラマの方はよいのですが、境界条件も含めると結構複雑な感じになってしまうんですよね。このあたり、ゲームアルゴリズムも同じかと思っています。

■ロジックを書くのに最低限必要なF#の文法はなんだろうか?

…ってのが、私の当面の課題で、もろもろのF#の便利な機能はさておき、F# と設計について考えてみた #FsAdventJP – かたちづくり と似た感じで進めたいと思っています。

  • ミニマムなデータ構造(Fortran、C言語互換)
  • データ構造アクセス(C++/CLI経由?)
  • 高速化計算(Fortran)
  • 通常計算(F#)
  • 数式の検証、試行錯誤(F#)
  • GUI(C#)

なところですね、画像解析の場合は、OpenCVを使いたいので、このあたりがC++になります。また、DirectXを使ってC++/CX経由になっても、F#の使いどころはありそうです。

これを踏まえたとき、最低限必要なところといえば、

  • 構造体アクセス(他アセンブリのアクセス、open)
  • パターンマッチング(match?)
  • リストアクセス(Listあるいは配列?)

だけで十分では?と乱暴なことを考えていたりするのですが、このあたりはもうちょっと先に実際にやってみてということで。

# タプルとか便利かな、と思っていくつか試したのですが、Fortranで扱うのが構造体のみだし。そのあたりは、うまいことC++/CLIでwrapしてF#から構造体へ直アクセスしたほうがよいかなと試行
&実験中です。

カテゴリー: F# | 1件のコメント

矢野顕子さとがえるコンサート2013に行ってきました

恒例のさとがえるコンサートに行ってきました。下の子は託児所に預けて、上の子と三人でNHKホールへ。託児所はNHKホール内にあるので、直行直帰という感じです。

今回は20年前に発表された「エレファントホテル」の全13曲プラスアルファということで、2時間強の結構な時間になりました。最近は1時間半ぐらい終了のパターンが多いので、ちょっと小3の娘には長かったかも。

ゲストは奥田民夫。バンドメンバは、テルミンを含む不思議な楽器のメンバでMATOKKU(松本淳一/トリ音/久保智美)です。

▶ “MATOKKU” LIVE~ヤノさん、マトック園であそぶ~ – YouTube
http://www.youtube.com/watch?v=8Iei9UfPVZE

たぶん、普段は現代音楽っぽいのでしょうが、今回はエレファントホテルのアレンジということで、ふつうの音楽してます。いや、矢野顕子の音楽自体が「ふつう」かというと、ふつうからは随分外れていると思うので、それはまた奇妙な組み合わせということです。が、20年前の声の復元性は、今回非常に高かった気がします。あの年齢であの声の高さとテンションを維持しているのが、すごいというか独自だからなのか、その独自性を維持することが重要≒日常なのか、という点で毎度見習わないと、と思ったりします。

夏にさそうあきら著「ミュジコフィリア」を読んだのを思い出して、矢野顕子の出す「は」の音が、「はの音楽なのかな?」と想像したり、フランク・ザッパのイエローシャークを連想したり、と楽しみました。

今年はいろいろあって大変で、実は年末までもいろいろありそうで大変なのですが、それでも「大変」の中に埋もれてしまう人生は詰まらないから、自分で動ける範囲は自分の自由意思で動く。いやいや、もっと生物学的に利己的な遺伝子的に動いているのを前提として、自分が好きなことをやる姿勢を維持しないと、と心新たにという一日。

カテゴリー: 雑談 | 矢野顕子さとがえるコンサート2013に行ってきました はコメントを受け付けていません