using Revise
+using DifferentialEquations, AlgebraicAgents
+using Plots
+using Random, DataFrames
+Random.seed!(0)
TaskLocalRNG()
+diff --git a/Project.toml b/Project.toml index 752d3cf..a0bd8a5 100644 --- a/Project.toml +++ b/Project.toml @@ -5,15 +5,17 @@ version = "0.3.16" [deps] Crayons = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" Glob = "c27321d9-0574-5035-807b-f59d2c89b15c" +IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Requires = "ae029012-a4dd-5104-9daa-d747884805df" +Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [compat] -julia = "1.8" -Requires = "1.3" Crayons = "4.1" Glob = "1.3" MacroTools = "0.5" +Requires = "1.3" +julia = "1.8" diff --git a/tutorials/Petri_refactor/CellAuto_Petrinet.html b/tutorials/Petri_refactor/CellAuto_Petrinet.html new file mode 100644 index 0000000..ece06e4 --- /dev/null +++ b/tutorials/Petri_refactor/CellAuto_Petrinet.html @@ -0,0 +1,909 @@ + +
+ + + + + + + + +version 0.1.2
+This document provides a detailed walkthrough of a cellular automata model simulated as a Petri Net. The model represents cells with a dynamical state (active, inactive, dormant) that can change based on the presence of cancer cells. Each cell also has a type (NormalCellA, NormalCellB, CancerCellA, CancerCellB) which is randomly assigned at the start of the simulation. The system evolves over time and the current state of the system is recorded and visualized in a plot.
+A Petri net framework can be used to model a biological system like cancer, as it represents dynamic processes in which individual items pass through multiple states. Petri nets are composed of places, transitions, and arcs. Places represent the state of an item and transitions represent the operations that affect the item. The arcs link the various places and transitions together to create a flow of information. For example, in the case of a cancer system, the places could represent tumor growth and the transitions could represent treatments that are administered, such as chemotherapy or radiation. The arcs between the places and transitions would then dictate how the treatments affect the tumor growth. The Petri net framework can then be used to analyze the system as a whole and identify potential areas for improvement or areas of focus.
+In this document, we will show how to use the Petri Net framework to simulate a cellular automata model. We will also see how to use the Petri Net framework to analyze the system as a whole. The Petri Net framework consists several key elements: 1. A Petri Net 1. Input Arc 1. connect Places to a transition 2. with arc weight to be the number of tokens required for the transition 3. contains 2. Output Arc 3. Place 4. Transition 5. Marking 2. Tokens 3. Dynamical state
+We start by importing the necessary packages:
+using Revise
+using DifferentialEquations, AlgebraicAgents
+using Plots
+using Random, DataFrames
+Random.seed!(0)
TaskLocalRNG()
+We define an abstract type AbstractCellType and four concrete types NormalCellA, NormalCellB, CancerCellA, and CancerCellB that inherit from AbstractCellType.
+abstract type AbstractCellType end
+struct NormalCellA <: AbstractCellType end
+struct NormalCellB <: AbstractCellType end
+struct CancerCellA <: AbstractCellType end
+struct CancerCellB <: AbstractCellType end
We define a mutable struct Cell with fields for the cell type, an ID, the dynamical state, and the time in active state. We also define a struct Place that represents a location with a name and a vector of cells (tokens).
+mutable struct Cell
+::AbstractCellType
+ cell_type::Int
+ id::Symbol
+ dynamical_state::Int
+ time_in_active_stateend
+
+mutable struct Place
+::String
+ name::Vector{Cell}
+ tokensend
A Transition represents a transformation from input places to output places. It has fields for the name of the transition, the input places, and the output places.
+struct Transition
+::String
+ name::Vector{Tuple{Int, Place, DataType}}
+ input::Vector{Tuple{Int, Place, DataType}}
+ outputend
We define two helper functions add_token! and remove_token! to add and remove cells from a place.
+function add_token!(place::Place, token::Cell)
+push!(place.tokens, token)
+ end
+
+function remove_token!(place::Place, token::Cell)
+deleteat!(place.tokens, findfirst(t -> t == token, place.tokens))
+ end
remove_token! (generic function with 1 method)
+We define a function simulate_dynamical_state! that updates the dynamical state of a cell based on the presence of cancer cells in the same place.
+function simulate_dynamical_state!(cell::Cell, cancer_cell_present::Bool,
+::Int)
+ max_active_timeif cancer_cell_present &&
+ == :dormant || cell.dynamical_state == :inactive)
+ (cell.dynamical_state = :active
+ cell.dynamical_state = 0
+ cell.time_in_active_state elseif cell.dynamical_state == :active
+ += 1
+ cell.time_in_active_state if cell.time_in_active_state >= max_active_time
+ = :dormant
+ cell.dynamical_state = 0
+ cell.time_in_active_state end
+ end
+ end
simulate_dynamical_state! (generic function with 1 method)
+The function perform_transition! checks the feasibility of a transition by confirming if it has the required number of tokens in the input places. If the transition is feasible, it removes the tokens from the input places and adds new tokens to the output places. The function also updates the dynamical state of the tokens based on the presence of cancer cells.
+function perform_transition!(transition::Transition)
+= false
+ cancer_cell_exists = 0
+ num_tokens
+for (count, place, token_type) in transition.input
+ = filter(t -> isa(t.cell_type, token_type), place.tokens)
+ tokens_to_remove
+if length(tokens_to_remove) < count
+ return false
+ end
+
+for token in tokens_to_remove
+ if isa(token.cell_type, CancerCellA) || isa(token.cell_type, CancerCellB)
+ = true
+ cancer_cell_exists end
+ += 1
+ num_tokens end
+ end
+
+if cancer_cell_exists && num_tokens > 30
+ for (count, place, token_type) in transition.input
+ = filter(t -> isa(t.cell_type, token_type), place.tokens)
+ tokens_to_remove for token in tokens_to_remove[1:count]
+ remove_token!(place, token)
+ end
+ end
+
+for (count, place, token_type) in transition.output
+ for _ in 1:count
+ = Cell(token_type(), rand(Int),
+ new_cell rand([:active, :inactive, :dormant]), max_active_time)
+ simulate_dynamical_state!(new_cell, cancer_cell_exists, max_active_time)
+ add_token!(place, new_cell)
+ end
+ end
+
+return true
+ end
+
+return false
+ end
perform_transition! (generic function with 1 method)
+We define a Petri Net using the previously defined Place and Transition structures. The Petri Net also includes InputArc and OutputArc structures that connect Places and Transitions.
+struct InputArc
+::Place
+ source::Transition
+ target::DataType
+ cell_type::Int
+ weightend
+
+struct OutputArc
+::Transition
+ source::Place
+ target::DataType
+ cell_type::Int
+ weightend
+
+struct PetriNet
+::Vector{Place}
+ places::Vector{Transition}
+ transitions::Vector{InputArc}
+ input_arcs::Vector{OutputArc}
+ output_arcsend
We initialize the Petri Net with a certain number of cells and let the system evolve over a predefined number of time steps. At each time step, we update the dynamical state of each cell, check if each transition can fire, and if so, fire the transition. We also record the state of the system at each time step.
+function can_fire(transition::Transition, places::Vector{Place})
+for (count, place, token_type) in transition.input
+ = filter(t -> isa(t.cell_type, token_type), place.tokens)
+ tokens_to_remove
+if length(tokens_to_remove) < count
+ return false
+ end
+ end
+
+return true
+ end
+
+function count_cells(pn::PetriNet)
+= Dict{Tuple{DataType, Symbol}, Int}()
+ cell_counts
+for place in pn.places
+ for cell in place.tokens
+ = (typeof(cell.cell_type), cell.dynamical_state)
+ cell_key = get(cell_counts, cell_key, 0) + 1
+ cell_counts[cell_key] end
+ end
+
+return cell_counts
+ end
count_cells (generic function with 1 method)
+the Plots package to visualize the evolution of the system. At each time step, we create a bar plot of the count of each type of cell in each dynamical state.
+function plot_petri_net(pn::PetriNet)
+= count_cells(pn)
+ cell_counts
+= bar([string(key) for key in keys(cell_counts)],
+ p in values(cell_counts)],
+ [value for value = "Cell Type and Dynamical State",
+ xlabel = "Count",
+ ylabel = "Petri Net Simulation",
+ title = false)
+ legend display(p)
+ end
plot_petri_net (generic function with 1 method)
+To run the simulation, we initialize the Petri Net and then perform a series of updates in a loop. At each time step, we print out the current state of the Petri Net and generate a plot.
+We initialize the Petri net by creating the places (P1 and P2), the transitions (T1), and setting up the input and output arcs.
+function init_petri_net(num_cells::Int, max_active_time::Int)
+# Define cell types
+ = [NormalCellA, NormalCellB, CancerCellA, CancerCellB]
+ cell_types
+# Define the places
+ = Place("P1",
+ P1 Cell(rand(cell_types)(), i, rand([:active, :inactive, :dormant]),
+ [in 1:round(Int, 0.5 * num_cells)])
+ max_active_time) for i = Place("P2",
+ P2 Cell(rand(cell_types)(), i, rand([:active, :inactive, :dormant]),
+ [in 1:round(Int, 0.5 * num_cells)])
+ max_active_time) for i = Place("P3", [])
+ P3
+# Define the transitions
+ = Transition("T1", [(3, P1, NormalCellA), (1, P2, CancerCellA)],
+ T1 1, P3, CancerCellA)])
+ [(
+# Define arcs
+ = InputArc(P1, T1, NormalCellA, 30)
+ input_arc1 = InputArc(P2, T1, CancerCellA, 30)
+ input_arc2 = OutputArc(T1, P3, CancerCellA, 50)
+ output_arc1
+# Define the Petri net
+ = PetriNet([P1, P2, P3], [T1], [input_arc1, input_arc2], [output_arc1])
+ pn
+return pn
+ end
init_petri_net (generic function with 1 method)
+We update the Petri net by checking the state of the cells and performing transitions if possible.
+function update_petri_net(pn::PetriNet)
+# Iterate through places
+ for place in pn.places
+ # Check if cancer cells are present in the place
+ = any(cell -> isa(cell.cell_type, CancerCellA) ||
+ cancer_cell_present isa(cell.cell_type, CancerCellB), place.tokens)
+
+# Update dynamical states of all cells in the place
+ for cell in place.tokens
+ simulate_dynamical_state!(cell, cancer_cell_present, max_active_time)
+ end
+ end
+
+# Iterate through transitions
+ for transition in pn.transitions
+ # Check if the transition can be fired
+ if can_fire(transition, pn.places)
+ # Fire the transition and update the system's state
+ perform_transition!(transition)
+ end
+ end
+ end
update_petri_net (generic function with 1 method)
+function plot_trajectory(cell_trajectory::Vector{Dict{Tuple{DataType, Symbol}, Int}})
+# Initialize empty DataFrame
+ = DataFrame(cell_type = DataType[], dynamical_state = Symbol[], count = Int[],
+ df_traj = Int[])
+ time_step
+# Iterate over cell_trajectory
+ for (i, dict) in enumerate(cell_trajectory)
+ for (key, value) in dict
+ = key
+ cell_type, dynamical_state push!(df_traj, (cell_type, dynamical_state, value, i))
+ end
+ end
+
+# Convert cell type to string for plotting
+ :cell_type] = string.(df_traj[!, :cell_type])
+ df_traj[!,
+# Separate data frames based on cell type
+ = filter(row -> row[:cell_type] == "NormalCellA", df_traj)
+ df_normalA = filter(row -> row[:cell_type] == "NormalCellB", df_traj)
+ df_normalB = filter(row -> row[:cell_type] == "CancerCellA", df_traj)
+ df_cancerA = filter(row -> row[:cell_type] == "CancerCellB", df_traj)
+ df_cancerB
+# Initialize plot
+ = plot()
+ p
+# Add lines for each cell type and dynamical state
+ for df_cell in [df_normalA, df_normalB, df_cancerA, df_cancerB]
+ for state in unique(df_cell[!, :dynamical_state])
+ = filter(row -> row[:dynamical_state] == state, df_cell)
+ df_state plot!(p, df_state[!, :time_step], df_state[!, :count],
+ = "$(df_state[1, :cell_type]) - $(state)")
+ label end
+ end
+
+# Show plot
+ return p
+ end
plot_trajectory (generic function with 1 method)
+function init_trajectory()
+# Initialize an empty array to store the states of the Petri net at each time step
+ = Dict{Tuple{DataType, Symbol}, Int}[]
+ trajectory
+return trajectory
+ end
+
+function append_to_trajectory!(trajectory, pn)
+# Append the current state of the Petri net to the trajectory
+ push!(trajectory, count_cells(pn))
+ end
append_to_trajectory! (generic function with 1 method)
+run a simulation
+# Initialize Petri net
+= 500
+ num_cells = 5
+ max_active_time = init_petri_net(num_cells, max_active_time)
+ pn
+# Define the time range
+= (0.0, 30.0)
+ tspan
+# Initialize trajectory
+= init_trajectory()
+ traj
+# Run the simulation
+for t in tspan[1]:tspan[2]
+update_petri_net(pn)
+ append_to_trajectory!(traj, pn)
+ end
+
+# Visualize the trajectory
+plot_trajectory(traj)
Multi-scale cell fit networks are a fascinating area of research that explores the intricate connections between different scales of biological systems. At the cell level, these networks examine the complex interactions between cells and their environment, while at the self-cell communication level, they delve into the intricate signaling pathways that allow cells to communicate with one another. These networks also explore the distribution of signals and the continuous dynamical systems that can occur within a single cell. All of these fascinating phenomena can be explored and analyzed using QMD files, making multi-scale cell fit networks an exciting and promising area of research for scientists and researchers alike.
+How to create a multi-scale cell fit network hierarchy using AlgebraicAgent.jl
+