Message Boards Message Boards

Applying Instance Indexed OOP to Multi-core Life Game

Posted 3 years ago

Preface

Mathematica OOP now presents an environment of direct addressing for the instances with each other.

In general the name of Instance is a simple symbol to distinguish with each other, however, Mathematica OOP has the capability to use equation as a name of the Instance. Using this method, Instance come to obtaining the ability to hold the own address, let's name this method to indexed instance, and this method approve variety of addressing style.

Preliminary, OOP-ed Life Game was shown where the equation was applied to the instance name instead of a symbol.

Abstraction of addressing for indexed instances

Basically, addressing is the reflection of the CPU memory. Then giving the expression as a name of Instance, address of the instance become no relation with the memory, that is the abstraction of addressing. Following example shows the results obtained from the indexed instance .

In this program, following addressing style is adopted as the NAME of instance.

cell[row, column]

This style gives direct addressing method to find an instance that is a node on 2-dimensional grid.

Abstracted address can adapt to multi-core environment as a uniformed addressed instances over the multi cores. Then, the calculation on a uniform addressing can adopt an arbitrary number of cores.

Preparing for Life Game

The each Life Game cell is determined by environment cells, then in the multi-core condition, a special handling must be prepared for the edge cell. In this code, the border-cell-line which will be refferd by the cells on the edge is a copy cell-line of another core, and must be refreshed at the end of each calculation steps.

All parameters of the core-name, core-number, working-cell-line, and border-cell-line are defined by the association. In this sample, Life-Game working area is divided to 2 cores to show the calculating environment more clealy.

Multi-core Life Game code

First code is the parameter definitions for 2-core calculation.

row = 40;
column = 40;
coreObject = {
   Association["coreNumber" -> 1, "name" -> core[1], 
    "workingCell" -> {1, 20}, "borderCell" -> {21, 40}],
   Association["coreNumber" -> 2, "name" -> core[2], 
    "workingCell" -> {21, 40}, "borderCell" -> {1, 20}]
   };

When you want to use 4-core, following is another definition.

coreObject = {
   Association["coreNumber" -> 1, "name" -> core[1], 
    "workingCell" -> {1, 10}, "borderCell" -> {11, 40}],
   Association["coreNumber" -> 2, "name" -> core[2], 
    "workingCell" -> {11, 20}, "borderCell" -> {10, 21}],
   Association["coreNumber" -> 3, "name" -> core[3], 
    "workingCell" -> {21, 30}, "borderCell" -> {20, 31}],
   Association["coreNumber" -> 4, "name" -> core[4], 
    "workingCell" -> {31, 40}, "borderCell" -> {1, 30}]
   };

Next, we will starts cores as,

CloseKernels[];
LaunchKernels[]

Definition of core-class which includes nested cell-class as,

coreClass[name_, cellMap_, border_] := Module[
  {myCellName,
   myBorderCellName,
   myAllCellName},

  (* initialization: prepare cell name *)

  myCellName = 
   Table[cell[i, j], {i, cellMap[[1]], cellMap[[2]]}, {j, column}];
  myBorderCellName = Map[Table[cell[#, j], {j, column}] &, border];
  myAllCellName = Flatten[{myCellName, myBorderCellName}, 1];
  (* initialization: construct cell instance *)

  Map[cellClass[name[#]] &, myAllCellName, {2}];
  (* end of initialization *)

  (* message for all core-class *)

  randomInitCell := 
   Map[cellSet[#[RandomInteger[]]] &, myCellName, {2}];
  getCell := Map[cellGet[#] &, myCellName, {2}];
  getBorderCell := Map[cellGet[#] &, myBorderCellName, {2}];
  neighbour := Map[neighbourCount[#] &, myCellName, {2}];
  stepForward := Map[nextMode[#] &, myCellName, {2}];

  (* message specified to me *)

  getCellLine[name[rowLine_]] ^:= Map[get[#] &, myCellName[[rowLine]]];
  setCellLine[name[{cellLineName_, cellLineData_}]] ^:=

   MapThread[cellSet[#1[#2]] &, {cellLineName, cellLineData}];
  setCellRow[name[{rowLine_, cellLineData_}]] ^:=

   MapThread[
    cellSet[#1[#2]] &, {Cases[myAllCellName, cell[rowLine, _], {2}], 
     cellLineData}];

  (* specific cell pattern *)

  glider := (Map[cellSet[#[0]] &, myCellName, {2}];
    {cellSet[cell[33, 23][2]], cellSet[cell[33, 24][3]], 
     cellSet[cell[33, 25][4]],
     cellSet[cell[34, 23][5]], cellSet[cell[35, 24][6]]}
    );
  flush := (Map[cellSet[#[0]] &, myCellName, {2}];
    {cellSet[cell[5, 2][7]], cellSet[cell[5, 3][8]], 
     cellSet[cell[5, 4][9]]}
    );

  (* cell class *)
  cellClass[name[cellNam_]] ^:= Module[
    {r, c,
     status = 0,
     nbrList, nbrT,
     updatedStatus = 0},

    (* cell messages *)
    cellSet[cellNam[x_]] ^:= status = x;
    cellGet[cellNam] ^:= status;
    neighbourCount[cellNam] ^:= (
      updatedStatus = status;
      {r, c} = List @@ cellNam;
      nbrList = {
        cell[Mod[r - 1, row, 1], Mod[c - 1, column, 1]],
        cell[Mod[r - 1, row, 1], c],
        cell[Mod[r - 1, row, 1], Mod[c + 1, column, 1]],
        cell[r, Mod[c - 1, column, 1]],
        cell[r, Mod[c + 1, column, 1]],
        cell[Mod[r + 1, row, 1], Mod[c - 1, column, 1]],
        cell[Mod[r + 1, row, 1], c],
        cell[Mod[r + 1, row, 1], Mod[c + 1, column, 1]]};
      nbrT = Total[Map[cellGet[#] &, nbrList]];
      If[nbrT == 3, updatedStatus = 1] /; status == 0;
      If[Or[nbrT <= 1, nbrT >= 4], updatedStatus = 0] /; status == 1;
      );
    nextMode[cellNam] ^:= (
      status = updatedStatus;
      updatedStatus = 0;
      )
    ]
  (* end of cell class *)
  ]
(* end of core class *)

Following is core and cell construction. Mathematica will fail with the first cell construction, then we must execute same line.

Map[ParallelEvaluate[
    coreClass[#name, #workingCell, #borderCell], #coreNumber] &, 
  coreObject];
Map[ParallelEvaluate[
    coreClass[#name, #workingCell, #borderCell], #coreNumber] &, 
  coreObject];

Followings are cell setter. as,

  1. random cell setter

    ParallelEvaluate[randomInitCell];
    d = ParallelEvaluate[getCell][[1 ;; Length[coreObject]]];
    
  2. glider setter

    ParallelEvaluate[glider];
    d = ParallelEvaluate[getCell][[1 ;; Length[coreObject]]];
    
  3. simple flush bar

    ParallelEvaluate[flush];
    d = ParallelEvaluate[getCell][[1 ;; Length[coreObject]]];
    

Then we can display the working area as,

Dynamic[ArrayPlot[Flatten[d, 1], Mesh -> True]]

Working area started from random seeding to several times of steps

Here is the stepper as,

Do[

 (* message to cell, neigbour calculation *)

 ParallelEvaluate[neighbour];
 (* message to cell, step forward *)
 ParallelEvaluate[stepForward];
 (* get data of cell to display *)

 d = ParallelEvaluate[getCell][[1 ;; Length[coreObject]]];
 (* reflesh border cell *)

 Map[{ParallelEvaluate[
     setCellRow[#name[{First[#borderCell], 
        Flatten[d, 1][[First[#borderCell]]]}]], #coreNumber],
    ParallelEvaluate[
     setCellRow[#name[{Last[#borderCell], 
        Flatten[d, 1][[Last[#borderCell]]]}]], #coreNumber]}
   &, coreObject];

 , 200]

Enjoy !

enter image description here -- you have earned Featured Contributor Badge enter image description here Your exceptional post has been selected for our editorial column Staff Picks http://wolfr.am/StaffPicks and Your Profile is now distinguished by a Featured Contributor Badge and is displayed on the Featured Contributor Board. Thank you!

POSTED BY: Moderation Team
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract