Skip to content

Instantly share code, notes, and snippets.

@Lirimy
Created June 12, 2018 07:39
Show Gist options
  • Save Lirimy/127d6a557ba10d549d17c6687ee9c5c2 to your computer and use it in GitHub Desktop.
Save Lirimy/127d6a557ba10d549d17c6687ee9c5c2 to your computer and use it in GitHub Desktop.
Ising Model Simulation using ArrayFire
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Calculating Ising Model with ArrayFire \n",
"MIT License \n",
"Copyright (C) 2018 Lirimy "
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ArrayFire v3.6.0 (CUDA, 64-bit Linux, build 2858662)\n",
"Platform: CUDA Toolkit 9.1, Driver: 396.26\n",
"[0] GeForce GTX 980, 4041 MB, CUDA Compute 5.2\n"
]
}
],
"source": [
"using ArrayFire"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import Colors: Gray\n",
"#import Images: display\n",
"import Plots: gif, mp4, frame"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"include(\"SimpleAnimation.jl\")\n",
"using SimpleAnimation\n",
"#using Plots"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"const MB = UInt64(2^20);"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"initS (generic function with 1 method)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gpu32(a) = AFArray([Float32(a)])\n",
"rnd() = rand(AFArray{Float32}, m, n)\n",
"initS() = AFArray(Float32.(rand([1, -1], m, n)))"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"genP (generic function with 1 method)"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# generate padding matrix of circular edge\n",
"function genP(n)\n",
" P = eye(AFArray{Float32}, n+2)\n",
" P[1, 1] = P[end, end] = 0\n",
" P[1, end-1] = P[end, 2] = 1\n",
" return P\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"pconv"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function pconv!(u, ret, K=kernel, Pl=Pl, Pr=Pr)\n",
" conved = convolve2(u, K, AF_CONV_EXPAND, AF_CONV_SPATIAL)\n",
" ret .= (Pl' * conved * Pr)[2:end-1, 2:end-1]\n",
" nothing\n",
"end\n",
"\n",
"\"\"\"Padding and Convolution\n",
"K: kernel\n",
"P: padding matrix\n",
"size(P) = size(u) + size(K) - 1\n",
"\n",
"P example of free edge:\n",
"\n",
"AFArray: 5×5 Array{Float32,2}:\n",
" 0.0 1.0 0.0 0.0 0.0\n",
" 0.0 1.0 0.0 0.0 0.0\n",
" 0.0 0.0 1.0 0.0 0.0\n",
" 0.0 0.0 0.0 1.0 0.0\n",
" 0.0 0.0 0.0 1.0 0.0\n",
"\"\"\"\n",
"function pconv(u, K=kernel, Pl=Pl, Pr=Pr)\n",
" ret = similar(u)\n",
" pconv!(u, ret, K, Pl, Pr)\n",
" ret\n",
"end\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"prob (generic function with 1 method)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"b = gpu32(-log(1+sqrt(2))) # 2b\n",
"kernel = AFArray(Float32.([0 1 0; 1 0 1; 0 1 0]))\n",
"\n",
"function prob(s::AFArray{Float32, 2})::AFArray{Float32, 2}\n",
" exp(b .* s .* pconv(s))\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"update! (generic function with 2 methods)"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function update!(s::AFArray{Float32, 2}, elim=1/10)\n",
" s .*= 1 - 2 * signbit(rnd() - prob(s) .* signbit(rnd().-gpu32(elim)))\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"#m, n = 1080, 1920\n",
"m, n = 360, 640\n",
"\n",
"Pl, Pr = genP(m), genP(n)\n",
"\n",
"s = initS();"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 7.548005 seconds (573.27 k allocations: 1.753 GiB, 3.72% gc time)\n",
" 4.014858 seconds (971 allocations: 55.891 KiB)\n"
]
},
{
"data": {
"text/html": [
"<img src=\"ising.gif?0.5650487844056284>\" />"
],
"text/plain": [
"SimpleAnimation.AnimatedFile(\"/home/laputan/Desktop/ising.gif\")"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"b = gpu32(-log(1+sqrt(2)))\n",
"\n",
"afgc(1)\n",
"s = initS()\n",
"anim = SimpleAnim()\n",
"\n",
"@time for count in 1:450\n",
" for i in 1:10\n",
" update!(s)\n",
" end\n",
" \n",
" frame(anim, Gray.((Array(sync(s))+1)/2))\n",
" \n",
" # when benchmarking, uncomment these 2 lines and comment out frame\n",
" #sync(s)\n",
" #gc(false)\n",
" \n",
" afgc(3200MB)\n",
"end\n",
"\n",
"@time gif(anim, \"ising.gif\")"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 5.856016 seconds (940 allocations: 51.953 KiB)\n"
]
},
{
"data": {
"text/html": [
"<video controls><source src=\"ising.mp4?0.4310710648755769>\" type=\"video/mp4\"></video>"
],
"text/plain": [
"SimpleAnimation.AnimatedFile(\"/home/laputan/Desktop/ising.mp4\")"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"@time mp4(anim, \"ising.mp4\")"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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",
"text/plain": [
"360×640 Array{Gray{Float32},2}:\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) … Gray{Float32}(1.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) Gray{Float32}(1.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) Gray{Float32}(1.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) Gray{Float32}(1.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) Gray{Float32}(1.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) … Gray{Float32}(1.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) Gray{Float32}(1.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) Gray{Float32}(1.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) Gray{Float32}(1.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) Gray{Float32}(1.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) … Gray{Float32}(1.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) Gray{Float32}(1.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) Gray{Float32}(1.0)\n",
" ⋮ ⋱ \n",
" Gray{Float32}(1.0) Gray{Float32}(0.0) Gray{Float32}(0.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(0.0) Gray{Float32}(0.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) … Gray{Float32}(1.0)\n",
" Gray{Float32}(0.0) Gray{Float32}(0.0) Gray{Float32}(0.0)\n",
" Gray{Float32}(0.0) Gray{Float32}(0.0) Gray{Float32}(1.0)\n",
" Gray{Float32}(0.0) Gray{Float32}(0.0) Gray{Float32}(0.0)\n",
" Gray{Float32}(0.0) Gray{Float32}(0.0) Gray{Float32}(0.0)\n",
" Gray{Float32}(0.0) Gray{Float32}(1.0) … Gray{Float32}(0.0)\n",
" Gray{Float32}(0.0) Gray{Float32}(0.0) Gray{Float32}(0.0)\n",
" Gray{Float32}(0.0) Gray{Float32}(0.0) Gray{Float32}(0.0)\n",
" Gray{Float32}(0.0) Gray{Float32}(0.0) Gray{Float32}(0.0)\n",
" Gray{Float32}(1.0) Gray{Float32}(1.0) Gray{Float32}(1.0)"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Gray.((Array(sync(s))+1)/2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Julia 0.6.2",
"language": "julia",
"name": "julia-0.6"
},
"language_info": {
"file_extension": ".jl",
"mimetype": "application/julia",
"name": "julia",
"version": "0.6.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
# SimpleAnimation
#
# This code is licensed under MIT license.
# Copyright (C) 2018 Lirimy
#
# This code contains copy and modification of Plots.jl.
# Plots.jl: Copyright (c) 2015: Thomas Breloff.
#
# *** Requirements ***
#
# Plots.jl
# Imagemagick.jl ?
# ffmpeg
#
# *** How to use ***
#=
include("SimpleAnimation.jl")
using SimpleAnimation
import Colors: Gray
import Plots: gif, mp4, frame
anim = SimpleAnim()
@time for i in 1:60
img = Gray.(rand(240, 320))
frame(anim, img)
end
mp4(anim, "anim.mp4")
=#
__precompile__(true)
module SimpleAnimation
import ColorTypes: Color
import FileIO: save
using Plots
export
SimpleAnim
struct SimpleAnim
dir::String
frames::Vector{Int}
end
function SimpleAnim()
tmpdir = convert(String, mktempdir())
SimpleAnim(tmpdir, [])
end
struct AnimatedFile
filename::String
end
function Plots.frame(anim::SimpleAnim, img::Array{T, 2}) where T<:Color
i = length(anim.frames) + 1
filename = @sprintf("%06d.bmp", i)
save(joinpath(anim.dir, filename), img)
push!(anim.frames, i)
end
file_extension(fn) = Base.Filesystem.splitext(fn)[2][2:end]
function Plots.mp4(anim::SimpleAnim, fn="out.mp4")
fn = abspath(fn)
run(`ffmpeg -v 0 -framerate 30 -i $(anim.dir)/%06d.bmp -y -vcodec libx264 -pix_fmt yuv420p $fn`)
AnimatedFile(fn)
end
function Plots.gif(anim::SimpleAnim, fn="out.gif")
fn = abspath(fn)
# generate a colorpalette first so ffmpeg does not have to guess it
run(`ffmpeg -v 0 -i $(anim.dir)/%06d.bmp -vf "palettegen=stats_mode=diff" -y "$(anim.dir)/palette.bmp"`)
# then apply the palette to get better results
run(`ffmpeg -v 0 -framerate 30 -loop 0 -i $(anim.dir)/%06d.bmp -i "$(anim.dir)/palette.bmp" -lavfi "paletteuse=dither=sierra2_4a" -y $fn`)
AnimatedFile(fn)
end
function Base.show(io::IO, ::MIME"text/html", afile::AnimatedFile)
ext = file_extension(afile.filename)
write(io, if ext == "gif"
"<img src=\"$(relpath(afile.filename))?$(rand())>\" />"
elseif ext == "mp4"
"<video controls><source src=\"$(relpath(afile.filename))?$(rand())>\" type=\"video/$ext\"></video>"
end)
end
end # module
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment