Ruby で簡単な Gray-Scott

qiita.comここの Python 版を Ruby に移植しただけです。計算に Gem 'numo-narray' を使っています。画像化には Gem 'gdk_pixbuf2', 'rmagick' を使いました。できたものはこんな感じ。
20191115013736
 
Ruby コード。Ruby 2.6.0, Linux Mint 19.2 で動作確認。
gray_scott.rb

require 'numo/narray'
require 'gdk_pixbuf2'
require 'rmagick'

include Numo

SPACE_GRID_SIZE = 256
VISUALIZATION_STEP = 8
Dx = 0.01
Du = 2e-5
Dv = 1e-5

def laplacian(ary, base)
  #NArray オブジェクトのコピー
  inflow_from_top    = base.reshape(SPACE_GRID_SIZE, SPACE_GRID_SIZE)
  inflow_from_bottom = base.reshape(SPACE_GRID_SIZE, SPACE_GRID_SIZE)
  inflow_from_left   = base.reshape(SPACE_GRID_SIZE, SPACE_GRID_SIZE)
  inflow_from_right  = base.reshape(SPACE_GRID_SIZE, SPACE_GRID_SIZE)
  outflow            = base.reshape(SPACE_GRID_SIZE, SPACE_GRID_SIZE)
  
  #ラプラシアンの計算
  (SPACE_GRID_SIZE - 1).times do |i|
    inflow_from_top[i + 1, true]  = ary[i, true]
    inflow_from_bottom[i, true]   = ary[i + 1, true]
    inflow_from_left[true, i + 1] = ary[true, i]
    inflow_from_right[true, i]    = ary[true, i + 1]
  end
  outflow = ary * 4
  
  (inflow_from_top + inflow_from_bottom + inflow_from_left +
    inflow_from_right - outflow) / (Dx * Dx)
end

#計算(f, k の値によって反応が変わります)
def calc(u, v, f, k)
  u_base = SFloat.ones(SPACE_GRID_SIZE, SPACE_GRID_SIZE)
  v_base = SFloat.zeros(SPACE_GRID_SIZE, SPACE_GRID_SIZE)
  VISUALIZATION_STEP.times do
    partial_u = laplacian(u, u_base) * Du - u * v * v + (1.0 - u) * f
    partial_v = laplacian(v, v_base) * Dv + u * v * v - (f + k) * v
    u += partial_u
    v += partial_v
  end
  [u, v]
end


#初期設定
Dir.chdir("picture")

SQUARE_SIZE = 20

u = SFloat.ones(SPACE_GRID_SIZE, SPACE_GRID_SIZE)
v = SFloat.zeros(SPACE_GRID_SIZE, SPACE_GRID_SIZE)
square_start = SPACE_GRID_SIZE / 2 - SQUARE_SIZE / 2
square_end   = SPACE_GRID_SIZE / 2 + SQUARE_SIZE / 2
u[square_start..square_end, square_start..square_end] = 0.5
v[square_start..square_end, square_start..square_end] = 0.25

#画像の生成
400.times do |i|
  u, v = calc(u, v, 0.022, 0.051)
  
  for_visualize = UInt8.cast(u * 255)
  data = UInt8.zeros(SPACE_GRID_SIZE, SPACE_GRID_SIZE, 3)
  data[true, true, 0] = for_visualize
  data[true, true, 1] = for_visualize
  data[true, true, 2] = for_visualize
  
  pixbuf = GdkPixbuf::Pixbuf.new(data: data.to_string,
              width: SPACE_GRID_SIZE, height: SPACE_GRID_SIZE)
  pixbuf.save("%04d.png" % i)
end

#gif化
list = Magick::ImageList.new
Dir.glob("*").sort.each do |fn|
  list.concat(Magick::ImageList.new(fn))
end
list.delay = 3
list.write("gifanime.gif")

作業用にカレントディレクトリに 'picture' ディレクトリを作って下さい。このコードの場合だと、400ステップ分の画像を生成します。

なお、ラプラシアンの差分方程式化はここなどが参考になります。

※参考
Gray-Scott というのを作ってみた記録 – ✈
Numo::NArray概要 · ruby-numo/numo-narray Wiki · GitHub
 

f = 0.012, k = 0.05 の場合。
20191115014553
 
Gtk によるリアルタイム・アニメーション版はこちら。
https://gist.github.com/obelisk68/ecd97e79b87f69365a0fb0747ffcedca