The One with ...

思いついたことや作業メモなどを公開しています

Refreshに関するメモ

Mathematica内蔵関数Refreshの引数である,UpdateIntervalは更新を制御できたりできなかったりして,いまいち仕組みが分からない.そこでいろいろと実験してみた.

Manipulate[
 Refresh[Random[], UpdateInterval -> If[go, 0.105, Infinity]],
 {{go, False}, {True, False}}]

このようなコードなら,UpdateIntervalによって更新頻度が制御できる.一方で

x = 1;
Manipulate[
 Refresh[x = x + 0.01; x, UpdateInterval -> If[go, 0.02, Infinity]],
 {{go, False}, {True, False}}]

これはUpdateIntervalInfinityにもかかわらず暴走してしまう

そこでRefreshの中にIfを挟んで代入を制御する.

x = 1;
Manipulate[
 Refresh[
  If[go, x = x + 0.01], UpdateInterval -> If[go, 1, Infinity]],
 {{go, False}, {True, False}}]

これならオンオフだけ制御できる.しかしインターバルの時間は指定通りにならない.

TrackedSymbols -> {} で制御しようとしてもうまくいかない.

結局,即時割り当てをManipulateの中に入れると,Dynamicに更新されるので,その場合はUpdateIntervalで制御できる範疇を超えてしまうらしい.

オブジェクトを再帰的代入すると,refreshしてもうまくいかないらしい

次のような命令なら代入でも問題ないし,UpdateIntervalによる制御もうまくいく

Manipulate[
 Refresh[
  If[go, x = Random[]], UpdateInterval -> If[go, 0.8, Infinity]],
 {{go, False}, {True, False}},
 Button["reset", go = False; x = 100]
 ]

しかし再帰的に代入すると,Dynamicオブジェクトが自分自身を連続的に更新するので UpdateIntervalを指定しても,機能しない (のではないかと推測する)

Manipulate[
 Refresh[
  If[go, x = x + Random[]], UpdateInterval -> If[go, 0.8, Infinity]],
 {{go, False}, {True, False}},
 Button["reset", go = False; x = 100]
 ]

グラフィックオブジェクトの動的更新

例えばつぎのようなDynamicオブジェクトはRefreshによって動的に更新される (ただし止まらない)

Dynamic[Refresh[Histogram[RandomReal[10, 100]], 
  UpdateInterval -> 0.1]]

制御したければManipulate内に入れて,チェックボックスUpdateIntervalの値を指定する方法がある.

Manipulate[
 Refresh[Histogram[RandomReal[10, 1000]], 
  UpdateInterval -> If[x, 0, Infinity]], {{x, False}, {True, False}}]

今まで,ピコピコ系アウトプットはこの方法で作ってきた.

以下のようなアホなコード(めっちゃ計算しているようにみえるが,実は無意味なコード)も簡単に書ける.

Manipulate[Refresh[
  Histogram[
   {RandomVariate[NormalDistribution[0, 1], n],
    RandomVariate[NormalDistribution[a, 1/2], n],
    RandomVariate[NormalDistribution[10, 1], n],
    RandomVariate[LogNormalDistribution[1, 0.3], n],
    RandomVariate[NormalDistribution[8, 0.5], n]}, {.25}, 
   PlotRange -> {{-3, 15}, {0, 100}}],
  UpdateInterval -> If[x, 0, Infinity]],
 {{x, False, "Run MCMC"}, {True, False}},
 {{n, 300}, 1, 500, 1},
 {{a, 3, "\[Mu]"}, 1, 10}]

いろいろ実験して分かったことは,ListPlotみたいな即時実行命令をRefreshするとうまくいかない,ということだ.

冗長なユーザ定義関数を割り当て(実際にはクロージャを含まないダミーでよい),遅延割り当てにすると,ちゃんとRefreshが機能する.

だからvisualizeみたいなユーザ関数を使うとうまくいくわけだ.

以上をふまえて作ったコード

(* 関数の定義 *)
initialdata[n_, m1_, 
   m2_] := {RandomVariate[NormalDistribution[m1, 5], n], 
   RandomVariate[NormalDistribution[m2, 5], n]};

fightonce[group_] := Module[{power1, power2},
   power1 = group[[1]]; power2 = group[[2]];
   If[Length[power1] > 0 && Length[power2] > 0,
    power1 = RandomSample[power1];
    power2 = RandomSample[power2];
    
    If[(* 戦闘力を比較して勝者を残す*)
     RandomChoice[
      {power1[[1]]/(power1[[1]] + power2[[1]]), 
        power2[[1]]/(power1[[1]] + power2[[1]])} -> {True, False}],
     (* True -> 1が勝つ*)
     power2 = Drop[power2, 1],
     power1 = Drop[power1, 1]];(* If end*)
    ];(* If end*)
   {power1, power2}
   ];

(* update関数のほうに停止条件を書いておく. dataがなくなったらNullを返す*)
visualize[data_, hight_] := 
  Histogram[data, {1}, PlotRange -> {{0, 100}, {0, hight}}];
(* histogram を遅延評価させるために,関数化する*)

(*n=50;ylim=25;sample=initialdata[n];*)


Manipulate[Refresh[
  If[x, sample = fightonce[sample]];
  (* If文で実行する動作:UpdateInterval\[Rule]0 のときだけ,update関数を再帰的代入する*)
  visualize[sample, ylim], UpdateInterval -> If[x, 0, Infinity]],
 {{x, False}, {True, False}},
 {{n, 100}, 50, 1000, 1}, {{m1, 40}, 10, 70}, {{m2, 50}, 10, 70},
 Button["reset", x = False; sample = initialdata[n, m1, m2]; 
  ylim = n/5]]

少しだけRefreshの理解が深まった(Dynamicは,まだ謎が多い).