0 people like it.
Like the snippet!
cell-simulation.fsx
F# Simulation lab
Part III Systems Biology
Cambridge University
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
|
(*
We consider the movement of cells towards an area of ligand activity (chemotaxis) and the cells’
reactions to different ligands.
For simplicity, we will consider only horizontal movement on an integer grid in the first two
parts of this exercise. That is, the locations of the cells can only take integer values and
the movement will only change the x-part of the cell location.
The module Cells defines the object Cell and functions that can be applied to it in order to
define the behaviour of the model.
What you should observe are cells (circles) moving towards two different kinds of ligands,
called Ras and Notch (indicated as areas of different colour). The cells start in areas
with no ligand present and move towards the closest ligand or in a random direction if
they are in the middle between two ligand areas. Once the cells have reached the area of
ligand activity, they react to the specific ligand. Ras causes the cells to stop their
movement and grow and Notch results in the cells to stop moving.
*)
/// Defines the properties of cells and functions describing their behaviour
module Cells =
/// Creates a random number generator
let randomGenerator = System.Random()
/// Represents a point (x,y) in the model with coordinates on an integer grid
type Point = {x:int;y:int}
/// Build a point with coordinates x and y
let Point(x,y) = {x=x;y=y}
/// Represents ligand activity
type Ligand =
/// Represents the presence of Notch protein
| Notch
/// Represents the presence of Ras protein
| Ras
/// Represents the absence of ligand activity
| NoLigand
///Represents the state of a cell that defines how it will behave
type CellMarker =
/// Represents a cell that is able to move
| Move
/// Represents a cell that is able to grow
| Grow
/// Represents a cell that stays in the place that it is in and does not do anything
| Stopped
/// Represents a cell that has died
| Dead
/// Represents a cell that is set to divide
| Divide
/// Defines the states and state modifications of a cell
type Cell(x,y) =
// Define the initial states of the cell. The keyword 'mutable' is used to indicate
// that the state can change over time.
let mutable location = Point(x,y)
let mutable size = 0.5
let mutable marker = Move
let mutable direction = 0
// Expose the states of the cell to make them accessible from outside of the object
member this.Location with get() = location and set(x) = location <- x
member this.Marker with get() = marker and set(x) = marker <- x
member this.Direction with get() = direction and set(x) = direction <- x
member this.Size with get() = size and set(x) = size <- x
// Modify states of the cell using functions that can be called outside of the object
member this.Move() = location <- Point(location.x + direction,location.y)
member this.Grow(x) = size <- size + x
member this.RenderData() =
let color =
match marker with
| Move -> "blue"
| Grow -> "magenta"
| Dead -> "black"
| Divide -> "red"
| _ -> "green"
(location.x, location.y, size, color)
/// Computes the ligand activity in location p.
let ligand (p:Point) =
let x = p.x
if x < 10 then Ras
elif x > 20 && x < 30 then Notch
elif x > 40 && x < 50 then Ras
elif x > 60 && x < 70 then Notch
elif x > 80 && x < 90 then Ras
else NoLigand
/// This function is called by the simulation engine. It sets the marker of a cell according
/// to the ligand activity at its location and in part B depending its neighbour and the ligand
/// activity at its next location.
let setMarker (cells:Cell[]) (cell:Cell) =
// These three definitions are needed in part B
let nextX = cell.Location.x + cell.Direction
let nextLoc = Point(nextX,cell.Location.y)
let neighbour = Array.filter (fun (c:Cell) -> c.Location = nextLoc) cells
if ligand cell.Location = Ras then
cell.Marker <- Grow
elif ligand cell.Location = Notch then
cell.Marker <- Stopped
/// This function is called by the simulation engine. It performs the actions
/// described by the markers decided by the 'setMarkers' function in an earlier phase of
/// the simulation on a cell.
let react (step:float) (cell:Cell) =
if cell.Marker = Move then
cell.Move()
elif cell.Marker = Grow then
cell.Grow(step)
/// This function is called by the simulation engine. It defines the direction of a cell
/// according to its location relative to the ligands.
let cellDirection (cell:Cell) =
let x = cell.Location.x
let y = cell.Location.y
let right = Point(x+5,y)
let left = Point(x-5,y)
if ligand cell.Location = NoLigand && ligand right = NoLigand && ligand left = NoLigand then
// If the cell is located with equal distance to the ligand on the left and the ligand on the right, the direction is chosen at random.
let random = randomGenerator.Next(0,3)
cell.Direction <- random - 1
else if cell.Direction = 0 then
// Else if the cell's direction hasn't been set yet, it will move towards the closest ligand.
if ligand right = Notch || ligand right = Ras then
cell.Direction <- 1
else if ligand left = Notch || ligand left = Ras then
cell.Direction <- -1
/// This function is called by the simulation engine. It lets a cell divide if the location
/// to its left or its right is free, it places the new daughter cell in the free space
/// and it resets the division timer of a cell that has just divided. If the neighbouring
/// locations aren't free, the function does nothing so that the cell can try to divide
/// again at the next simulation step. The function returns the new cell.
let cellDivision (cells:Cell[]) (cell:Cell) = [| |]
/// This function is called by the simulation engine. It lets a cell die if would
/// move from an area without ligand to an area with Ras activity, but the location
/// is occupied.
let cellDeath (cell:Cell) = ()
// Now create an HTML5 Canvas element to visualize the scene in the HTML graphics pane
let width, height = 1000, 1000
TryFSharp.Canvas.Show()
TryFSharp.Canvas.RunJavaScript (sprintf @"
(function (self) {
var canv = document.getElementById('canvas1');
if (canv == undefined) {
canv = document.createElement('canvas');
if (canv.getContext) {
canv.id = 'canvas1';
canv.width = %A;
canv.height = %A;
document.body.appendChild(canv);
} else {
alert('Your browser does not seem to support HTML5 Canvas.');
};
};
var ctx = canv.getContext('2d');
ctx.scale(2.0,2.0);
self.render_wall = function() {
var start,stop,range,color;
ctx.clearRect(0,0,%A,%A);
start=0; stop=10; range=stop-start; color='pink';
ctx.fillStyle = color; ctx.fillRect(start,0,range,50);
start=20; stop=30; range=stop-start; color='brown';
ctx.fillStyle = color; ctx.fillRect(start,0,range,50);
start=40; stop=50; range=stop-start; color='pink';
ctx.fillStyle = color; ctx.fillRect(start,0,range,50);
start=60; stop=70; range=stop-start; color='brown';
ctx.fillStyle = color; ctx.fillRect(start,0,range,50);
start=80; stop=90; range=stop-start; color='pink';
ctx.fillStyle = color; ctx.fillRect(start,0,range,50);
ctx.fill();
};
self.render_cell = function(pos_x,pos_y,size,color) {
ctx.beginPath();
ctx.arc(pos_x,pos_y,size,0,2*Math.PI);
ctx.fillStyle=color;
ctx.fill();
ctx.strokeStyle=color;
ctx.stroke();
};
return self;
}(window.CellSim = window.CellSim || {}));
" width height width height)
// Export JS to F#
let r_cell = TryFSharp.Canvas.JavaScriptFunction("CellSim.render_cell")
let r_wall = TryFSharp.Canvas.JavaScriptFunction("CellSim.render_wall")
// Simulation step function.
let timeDelta = 0.5
let step (cells : Cells.Cell[]) =
for cell in cells do
Cells.cellDirection cell
for cell in cells do
Cells.setMarker cells cell
for cell in cells do
Cells.react timeDelta cell
cells
let step_and_draw cells =
// Run a simulation step.
let cells' = step cells
// Render just the wall.
r_wall.Invoke() |> ignore
// Render each cell on the wall.
Array.iter
(fun (c:Cells.Cell) ->
let (x,y,size,clr) = c.RenderData()
r_cell.Invoke(x,y,size,clr) |> ignore)
cells'
cells'
let rec run_steps num_of_steps step state =
if num_of_steps = 0 then state
else
let state' = step state
run_steps (num_of_steps - 1) step state'
// Initial simulation state,
let mk_initial_cells _ =
let start_cells (x:int) = [| for y in 0 .. 9 -> Cells.Cell(x,2+4*y) |]
[| yield! start_cells 15
yield! start_cells 35
yield! start_cells 55
yield! start_cells 75 |]
/// Run the simulation
// Make the cells
let c0 = mk_initial_cells ()
// Render the activity wall.
r_wall.Invoke() |> ignore
// Run 7 steps
run_steps 7 step_and_draw c0
|
val randomGenerator : System.Random
Full name: Script.Cells.randomGenerator
Creates a random number generator
namespace System
Multiple items
type Random =
new : unit -> Random + 1 overload
member Next : unit -> int + 2 overloads
member NextBytes : buffer:byte[] -> unit
member NextDouble : unit -> float
Full name: System.Random
--------------------
System.Random() : unit
System.Random(Seed: int) : unit
type Point =
{x: int;
y: int;}
Full name: Script.Cells.Point
Represents a point (x,y) in the model with coordinates on an integer grid
Point.x: int
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
Point.y: int
Multiple items
val Point : x:int * y:int -> Point
Full name: Script.Cells.Point
Build a point with coordinates x and y
--------------------
type Point =
{x: int;
y: int;}
Full name: Script.Cells.Point
Represents a point (x,y) in the model with coordinates on an integer grid
val x : int
val y : int
type Ligand =
| Notch
| Ras
| NoLigand
Full name: Script.Cells.Ligand
Represents ligand activity
union case Ligand.Notch: Ligand
Represents the presence of Notch protein
union case Ligand.Ras: Ligand
Represents the presence of Ras protein
union case Ligand.NoLigand: Ligand
Represents the absence of ligand activity
type CellMarker =
| Move
| Grow
| Stopped
| Dead
| Divide
Full name: Script.Cells.CellMarker
Represents the state of a cell that defines how it will behave
union case CellMarker.Move: CellMarker
Represents a cell that is able to move
union case CellMarker.Grow: CellMarker
Represents a cell that is able to grow
union case CellMarker.Stopped: CellMarker
Represents a cell that stays in the place that it is in and does not do anything
union case CellMarker.Dead: CellMarker
Represents a cell that has died
union case CellMarker.Divide: CellMarker
Represents a cell that is set to divide
Multiple items
type Cell =
new : x:int * y:int -> Cell
member Grow : x:float -> unit
member Move : unit -> unit
member RenderData : unit -> int * int * float * string
member Direction : int
member Location : Point
member Marker : CellMarker
member Size : float
member Direction : int with set
member Location : Point with set
...
Full name: Script.Cells.Cell
Defines the states and state modifications of a cell
--------------------
new : x:int * y:int -> Cell
val mutable location : Point
val mutable size : float
val mutable marker : CellMarker
val mutable direction : int
val this : Cell
member Cell.Location : Point with set
Full name: Script.Cells.Cell.Location
val set : elements:seq<'T> -> Set<'T> (requires comparison)
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
val x : Point
member Cell.Marker : CellMarker with set
Full name: Script.Cells.Cell.Marker
val x : CellMarker
member Cell.Direction : int with set
Full name: Script.Cells.Cell.Direction
member Cell.Size : float with set
Full name: Script.Cells.Cell.Size
val x : float
member Cell.Move : unit -> unit
Full name: Script.Cells.Cell.Move
member Cell.Grow : x:float -> unit
Full name: Script.Cells.Cell.Grow
member Cell.RenderData : unit -> int * int * float * string
Full name: Script.Cells.Cell.RenderData
val color : string
val ligand : p:Point -> Ligand
Full name: Script.Cells.ligand
Computes the ligand activity in location p.
val p : Point
val setMarker : cells:Cell [] -> cell:Cell -> unit
Full name: Script.Cells.setMarker
This function is called by the simulation engine. It sets the marker of a cell according
to the ligand activity at its location and in part B depending its neighbour and the ligand
activity at its next location.
val cells : Cell []
val cell : Cell
val nextX : int
property Cell.Location: Point
property Cell.Direction: int
val nextLoc : Point
val neighbour : Cell []
module Array
from Microsoft.FSharp.Collections
val filter : predicate:('T -> bool) -> array:'T [] -> 'T []
Full name: Microsoft.FSharp.Collections.Array.filter
val c : Cell
property Cell.Marker: CellMarker
val react : step:float -> cell:Cell -> unit
Full name: Script.Cells.react
This function is called by the simulation engine. It performs the actions
described by the markers decided by the 'setMarkers' function in an earlier phase of
the simulation on a cell.
val step : float
Multiple items
val float : value:'T -> float (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.float
--------------------
type float = System.Double
Full name: Microsoft.FSharp.Core.float
--------------------
type float<'Measure> = float
Full name: Microsoft.FSharp.Core.float<_>
member Cell.Move : unit -> unit
member Cell.Grow : x:float -> unit
val cellDirection : cell:Cell -> unit
Full name: Script.Cells.cellDirection
This function is called by the simulation engine. It defines the direction of a cell
according to its location relative to the ligands.
val right : Point
val left : Point
val random : int
System.Random.Next() : int
System.Random.Next(maxValue: int) : int
System.Random.Next(minValue: int, maxValue: int) : int
val cellDivision : cells:Cell [] -> cell:Cell -> 'a []
Full name: Script.Cells.cellDivision
This function is called by the simulation engine. It lets a cell divide if the location
to its left or its right is free, it places the new daughter cell in the free space
and it resets the division timer of a cell that has just divided. If the neighbouring
locations aren't free, the function does nothing so that the cell can try to divide
again at the next simulation step. The function returns the new cell.
val cellDeath : cell:Cell -> unit
Full name: Script.Cells.cellDeath
This function is called by the simulation engine. It lets a cell die if would
move from an area without ligand to an area with Ras activity, but the location
is occupied.
val width : int
Full name: Script.width
val height : int
Full name: Script.height
val sprintf : format:Printf.StringFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val r_cell : obj
Full name: Script.r_cell
val r_wall : obj
Full name: Script.r_wall
val timeDelta : float
Full name: Script.timeDelta
val step : cells:Cells.Cell [] -> Cells.Cell []
Full name: Script.step
val cells : Cells.Cell []
module Cells
from Script
Defines the properties of cells and functions describing their behaviour
Multiple items
type Cell =
new : x:int * y:int -> Cell
member Grow : x:float -> unit
member Move : unit -> unit
member RenderData : unit -> int * int * float * string
member Direction : int
member Location : Point
member Marker : CellMarker
member Size : float
member Direction : int with set
member Location : Point with set
...
Full name: Script.Cells.Cell
Defines the states and state modifications of a cell
--------------------
new : x:int * y:int -> Cells.Cell
val cell : Cells.Cell
val cellDirection : cell:Cells.Cell -> unit
Full name: Script.Cells.cellDirection
This function is called by the simulation engine. It defines the direction of a cell
according to its location relative to the ligands.
val setMarker : cells:Cells.Cell [] -> cell:Cells.Cell -> unit
Full name: Script.Cells.setMarker
This function is called by the simulation engine. It sets the marker of a cell according
to the ligand activity at its location and in part B depending its neighbour and the ligand
activity at its next location.
val react : step:float -> cell:Cells.Cell -> unit
Full name: Script.Cells.react
This function is called by the simulation engine. It performs the actions
described by the markers decided by the 'setMarkers' function in an earlier phase of
the simulation on a cell.
val step_and_draw : cells:Cells.Cell [] -> Cells.Cell []
Full name: Script.step_and_draw
val cells' : Cells.Cell []
val ignore : value:'T -> unit
Full name: Microsoft.FSharp.Core.Operators.ignore
val iter : action:('T -> unit) -> array:'T [] -> unit
Full name: Microsoft.FSharp.Collections.Array.iter
val c : Cells.Cell
val size : float
val clr : string
member Cells.Cell.RenderData : unit -> int * int * float * string
val run_steps : num_of_steps:int -> step:('a -> 'a) -> state:'a -> 'a
Full name: Script.run_steps
val num_of_steps : int
val step : ('a -> 'a)
val state : 'a
val state' : 'a
val mk_initial_cells : 'a -> Cells.Cell []
Full name: Script.mk_initial_cells
val start_cells : (int -> Cells.Cell [])
val c0 : Cells.Cell []
Full name: Script.c0
Run the simulation
More information