Advertisement
Guest User

Ising model with line by line graphics

a guest
Jan 2nd, 2022
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 8.90 KB | None | 0 0
  1. -- Gotta modify the mod function because taking the mod of a negative number just returns that same number.
  2. local function mod(x, y)
  3.     return (x>=0) and math.fmod(x,y) or math.fmod(x,y)+y
  4. end
  5.  
  6. -- Can you believe Lua doesn't have a built-in sign function?
  7. local function sign(x)
  8.     if x>0 then     -- For this application, we want x to be either -1 or 1 so we can choose a backdrop color, hence no x=0 case.
  9.         return 1
  10.     else
  11.         return -1
  12.     end
  13. end
  14.  
  15. -- So it turns out gui.drawLine doesn't accept lines of one pixel in length.  Boo!
  16. local function line(x1, y1, x2, y2, color)
  17.     if x1==x2 and y1==y2 then
  18.         gui.drawPixel(x1, y1, color)
  19.     else
  20.         gui.drawLine(x1, y1, x2, y2, color)
  21.     end
  22. end
  23.  
  24. local function initialize(width, height)
  25.     local grid={}
  26.     for i=0,width-1 do
  27.         grid[i]={}
  28.         for j=0,height-1 do
  29.             --grid[i][j]=math.floor(2*math.random()-1)      -- Selecting random numbers in this way doesn't work and I have no idea why.
  30.             grid[i][j]=2*math.random(2)-3
  31.             --grid[i][j]=-1
  32.         end
  33.     end
  34.     return grid
  35. end
  36.  
  37. -- Copy a table.
  38. local function tablecopy(t)
  39.     local tcopy={}
  40.     for k, v in pairs(t) do
  41.         if type(v)=="table" then
  42.             tcopy[k]=tablecopy(v)
  43.         else
  44.             tcopy[k]=v
  45.         end
  46.     end
  47.     return tcopy
  48. end
  49.  
  50. local function pickrandom(dimension)        -- dimension can be either width or height
  51.     return math.random(dimension)-1         -- Subtract 1 because indexing starts at 0.
  52. end
  53.  
  54. local function magnetization(grid)
  55.     local mag=0
  56.     for i=0,#grid do
  57.         for j=0,#grid[i] do
  58.             mag=mag+grid[i][j]
  59.         end
  60.     end
  61.    
  62.     --local width, height = #grid+1, #grid[1]+1
  63.     --mag=mag/(width*height)
  64.     return mag
  65. end
  66.  
  67. -- Draws the grid pixel by pixel.  Turns out this is extremely inefficient.
  68. local function pixelbypixel(grid)
  69.     local colorkey={"white", [-1]="black"}
  70.     local mag=sign(magnetization(grid))
  71.     gui.drawRectangle(0,0,#grid,#grid[1],colorkey[mag],colorkey[mag])       -- drawPixel is computationally expensive, so we use the magnetization to set the backdrop color.
  72.     for i=0,#grid do
  73.         for j=0,#grid[i] do
  74.             if not(grid[i][j]==mag) then
  75.                 gui.drawPixel(i, j, colorkey[grid[i][j]])
  76.             end
  77.         end
  78.     end
  79. end
  80.  
  81. -- Draws pixels row by row, hopefully saving computation.  Also updates the magnetization.
  82. local function linebyline(grid, mag)
  83.     local colorkey={"white", [-1]="black"}
  84.     signmag = sign(mag)
  85.     gui.drawRectangle(0,0,#grid,#grid[1],colorkey[signmag],colorkey[signmag])
  86.     mag=0       -- Reset magnetization to 0.
  87.     for row=0,#grid[1] do
  88.         ---[[
  89.         local prev=0
  90.         for i=0,#grid-1 do
  91.             if not(grid[i][row]==grid[i+1][row]) then
  92.                 if not(grid[prev][row]==signmag) then
  93.                     line(prev, row, i, row, colorkey[-signmag])
  94.                 end
  95.                 mag=mag+grid[prev][row]*(i-prev+1)
  96.                 prev=i+1
  97.             end
  98.         end
  99.         if not(grid[prev][row]==signmag) then
  100.             line(prev, row, #grid, row, colorkey[-signmag])
  101.         end
  102.         mag=mag+grid[prev][row]*(#grid-prev+1)
  103.         --]]
  104.        
  105.         --[[
  106.         local s = 0
  107.         local e = 1
  108.         while e<#grid do
  109.             if not(grid[s][row]==grid[e][row]) then
  110.                 if not(grid[s][row]==signmag) then
  111.                     gui.drawLine(s, row, e-1, row, colorkey[-signmag])
  112.                 end
  113.                 mag=mag+grid[s][row]*(e-s)
  114.                 s=e
  115.             end
  116.             e=e+1
  117.         end
  118.         if not(grid[s][row]==signmag) then
  119.             gui.drawLine(s, row, e-1, row, colorkey[-signmag])
  120.         end
  121.         mag=mag+grid[s][row]*(e-s)
  122.         --]]
  123.     end
  124.    
  125.     return mag
  126. end
  127.  
  128. -- Draws the grid in blocks of 15x10.  Compare with the known "quadtree" algorithm.  Why 15x10?  It's a nice round number that divides the width and height, plus it roughly evenly divides the task of drawing the block magnetization and pixel magnetization.
  129. local function blockbyblock(grid, blockmag)
  130.     local mag = sign(magnetization(blockmag))       -- Assumes the sign of the overall magnetization is the sign of the aggregate magnetization of the blocks.
  131.     local colorkey={"white", [-1]="black"}
  132.     gui.drawRectangle(0,0,#grid,#grid[1],colorkey[mag],colorkey[mag])
  133.     for blockcol = 0, 15 do         -- I'm subdividing this into a 16x16 grid of blocks of size 15x10, which presumes a width of 240 and height of 160.  This code will need to be altered if other widths/heights are to be accomodated.
  134.         for blockrow = 0, 15 do
  135.             local signmag=sign(blockmag[blockcol][blockrow])
  136.             local color=colorkey[signmag]
  137.             if not(signmag == mag) then
  138.                 gui.drawRectangle(blockcol*15, blockrow*10, 14, 9, color, color)
  139.             end
  140.             blockmag[blockcol][blockrow] = 0
  141.             for j=0,9 do
  142.                 local prev=0
  143.                 for i=0,13 do
  144.  
  145.  
  146.                     if not(grid[15*blockcol+i][10*blockrow+j]==grid[15*blockcol+i+1][10*blockrow+j]) then
  147.                         if not(grid[15*blockcol+prev][10*blockrow+j]==signmag) then
  148.                             line(15*blockcol+prev, 10*blockrow+j, 15*blockcol+i, 10*blockrow+j, colorkey[-signmag])
  149.                         end
  150.                         blockmag[blockcol][blockrow]=blockmag[blockcol][blockrow]+grid[15*blockcol+prev][10*blockrow+j]*(i-prev+1)
  151.                         prev=i+1
  152.                     end
  153.                 end
  154.                 if not(grid[15*blockcol+prev][10*blockrow+j]==signmag) then
  155.                     line(15*blockcol+prev, 10*blockrow+j, 15*blockcol+15, 10*blockrow+j, colorkey[-signmag])
  156.                 end
  157.                 blockmag[blockcol][blockrow]=blockmag[blockcol][blockrow]+grid[15*blockcol+prev][10*blockrow+j]*(14-prev+1)
  158.            
  159.             end
  160.         end
  161.     end
  162.     return blockmag
  163. end
  164.  
  165. local function neighbors(grid, i, j, width, height)
  166.     local l=mod(i-1, width)
  167.     local r=mod(i+1, width)
  168.     local u=mod(j-1, height)
  169.     local d=mod(j+1, height)
  170.    
  171.     -- This is all broken, samples the wrong points.
  172.     --[=[
  173.     local horz={l, r}
  174.     local vert={u, d}
  175.    
  176.     local eightneighbors = false        -- Use eight neighbors instead of four.
  177.     if eightneighbors then
  178.         table.insert(horz, 0)
  179.         table.insert(vert, 0)
  180.     end
  181.    
  182.     local nearest={}
  183.     for m=1,#horz do
  184.         for n=1,#vert do
  185.             table.insert(nearest, grid[horz[m]][vert[n]])
  186.         end
  187.     end
  188.    
  189.     if mod(#nearest, 2)==1 then     -- If #nearest is odd, we must have included the center square and need to remove it
  190.         table.remove(nearest,#nearest)
  191.     end
  192.     --]=]
  193.    
  194.     local nearest = {grid[l][j], grid[r][j], grid[i][u], grid[i][d]}
  195.    
  196.     return nearest
  197. end
  198.  
  199. local function deltaU(grid, i, j, width, height)
  200.     nearest=neighbors(grid, i, j, width, height)
  201.     local sum=0
  202.     for ii=1,#nearest do        -- Already using i as an argument, although Lua's scoping means we could use i if we wanted.
  203.         sum=sum+nearest[ii]
  204.     end
  205.    
  206.     return 2*grid[i][j]*sum
  207. end
  208.  
  209. local function atrandom(grid, width, height, temp, iterations)
  210.     for k=1,iterations do
  211.         i, j = pickrandom(width), pickrandom(height)
  212.         local Ediff = deltaU(grid, i, j, width, height)
  213.         if Ediff<=0 or math.random()<math.exp(-Ediff/temp) then
  214.             grid[i][j] = -grid[i][j]
  215.         end
  216.     end
  217.     return grid
  218. end
  219.  
  220. local function allatonce(oldgrid, width, height, temp, iterations)
  221.     local newgrid=tablecopy(oldgrid)
  222.     for k=1,iterations do
  223.         for i=0,width-1 do
  224.             for j=0,height-1 do
  225.                 local Ediff = deltaU(oldgrid, i, j, width, height)
  226.                 if Ediff<=0 or math.random()<math.exp(-Ediff/temp) then
  227.                     newgrid[i][j] = -oldgrid[i][j]
  228.                 end
  229.             end
  230.         end
  231.         oldgrid=tablecopy(newgrid)
  232.     end
  233.     return newgrid
  234. end
  235.  
  236. local width=240     -- Width and height are 240 and 160 (for GBA), but indexing starts at 0 so we subtract 1 when initializing the grid.  We still use these values for modular arithmetic.
  237. local height=160
  238. -- Critical temperature is ~2.26918531421
  239. local kT=2.26918531421
  240. local rate=0.2*width*height     -- Number of iterations to perform before drawing all the pixels, which is computationally expensive.
  241. local startframe=emu.framecount()
  242. local grid = initialize(width, height)
  243. local keys, last = input.get(), input.get()
  244. local printmag = true
  245. local printtemp = true
  246.  
  247. local mag=-1
  248. local drawstyle=0
  249. local blockmag={}
  250. for i=0,15 do
  251.     blockmag[i]={}
  252.     for j=0,15 do
  253.         blockmag[i][j] = 0
  254.     end
  255. end
  256.  
  257. while true do
  258.     --gui.pixelText(80, 20, math.exp(-1/2))
  259.     --gui.pixelText(80, 30, deltaU(grid, 30, 30, 50, 50))
  260.    
  261.     local t = emu.framecount()-startframe
  262.     local A = 0.8
  263.     local omega = math.pi/500
  264.     local lambda = math.log(1/0.8)/500      -- omega*t=pi ==> exp(-lambda*t) = 0.8 ==> -lambda*pi/omega = ln(0.8) ==> lambda = omega/pi * ln(1/0.8)
  265.     local temp = 2.26918531421 - A * math.exp(-lambda*t) * math.cos(omega*t)
  266.    
  267.    
  268.     keys, last = input.get(), tablecopy(keys)
  269.     if keys["Space"] and not last["Space"] then
  270.         printmag = not printmag
  271.     end
  272.    
  273.     if keys["Enter"] and not last["Enter"] then
  274.         printtemp = not printtemp
  275.     end
  276.    
  277.     if keys["D"] and not last["D"] then
  278.         drawstyle = math.fmod(drawstyle+1, 3)
  279.     end
  280.    
  281.     if drawstyle == 0 then
  282.         mag = linebyline(grid, mag)
  283.     elseif drawstyle == 1 then
  284.         blockmag = blockbyblock(grid, blockmag)
  285.         mag=magnetization(blockmag)
  286.     else
  287.         --pixelbypixel(grid)        -- Commenting out this line because pixelbypixel is inefficient and we can instead just draw nothing.
  288.     end
  289.    
  290.     if printmag then
  291.         --local mag = magnetization(grid)
  292.         gui.pixelText(80, 20, mag, "yellow", "black")
  293.     end
  294.    
  295.     if printtemp then gui.pixelText(80, 30, temp, "yellow", "black") end
  296.    
  297.     local grid = atrandom(grid, width, height, temp, rate)
  298.     --gui.pixelText(80, 20, kT,nil,"black")
  299.     --gui.pixelText(80, 30, emu.framecount()-startframe,nil,"black")
  300.    
  301.     emu.frameadvance()
  302. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement