diff --git a/Project.toml b/Project.toml index 66e164c..0ff143f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,10 +1,11 @@ name = "GraphDynamicalSystems" uuid = "13529e2e-ed53-56b1-bd6f-420b01fca819" authors = ["Reuben Gardos Reid <5456207+ReubenJ@users.noreply.github.com>"] -version = "0.0.5" +version = "0.0.6" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +AutoHashEquals = "15f4f7f2-30c1-5605-9d31-71845cf9641f" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" DynamicalSystemsBase = "6e36e845-645a-534a-86f2-f5d4aa5a06b4" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" @@ -12,33 +13,31 @@ HerbConstraints = "1fa96474-3206-4513-b4fa-23913f296dfc" HerbCore = "2b23ba43-8213-43cb-b5ea-38c12b45bd45" HerbGrammar = "4ef9e186-2fe5-4b24-8de7-9f7291f24af7" HerbSearch = "3008d8e8-f9aa-438a-92ed-26e9c7b4829f" +JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" +MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" MetaGraphsNext = "fa8bd995-216d-47f1-8a91-f3b68fbeb377" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - -[weakdeps] -JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" - -[extensions] -JSONExt = ["JSON", "MacroTools"] +StructUtils = "ec057cc2-7a8d-4b58-b3b3-92acb9f63b42" [compat] AbstractTrees = "0.4.5" +AutoHashEquals = "2.2.0" DocStringExtensions = "0.9.3" -DynamicalSystemsBase = "3.13.2" +DynamicalSystemsBase = "3.10" Graphs = "1.12" HerbConstraints = "0.4" HerbCore = "0.3.4" HerbGrammar = "0.6" HerbSearch = "0.4.1" -JSON = "0.21.4, 1" +JSON = "1" MLStyle = "0.4.17" MacroTools = "0.5.16" MetaGraphsNext = "0.7" Random = "1.10" SciMLBase = "2.74.1" StaticArrays = "1.9.12" +StructUtils = "2.5.1" julia = "1.10" diff --git a/ext/JSONExt.jl b/ext/JSONExt.jl deleted file mode 100644 index 96ff154..0000000 --- a/ext/JSONExt.jl +++ /dev/null @@ -1,159 +0,0 @@ -module JSONExt -import JSON - -using GraphDynamicalSystems: Asynchronous, QualitativeNetwork, default_target_function -using Graphs: SimpleDiGraph, add_edge!, add_vertex! -using MacroTools: @capture, postwalk -using MetaGraphsNext: MetaGraph, inneighbor_labels - -function nested_dicts_keys_to_lowercase(d) - if d isa AbstractDict - return Dict([lowercase(k) => nested_dicts_keys_to_lowercase(v) for (k, v) in d]) - elseif d isa AbstractVector - return [nested_dicts_keys_to_lowercase(v) for v in d] - else - return d - end -end - -function sanitize_formula(f) - # surround variable names with quotes - return replace(f, r"var\(([^\)]+)\)" => s"var(\"\1\")") -end - -function entity_name_from_in_neighbors(entity, in_neighbors) - # the formulas can reference their incoming edges - # with either the name of the neighbor entity or - # its id - e_id = tryparse(Int, entity) - - entity_name = [ - Symbol("$(name)_$id") for - (id, name, _) in in_neighbors if isnothing(e_id) ? name == entity : id == e_id - ] - - if length(entity_name) != 1 - error( - """ - Error while constructing name for entity: $entity, with in neighbors: \ - $in_neighbors. There are more than one incoming neighbor entities with the same \ - name. To fix this error, remove the erroneous relationships from the JSON file, \ - or reference the entity by id (like `var(3)`). - """, - ) - end - return only(entity_name) -end - -function create_target_function( - variable::Dict, - in_neighbor_ids::Vector{Int}, - id_to_name::Dict, - mg::MetaGraph, -) - formula = Meta.parse(sanitize_formula(variable["formula"])) - in_neighbor_names = getindex.((id_to_name,), in_neighbor_ids) - in_neighbor_types = getindex.((mg.edge_data,), in_neighbor_ids, (variable["id"],)) - in_neighbors = zip(in_neighbor_ids, in_neighbor_names, in_neighbor_types) - - if isnothing(formula) # default target function - if length(in_neighbor_ids) == 0 - @warn "$(variable["name"]) has no inputs, defaulting formula to lowest value ($(variable["rangefrom"]))." - return variable["rangefrom"] - else - activators = [ - Symbol("$(name)_$id") for - (id, name, ty) in in_neighbors if ty == "Activator" - ] - inhibitors = [ - Symbol("$(name)_$id") for - (id, name, ty) in in_neighbors if ty == "Inhibitor" - ] - return default_target_function( - variable["rangefrom"], - variable["rangeto"], - activators, - inhibitors, - ) - end - else # custom target function - return postwalk( - x -> - @capture(x, var(v_String)) ? - :($(entity_name_from_in_neighbors(v, in_neighbors))) : x, - formula, - ) - end -end - -function to_from_variable_id(r, from_to) - k = "$(from_to)variable" - k_w_id = k * "id" - - if haskey(r, k) - return r[k] - elseif haskey(r, k_w_id) - return r[k_w_id] - else - error(""" - Neither alternative key was found to retrieve the edge variable id. The \ - model file is not using the expected structure for BMA models. - """) - end -end - -function QualitativeNetwork(bma_file_path::AbstractString) - json_def = JSON.parse(read(bma_file_path, String)) - - json_def = nested_dicts_keys_to_lowercase(json_def) - model = json_def["model"] - variables = model["variables"] - relationships = model["relationships"] - - id_to_name = Dict([v["id"] => v["name"] for v in variables]) - names = [Symbol("$(v["name"])_$(v["id"])") for v in variables] - mg = MetaGraph(SimpleDiGraph(), Int, Union{Expr,Integer,Symbol}, String) - - foreach(variables) do v - id = v["id"] - name = v["name"] - # adding an empty expression: :() - # because we need to construct the interaction graph - # first before parsing the functions correctly - added = add_vertex!(mg, id, :()) - if !added - error( - """ - Failed to add the entity (\"$name\", id: #$id) from the input file while \ - constructing the QN. Check that there is only one entity in the model with \ - the id #$id. - """, - ) - end - end - - foreach(relationships) do r - from = to_from_variable_id(r, "from") - to = to_from_variable_id(r, "to") - type_of_edge = r["type"] - added = add_edge!(mg, from, to, type_of_edge) - if !added - @warn """ - Encountered a duplicate relationship between entities (from: \ - $(id_to_name[from]), #$from; to: $(id_to_name[to]), #$to) while constructing \ - the QN. - """ - end - end - - formulas = Union{Expr,Integer,Symbol}[ - create_target_function(v, collect(inneighbor_labels(mg, v["id"])), id_to_name, mg) for v in variables - ] - - # @show formulas - # formulas = Union{Expr,Integer,Symbol}[v["formula"] for v in variables] - domains = [v["rangefrom"]:v["rangeto"] for v in variables] - # - return QualitativeNetwork(names, formulas, domains; schedule = Asynchronous) -end -end diff --git a/src/GraphDynamicalSystems.jl b/src/GraphDynamicalSystems.jl index 684c110..5262512 100644 --- a/src/GraphDynamicalSystems.jl +++ b/src/GraphDynamicalSystems.jl @@ -17,6 +17,7 @@ export GraphDynamicalSystem, include("qualitative_networks.jl") export QualitativeNetwork, QN, + Entity, build_qn_grammar, update_functions_to_interaction_graph, sample_qualitative_network, @@ -24,6 +25,10 @@ export QualitativeNetwork, target_functions, interpret, create_qn_system, - default_target_function + default_target_function, + set_state!, + current_parameters + +include("io/bma.jl") end diff --git a/src/io/bma.jl b/src/io/bma.jl new file mode 100644 index 0000000..cc50be0 --- /dev/null +++ b/src/io/bma.jl @@ -0,0 +1,393 @@ +module BMA + +import GraphDynamicalSystems +import GraphDynamicalSystems: + Asynchronous, + EntityIdName, + QN, + QualitativeNetwork, + ScheduleStyle, + Synchronous, + default_target_function, + domain, + entities, + get_domain, + get_graph, + id, + name, + range_from, + range_to, + target_function, + target_functions +import Graphs: SimpleDiGraph, add_edge!, add_vertex! +import JSON +import MLStyle: @match +import MetaGraphsNext: MetaGraph, edge_labels, inneighbor_labels, labels +import StructUtils + +using DocStringExtensions +using MacroTools: @capture, postwalk + +StructUtils.@tags struct Relationship + id::Int & (json = (name = "id",),) + from::Int & (json = (name = "fromvariable",),) + to::Int & (json = (name = "tovariable",),) + type::String & (json = (name = "type",),) +end + +GraphDynamicalSystems.id(r::Relationship) = r.id +from(r::Relationship) = r.from +to(r::Relationship) = r.to +type(r::Relationship) = r.type + +StructUtils.@defaults struct Entity + target_function::Any & (json = (name = "formula",),) + id::Int & (json = (name = "id",),) + range_from::Int & (json = (name = "rangefrom",),) + range_to::Int & (json = (name = "rangeto",),) + name::String = "" & (json = (name = "name",),) +end +GraphDynamicalSystems.id(e::Entity) = e.id +target_function(e::Entity) = e.target_function +range_from(e::Entity) = e.range_from +range_to(e::Entity) = e.range_to +GraphDynamicalSystems.name(e::Entity) = e.name + +function GraphDynamicalSystems.Entity(e::Entity) + GraphDynamicalSystems.Entity( + (id(e), name(e)), + target_function(e), + range_from(e):range_to(e), + ) +end + +StructUtils.@tags struct Model + entities::Vector{Entity} & (json = (name = "variables",),) + relationships::Vector{Relationship} +end +entities(m::Model) = m.entities +relationships(m::Model) = m.relationships + +struct BMAInputFormat + model::Model + # layout is not needed here + # and thus ignored +end +model(bma::BMAInputFormat) = bma.model + +function bma_dict_to_qn(bma_model::Model) + bma_entities = GraphDynamicalSystems.Entity.(entities(bma_model)) + bma_relationships = relationships(bma_model) + + names = name.(bma_entities) + id_to_name = Dict(id.(bma_entities) .=> names) + mg = MetaGraph(SimpleDiGraph(), Int, Union{Expr,Integer,Symbol}, String) + + foreach(bma_entities) do v + # adding an empty expression: :() + # because we need to construct the interaction graph + # first before parsing the functions correctly + added = add_vertex!(mg, id(v), :()) + if !added + error( + """ + Failed to add the entity (\"$(name(v))\", id: #$(id(v))) from the input file while \ + constructing the QN. Check that there is only one entity in the model with \ + the id #$(id(v)). + """, + ) + end + end + + foreach(bma_relationships) do r + if from(r) ∉ labels(mg) || to(r) ∉ labels(mg) + error("Either the source or destination of the edge is not in the graph.") + end + added = add_edge!(mg, from(r), to(r), type(r)) + if !added + @warn """ + Could not create an edge between entities (from: \ + #$(from(r)); to: #$(to(r))) while constructing \ + the QN. + """ + end + end + + entities_with_functions = [ + GraphDynamicalSystems.Entity( + (id(e), name(e)), + create_target_function( + e, + collect(inneighbor_labels(mg, id(e))), + id_to_name, + mg, + ), + domain(e), + ) for e in bma_entities + ] + + return QualitativeNetwork(entities_with_functions; schedule = Synchronous) +end + +""" + $(SIGNATURES) + +Classify all symbols in `ex` as activators or inhibitors. + +## Examples + + +""" +function classify_activators_inhibitors(ex, sign = 1, activators = [], inhibitors = []) + (activators, inhibitors) = @match ex begin + ::Symbol => if sign == 1 + (push!(activators, ex), inhibitors) + else + (activators, push!(inhibitors, ex)) + end + ::Int => (activators, inhibitors) + Expr(:call, :(-), child) => + classify_activators_inhibitors(child, -sign, activators, inhibitors) + Expr(:call, :(-), left_child, right_child) => begin + (activators, inhibitors) = classify_activators_inhibitors( + left_child, + sign, + activators, + inhibitors, + ) + (activators, inhibitors) = classify_activators_inhibitors( + right_child, + -sign, + activators, + inhibitors, + ) + (activators, inhibitors) + end + Expr(:call, f, children...) => begin + for child in children + (activators, inhibitors) = classify_activators_inhibitors( + child, + sign, + activators, + inhibitors, + ) + end + (activators, inhibitors) + end + Expr(expr_type, _...) => error("Can't classify expression of type $expr_type") + end + + return activators, inhibitors +end + +function classify_activators_inhibitors(d::AbstractDict) + return Dict(e => classify_activators_inhibitors(fn) for (e, fn) in d) +end + +function swap_entity_names_to_var_ids(ex) + @match ex begin + ::Symbol && if (ex ∉ [:+, :-, :/, :*, :min, :max, :ceil, :floor]) + end => :(var($(parse(Int, last(rsplit(string(ex), "_"; limit = 2)))))) + Expr(:call, op, children...) => + Expr(:call, op, swap_entity_names_to_var_ids.(children)...) + _ => ex + end +end + +""" + stringify_fn(ex, lower_bound, upper_bound) + +Take an `ex` and if it's of the form of a default function, return "". +""" +function stringify_fn(ex, lower_bound, upper_bound) + if is_default_function(ex, lower_bound, upper_bound) + return "" + else + string(ex) + end +end + +function is_default_function(ex, lower_bound, upper_bound) + @match ex begin + # no inputs + -1 => true + # single activator + :(var($id)) => true + + # multiple activators + Expr(:call, :/, Expr(:call, :+, vars...), denom) && ( + if length(vars) == denom + end + ) => true + + # only inhibitor(s) + :($bound - $inh) && ( + if bound == upper_bound + end + ) => is_default_function(inh, lower_bound, upper_bound) + + # both inhibitor(s) and activator(s) + :(max($bound, $act - $inh)) && ( + if bound == lower_bound + end + ) => + is_default_function(act, lower_bound, upper_bound) && + is_default_function(inh, lower_bound, upper_bound) + _ => false + end +end + +""" + $(SIGNATURES) + +Write QN to a dictionary to output as JSON. + +Use `JSON.json(qn)` directly to convert to JSON. +""" +function qn_to_bma_dict(qn::QN{N,S,M}) where {N,S,C,G,L<:EntityIdName,M<:MetaGraph{C,G,L}} + lower_upper = extrema.(get_domain(qn)) + ids = id.(entities(qn)) + entity_names = name.(entities(qn)) + functions = [target_functions(qn)[e] for e in entities(qn)] + activator_inhibitor_pairs = classify_activators_inhibitors(target_functions(qn)) + functions = swap_entity_names_to_var_ids.(functions) + functions = stringify_fn.(functions, first.(lower_upper), last.(lower_upper)) + + variables = [ + Dict( + "RangeFrom" => d[1], + "RangeTo" => d[2], + "Id" => i, + "Formula" => f, + "Name" => n, + ) for (d, i, n, f) in zip(lower_upper, ids, entity_names, functions) + ] + relationships = [ + Dict( + "Id" => i, + "FromVariable" => id(src), + "ToVariable" => id(dst), + "Type" => let (activators, inhibitors) = activator_inhibitor_pairs[dst] + activators_transformed = EntityIdName.(activators) + inhibitors_transformed = EntityIdName.(inhibitors) + if src in activators_transformed + "Activator" + elseif src in inhibitors_transformed + "Inhibitor" + else + error( + "Malformed edge. $src not found in activators ($activators_transformed) or inhibitors ($inhibitors_transformed).", + ) + end + end, + ) for (i, (src, dst)) in enumerate(edge_labels(get_graph(qn))) + ] + output_dict = Dict( + "Model" => Dict("Variables" => variables, "Relationships" => relationships), + "Layout" => Dict( + "Variables" => + [Dict("Id" => v["Id"], "Name" => v["Name"]) for v in variables], + ), + ) + + return output_dict +end + +function sanitize_formula(f) + # surround variable names with quotes + return replace(f, r"var\(([^\)]+)\)" => s"var(\"\1\")") +end + +function entity_name_from_in_neighbors(entity, in_neighbors) + # the formulas can reference their incoming edges + # with either the name of the neighbor entity or + # its id + e_id = tryparse(Int, entity) + + entity_name = [ + Symbol("$(name)_$id") for + (id, name, _) in in_neighbors if isnothing(e_id) ? name == entity : id == e_id + ] + + if length(entity_name) != 1 + error( + """ + Error while constructing name for entity: $entity, with in neighbors: \ + $in_neighbors. There are more than one incoming neighbor entities with the same \ + name. To fix this error, remove the erroneous relationships from the JSON file, \ + or reference the entity by id (like `var(3)`). + """, + ) + end + return only(entity_name) +end + +function create_target_function( + variable::GraphDynamicalSystems.Entity{EntityIdName{S},Int}, + in_neighbor_ids::Vector{Int}, + id_to_name::Dict, + mg::MetaGraph, +) where {S} + formula = Meta.parse(sanitize_formula(target_function(variable))) + in_neighbor_names = getindex.((id_to_name,), in_neighbor_ids) + in_neighbor_types = getindex.((mg.edge_data,), in_neighbor_ids, (id(variable),)) + in_neighbors = zip(in_neighbor_ids, in_neighbor_names, in_neighbor_types) + + if isnothing(formula) # default target function + if length(in_neighbor_ids) == 0 + @warn "$(name(variable)) has no inputs, defaulting formula to -1" + return -1 + else + activators = [ + Symbol("$(name)_$id") for + (id, name, ty) in in_neighbors if ty == "Activator" + ] + inhibitors = [ + Symbol("$(name)_$id") for + (id, name, ty) in in_neighbors if ty == "Inhibitor" + ] + return default_target_function( + range_from(variable), + range_to(variable), + activators, + inhibitors, + ) + end + else # custom target function + return postwalk( + x -> + @capture(x, var(v_String)) ? + :($(entity_name_from_in_neighbors(v, in_neighbors))) : x, + formula, + ) + end +end + +function nested_dicts_keys_to_lowercase(d) + if d isa AbstractDict + return Dict([lowercase(k) => nested_dicts_keys_to_lowercase(v) for (k, v) in d]) + elseif d isa AbstractVector + return [nested_dicts_keys_to_lowercase(v) for v in d] + else + return d + end +end + +function QualitativeNetwork(bma_file_path::AbstractString) + bma_def_raw = JSON.parsefile(bma_file_path) + bma_def_raw = nested_dicts_keys_to_lowercase(bma_def_raw) + bma_def = JSON.parse(JSON.json(bma_def_raw), BMAInputFormat) + bma_model = model(bma_def) + + return bma_dict_to_qn(bma_model) +end + +function JSON.json(qn::QualitativeNetwork) + return JSON.json(qn_to_bma_dict(qn); omit_empty = false) +end +JSON.json(io_or_filename, qn::T) where {T<:QualitativeNetwork} = + JSON.json(io_or_filename, qn_to_bma_dict(qn); omit_empty = false) +JSON.json(io::IO, qn::T) where {T<:QualitativeNetwork} = + JSON.json(io, qn_to_bma_dict(qn); omit_empty = false) + +end diff --git a/src/qualitative_networks.jl b/src/qualitative_networks.jl index bb432e2..c97d0cf 100644 --- a/src/qualitative_networks.jl +++ b/src/qualitative_networks.jl @@ -1,14 +1,17 @@ -import DynamicalSystemsBase: get_state, set_state! +import AutoHashEquals: @auto_hash_equals +import DynamicalSystemsBase: current_parameters, get_state, set_state! +import JSON import SciMLBase -using AbstractTrees: Leaves -using DynamicalSystemsBase: ArbitrarySteppable, current_parameters, initial_state +using AbstractTrees: Leaves, PostOrderDFS +using DynamicalSystemsBase: ArbitrarySteppable, initial_state +using Graphs: AbstractGraph, SimpleDiGraph, add_edge!, add_vertex!, ne using HerbConstraints: DomainRuleNode, Forbidden, Ordered, Unique, VarNode, addconstraint! using HerbCore: AbstractGrammar, RuleNode, get_rule using HerbGrammar: @csgrammar, add_rule!, rulenode2expr using HerbSearch: rand using MLStyle: @match -using MetaGraphsNext: MetaGraph, SimpleDiGraph, add_edge!, labels, nv +using MetaGraphsNext: MetaGraph, add_edge!, edge_labels, inneighbor_labels, labels, nv using StaticArrays: MVector, SVector const base_qn_grammar = @csgrammar begin @@ -100,7 +103,7 @@ function build_qn_grammar( # Only use each of the entities once per function n_consts = length(constants) - entities = n_original_rules+1:length(g.rules)-n_consts + entities = (n_original_rules+1):(length(g.rules)-n_consts) if unique_constr addconstraint!.((g,), Unique.(entities)) @@ -210,39 +213,112 @@ function default_target_function( end end -struct Entity{I} +abstract type EntityLabel end + +struct EntityId <: EntityLabel + id::Int +end +id(e::EntityId) = e.id + +struct EntityName{S} <: EntityLabel + name::S +end +name(e::EntityName) = e.name + +@auto_hash_equals struct EntityIdName{S} <: EntityLabel + id::Int + name::S +end +function EntityIdName(s::Symbol) + en_str = string(s) + name_id_str_split = rsplit(en_str, "_"; limit = 2) + if length(name_id_str_split) != 2 + error("""Failed to convert the Symbol $s to an EntityIdName. \ + Expecting an EntityName with a name in the form of "Name_00".""") + end + (name_str, id_str) = name_id_str_split + + id_val = tryparse(Int, id_str) + if isnothing(id_val) + error("""Entity name ($s) contained an underscore but the \ + content after the underscore ($id_str) could not be parsed as \ + an integer to convert it to an ID.""") + end + + return EntityIdName(id_val, string(name_str)) +end +EntityIdName{String}(s::Symbol) = EntityIdName(s) +id(e::EntityIdName) = e.id +name(e::EntityIdName) = e.name +combined_name(e::EntityIdName) = Symbol("$(name(e))_$(id(e))") + +struct Entity{I<:EntityLabel,D} + label::I target_function::Any - # _f::Any - domain::UnitRange{I} + domain::UnitRange{D} +end +Entity(name::Symbol, args...) = Entity(EntityName(name), args...) +Entity(id::Int, args...) = Entity(EntityId(id), args...) +Entity((id, name), args...) = Entity(EntityIdName(id, name), args...) + +label(e::Entity) = e.label +id(e::Entity) = id(label(e)) +name(e::Entity) = name(label(e)) +target_function(e::Entity) = e.target_function +domain(e::Entity) = e.domain +range_from(e::Entity) = first(domain(e)) +range_to(e::Entity) = last(domain(e)) + +function get_used_entities(fn, entities_in_model::Vector{<:Entity{<:EntityIdName}}) + filter(in(combined_name.(label.(entities_in_model))), collect(Leaves(fn))) end -get_target_function(e::Entity) = e.target_function -get_domain(e::Entity) = e.domain +function get_used_entities(fn, entities_in_model) + filter(in(name.(entities_in_model)), collect(Leaves(fn))) +end """ $(TYPEDSIGNATURES) """ function update_functions_to_interaction_graph( - entities::AbstractVector{Symbol}, - update_functions::AbstractVector{Union{Integer,Symbol,Expr}}, - domains::AbstractVector{UnitRange{Int}}; + entities_in_model::AbstractVector{<:E}, schedule = Synchronous, -) +) where {EntityLabelType,E<:Entity{EntityLabelType}} graph = MetaGraph( SimpleDiGraph(); - label_type = Symbol, - vertex_data_type = Entity{Int}, + label_type = EntityLabelType, + vertex_data_type = E, graph_data = schedule, ) + if !allunique(label.(entities_in_model)) + val_counts = Dict() + for e in entities_in_model + val_counts[name(e)] = append!(get(val_counts, name(e), []), [e]) + end + duplicates = [v for v in values(val_counts) if length(v) > 1] + error("""The QN implementation only supports models with unique \ + entity name/id combinations. + + Duplicates: - for (entity, fn, domain) in zip(entities, update_functions, domains) - graph[entity] = Entity{Int}(fn, domain) + $duplicates""") end - for (dst, f) in zip(entities, update_functions) - input_entities = collect(Leaves(f)) - for src in input_entities - add_edge!(graph, src, dst) + for entity in entities_in_model + graph[label(entity)] = entity + end + + for dst in entities_in_model + input_entities = get_used_entities(target_function(dst), entities_in_model) + for src in EntityLabelType.(input_entities) + dst_label = label(dst) + l = collect(labels(graph)) + if !(src ∈ l && dst_label ∈ l) + error( + """Could not add edge from $src to $(dst_label). The vertex labels in the graph are currently $(collect(labels(graph))).""", + ) + end + add_edge!(graph, src, dst_label) end end @@ -263,7 +339,7 @@ function sample_qualitative_network( rulenode2expr(rand(RuleNode, g, :Val, max_eq_depth), g) for _ in entities ] - qn = QualitativeNetwork(entities, update_fns, domains; schedule = schedule) + qn = QualitativeNetwork(Entity.(entities, update_fns, domains); schedule = schedule) return qn end @@ -288,39 +364,44 @@ Systems that include the model semantics wrap around this struct with an from [`DynamicalSystems`](https://juliadynamics.github.io/DynamicalSystems.jl/stable/). See [`create_qn_system`](@ref) for an example. """ -struct QualitativeNetwork{N,S} <: GraphDynamicalSystem{N,S} +struct QualitativeNetwork{ + N, + Schedule, + M<:MetaGraph{Int,<:SimpleDiGraph,<:EntityLabel,<:Entity}, +} <: GraphDynamicalSystem{N,Schedule} "Graph containing the topology and target functions of the network" - graph::MetaGraph + graph::M "State of the network" state::MVector{N,Int} function QualitativeNetwork(graph, state; schedule = Synchronous) N = nv(graph) - return new{N,schedule()}(graph, state) + if N != length(state) + error("""The number of entities in the model ($N) must match the \ + length of the provided state vector ($(length(state))).""") + end + + return new{N,schedule(),typeof(graph)}(graph, state) end end function QualitativeNetwork( - entities::AbstractVector{Symbol}, - functions::AbstractVector{Union{Integer,Symbol,Expr}}, - domains; + entities::AbstractVector{<:Entity}; state = nothing, schedule = Synchronous, ) - graph = update_functions_to_interaction_graph( - entities, - functions, - domains; - schedule = schedule, - ) + graph = update_functions_to_interaction_graph(entities, schedule) if isnothing(state) - state = rand.(domains) + state = rand.(domain.(entities)) end return QualitativeNetwork(graph, state; schedule) end +QualitativeNetwork(entities::AbstractVector{<:AbstractString}, args...; kwargs...) = + QualitativeNetwork(Symbol.(entities), args...; kwargs...) + """ $(TYPEDSIGNATURES) @@ -333,11 +414,11 @@ const QN = QualitativeNetwork Get the domain of the entity `entity_label` in `qn`. """ -function get_domain(qn::QN, entity_label::Symbol) +function get_domain(qn::QN, entity_label) graph = get_graph(qn) entity = graph[entity_label] - return get_domain(entity) + return domain(entity) end """ @@ -350,37 +431,60 @@ function get_domain(qn::QN) end function _get_entity_index(qn::QN, entity) - return findfirst(isequal(entity), entities(qn)) + # Ugly, we shouldn't pass entities around as Symbols anymore + # but this works for now + # actually, doesn't because there are sometimes variables with underscores... time to rewrite more + entity_proc = if entity isa EntityName + split_res = rsplit(String(name(entity)), "_"; limit = 2) + entity_proc = if length(split_res) == 2 + (entity_str, id_str) = split_res + id_val = tryparse(Int, id_str) + if !isnothing(id_val) + EntityIdName(id_val, entity_str) + else + entity + end + else + entity + end + else + entity + end + i = findfirst(isequal(entity_proc), entities(qn)) + if isnothing(i) + error("""Tried to get the state of $entity_proc but could not retrieve it. \ + The entities in the model are $(entities(qn))""") + end + return i end - """ $(TYPEDSIGNATURES) """ function target_functions(qn::QN) return Dict([ - c => get_target_function(entity) for - (c, (_, entity)) in get_graph(qn).vertex_properties + c => target_function(entity) for (c, (_, entity)) in get_graph(qn).vertex_properties ]) end """ $(TYPEDSIGNATURES) """ -function get_state(qn::QN, component) - i = _get_entity_index(qn, component) +function get_state(qn::QN, entity) + i = _get_entity_index(qn, entity) return qn.state[i] end +get_state(qn::QN, entity::Symbol) = get_state(qn, EntityName(entity)) -function _set_state!(qn::QN, component::Symbol, value::Integer) - i = _get_entity_index(qn::QN, component::Symbol) +function _set_state!(qn::QN, entity, value::Integer) + i = _get_entity_index(qn::QN, entity) qn.state[i] = value end """ $(TYPEDSIGNATURES) """ -function set_state!(qn::QN, entity::Symbol, value::Integer) +function set_state!(qn::QN, entity, value::Integer) max_for_entity = maximum(get_domain(qn, entity)) if value > max_for_entity error( @@ -391,6 +495,14 @@ function set_state!(qn::QN, entity::Symbol, value::Integer) _set_state!(qn, entity, value) end +function set_state!(qn::QN, entity::Symbol, value::Integer) + set_state!(qn, EntityName(entity), value) +end + +function set_state!(qn::QN, values) + set_state!.((qn,), entities(qn), values) +end + """ $(TYPEDSIGNATURES) @@ -436,12 +548,7 @@ function limit_change( return limited_value end -""" - $(TYPEDSIGNATURES) -""" -function async_qn_step!(qn::QN) - entity_labels = collect(labels(qn.graph)) - entity = rand(entity_labels) +function _compute_next_state!(qn::QN, entity) (min_level, max_level) = extrema(get_domain(qn, entity)) t = target_functions(qn)[entity] old_state = get_state(qn, entity) @@ -449,18 +556,29 @@ function async_qn_step!(qn::QN) new_state = isnan(new_state) ? min_level : new_state new_state = isinf(new_state) ? max_level : new_state limited_state = limit_change(old_state, floor(Int, new_state), min_level, max_level) - set_state!(qn, entity, limited_state) +end + +""" + $(TYPEDSIGNATURES) +""" +function async_qn_step!(qn::QN) + entity_labels = entities(qn) + entity = rand(entity_labels) + next_state = _compute_next_state!(qn, entity) + set_state!(qn, entity, next_state) end """ $(TYPEDSIGNATURES) """ function sync_qn_step!(qn::QN) - throw(ErrorException("Synchronous step function not yet implemented")) + next_states = _compute_next_state!.((qn,), entities(qn)) + set_state!.((qn,), entities(qn), next_states) end extract_state(model::QN) = model.state extract_parameters(model::QN) = model.graph +current_parameters(model::QN) = model.graph reset_model!(model::QN, u, _) = model.state .= u function SciMLBase.reinit!( @@ -489,6 +607,6 @@ function create_qn_system(qn::QN) extract_state, extract_parameters, reset_model!, - isdeterministic = false, + isdeterministic = get_schedule(qn) == Synchronous(), ) end diff --git a/test/Project.toml b/test/Project.toml index 55eba4e..38d34ab 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -4,6 +4,7 @@ Attractors = "f3fd9213-ca85-4dba-9dfd-7fc91308fec7" DynamicalSystemsBase = "6e36e845-645a-534a-86f2-f5d4aa5a06b4" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" HerbCore = "2b23ba43-8213-43cb-b5ea-38c12b45bd45" +IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e" JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" MetaGraphsNext = "fa8bd995-216d-47f1-8a91-f3b68fbeb377" diff --git a/test/io/bma_test.jl b/test/io/bma_test.jl new file mode 100644 index 0000000..326b2c2 --- /dev/null +++ b/test/io/bma_test.jl @@ -0,0 +1,147 @@ +@testitem "Construct default target functions" begin + lower_bound = 0 + upper_bound = 4 + activators = [:A, :B, :C] + inhibitors = [:D, :E, :F] + + @test default_target_function(lower_bound, upper_bound, activators, inhibitors) == + :(max($lower_bound, (A + B + C) / 3 - (D + E + F) / 3)) + + activators = [:A, :B] + inhibitors = [:D] + + @test default_target_function(lower_bound, upper_bound, activators, inhibitors) == + :(max($lower_bound, (A + B) / 2 - D)) + + activators = [] + inhibitors = [:D] + + @test default_target_function(lower_bound, upper_bound, activators, inhibitors) == + :($upper_bound - D) + + activators = [:A] + inhibitors = [] + + @test default_target_function(lower_bound, upper_bound, activators, inhibitors) == :(A) + + @test_throws r"no activators or inhibitors" default_target_function(0, 4) +end + +@testitem "Load from BMA" begin + using DynamicalSystemsBase: step! + bma_models_path = joinpath(@__DIR__, "..", "resources", "bma_models") + good_models = joinpath(bma_models_path, "well_formed_examples") + + for model_path in readdir(good_models; join = true) + if occursin("Skin1D", model_path) + @test_broken QN(model_path) isa GraphDynamicalSystem + else + qn = QN(model_path) + @test qn isa GraphDynamicalSystem + step!(create_qn_system(qn), 100) + end + end + + bad_models = joinpath(bma_models_path, "error_examples") + + @test_throws "Failed to add" QN(joinpath(bad_models, "duplicate_entity_ids.json")) + @test_throws "Error while constructing name for entity" QN( + joinpath(bad_models, "multiple_incoming_edges_same_name.json"), + ) +end + +@testitem "Save to BMA" begin + import MetaGraphsNext: edge_labels, labels + import GraphDynamicalSystems.BMA: is_default_function + using JSON + + function test_json_roundtrip(model_path::AbstractString) + qn = QN(model_path) + @test length(labels(get_graph(qn))) > 0 + @test length(edge_labels(get_graph(qn))) > 0 + + output_str = JSON.json(qn) + output_dict = JSON.parse(output_str) + orig_dict = JSON.parse(read(model_path, String)) + + if !haskey(orig_dict, "Model") + @warn "Skipping test for file $model_path for now because of the nonstandard key names" + return + end + + @test haskey(output_dict, "Model") + + model_dict = output_dict["Model"] + + @test haskey(model_dict, "Variables") + variables = model_dict["Variables"] + for (orig_v, v) in zip(orig_dict["Model"]["Variables"], variables) + if v["Name"] == "" + @test !haskey(orig_v, "Name") + else + @test v["Name"] == orig_v["Name"] + end + orig_f = Meta.parse(orig_v["Formula"]) + f = Meta.parse(v["Formula"]) + + if is_default_function(orig_f, orig_v["RangeFrom"], orig_v["RangeTo"]) + @test isnothing(f) + else + @test orig_f == f + end + end + orig_variables_no_f = [ + Dict(k => v for (k, v) in var if k != "Formula" && k != "Name") for + var in orig_dict["Model"]["Variables"] + ] + output_variables_no_f = [ + Dict(k => v for (k, v) in var if k != "Formula" && k != "Name") for + var in variables + ] + @test orig_variables_no_f == output_variables_no_f + + @test haskey(model_dict, "Relationships") + relationships = model_dict["Relationships"] + orig_relationships_no_id = [ + Dict(k => v for (k, v) in rel if k != "Id") for + rel in orig_dict["Model"]["Relationships"] + ] + output_relationships_no_id = + [Dict(k => v for (k, v) in rel if k != "Id") for rel in relationships] + + # At least check that we are not creating new relationships out of nothing + @test length(setdiff(output_relationships_no_id, orig_relationships_no_id)) == 0 + + if occursin("VPC.json", model_path) + @test_broken false # VPC has some weird relationships that are not used in the target functions + else + @test Set(orig_relationships_no_id) == Set(output_relationships_no_id) + end + end + bma_models_path = joinpath(@__DIR__, "..", "resources", "bma_models") + good_models = joinpath(bma_models_path, "well_formed_examples") + + # just another reminder that the "Skin1D" example isn't working with this test + @test_broken false + + for model_path in filter(!contains(r"Skin1D"), readdir(good_models; join = true)) + test_json_roundtrip(model_path) + end +end + +@testitem "is default function" begin + import IterTools: subsets + import GraphDynamicalSystems.BMA: + is_default_function, default_target_function, swap_entity_names_to_var_ids + combinations = Iterators.filter( + x -> !all(isempty.(x)), + Iterators.product(subsets([:A_1, :B_2, :X_5, :Y_6]), subsets([:C_3, :D_4, :Z_7])), + ) + activators = first.(combinations) + inhibitors = last.(combinations) + + fns = default_target_function.(0, 4, activators, inhibitors) + for f in swap_entity_names_to_var_ids.(fns) + @test is_default_function(f, 0, 4) + end +end diff --git a/test/qn_test.jl b/test/qn_test.jl index 6e4b895..71c203a 100644 --- a/test/qn_test.jl +++ b/test/qn_test.jl @@ -23,16 +23,19 @@ end end @testitem "QN Graph Correctness" begin - entities = [:a, :b, :c] + import GraphDynamicalSystems: EntityName + import MetaGraphsNext: edge_labels + + entity_labels = [:a, :b, :c] target_fns = Union{Expr,Integer,Symbol}[:(-c), :a, :b] domains = [0:2 for _ = 1:3] - qn = QN(entities, target_fns, domains) + qn = QN(Entity.(entity_labels, target_fns, domains)) g = get_graph(qn) - @test haskey(g, :c, :a) - @test haskey(g, :a, :b) - @test haskey(g, :b, :c) + @test haskey(g, EntityName(:c), EntityName(:a)) + @test haskey(g, EntityName(:a), EntityName(:b)) + @test haskey(g, EntityName(:b), EntityName(:c)) end @testitem "QN Sampling" setup = [RandomSetup, ExampleQN] begin @@ -44,7 +47,8 @@ end end @testitem "QN properties, fields" setup = [RandomSetup, ExampleQN] begin - using DynamicalSystemsBase: step!, get_state, set_state! + import GraphDynamicalSystems: EntityName + using DynamicalSystemsBase: get_state, set_state!, step! set_state!(qn, :A, 1) @@ -59,12 +63,6 @@ end @test_throws r"max" set_state!(qn, :A, 6) end -@testitem "QN Construction" setup = [RandomSetup, ExampleQN] begin - initial_state_beyond_domain = [5, 5, 5] - - # @test_throws r"<=" set_state!(qn, en -end - @testitem "Target Function" setup = [RandomSetup, ExampleQN] begin using DynamicalSystemsBase: step!, get_state, set_state! set_state!(qn, :A, 1) @@ -125,54 +123,3 @@ end basins = basins_of_attraction(mapper, grid) end - -@testitem "Construct default target functions" begin - lower_bound = 0 - upper_bound = 4 - activators = [:A, :B, :C] - inhibitors = [:D, :E, :F] - - @test default_target_function(lower_bound, upper_bound, activators, inhibitors) == - :(max($lower_bound, (A + B + C) / 3 - (D + E + F) / 3)) - - activators = [:A, :B] - inhibitors = [:D] - - @test default_target_function(lower_bound, upper_bound, activators, inhibitors) == - :(max($lower_bound, (A + B) / 2 - D)) - - activators = [] - inhibitors = [:D] - - @test default_target_function(lower_bound, upper_bound, activators, inhibitors) == - :($upper_bound - D) - - activators = [:A] - inhibitors = [] - - @test default_target_function(lower_bound, upper_bound, activators, inhibitors) == :(A) - - @test_throws r"no activators or inhibitors" default_target_function(0, 4) -end - -@testitem "Load from BMA" setup = [RandomSetup] begin - using JSON - using DynamicalSystemsBase: step! - bma_models_path = joinpath(@__DIR__, "resources", "bma_models") - good_models = joinpath(bma_models_path, "well_formed_examples") - - for model_path in readdir(good_models; join = true) - qn = QN(model_path) - @test qn isa GraphDynamicalSystem - step!(create_qn_system(qn), 100) - end - - bad_models = joinpath(bma_models_path, "error_examples") - - @test_throws "Neither alternative" QN(joinpath(bad_models, "bad_edge_key.json")) - @test_throws "Failed to add" QN(joinpath(bad_models, "duplicate_entity_ids.json")) - @test_throws "Error while constructing name for entity" QN( - joinpath(bad_models, "multiple_incoming_edges_same_name.json"), - ) - -end diff --git a/test/resources/bma_models/well_formed_examples/VPC.json b/test/resources/bma_models/well_formed_examples/VPC.json new file mode 100644 index 0000000..b44779e --- /dev/null +++ b/test/resources/bma_models/well_formed_examples/VPC.json @@ -0,0 +1,2384 @@ +{ + "Model": { + "Name": "VPC", + "Variables": [ + { + "Id": 95, + "RangeFrom": 0, + "RangeTo": 0, + "Formula": "" + }, + { + "Id": 1, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2" + }, + { + "Id": 2, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(23))*(max(var(7) - 1,0) + 1)))" + }, + { + "Id": 3, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(1),var(21))" + }, + { + "Id": 4, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(3))*(max(var(9) - 1,0) + 1)))" + }, + { + "Id": 5, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(4))*(max(var(9)-1,0)+1)))" + }, + { + "Id": 6, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(5))*(max(var(9)-1,0)+1)))" + }, + { + "Id": 7, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(var(6)+var(8),2)" + }, + { + "Id": 8, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(24) - var(9) - 1,0)" + }, + { + "Id": 9, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(2 - var(3),1)*var(2)" + }, + { + "Id": 10, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(var(7),var(1))" + }, + { + "Id": 21, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2 - var(22)" + }, + { + "Id": 22, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2" + }, + { + "Id": 23, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(31),var(31))" + }, + { + "Id": 24, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(34),var(34))" + }, + { + "Id": 25, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "1" + }, + { + "Id": 26, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(89))*(max(var(31) - 1,0) + 1)))" + }, + { + "Id": 27, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(25),var(87))" + }, + { + "Id": 28, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(27))*(max(var(33) - 1,0) + 1)))" + }, + { + "Id": 29, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(28))*(max(var(33)-1,0)+1)))" + }, + { + "Id": 30, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(29))*(max(var(33)-1,0)+1)))" + }, + { + "Id": 31, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(var(30)+var(32),2)" + }, + { + "Id": 32, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(90) - var(33) - 1,0)" + }, + { + "Id": 33, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(2 - var(27),1)*var(26)" + }, + { + "Id": 34, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(var(31),var(25))" + }, + { + "Id": 35, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "0" + }, + { + "Id": 36, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(93))*(max(var(41) - 1,0) + 1)))" + }, + { + "Id": 37, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(35),var(91))" + }, + { + "Id": 38, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(37))*(max(var(43) - 1,0) + 1)))" + }, + { + "Id": 39, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(38))*(max(var(43)-1,0)+1)))" + }, + { + "Id": 40, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(39))*(max(var(43)-1,0)+1)))" + }, + { + "Id": 41, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(var(40)+var(42),2)" + }, + { + "Id": 42, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(94) - var(43) - 1,0)" + }, + { + "Id": 43, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(2 - var(37),1)*var(36)" + }, + { + "Id": 44, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(var(41),var(35))" + }, + { + "Id": 45, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "1" + }, + { + "Id": 46, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(77))*(max(var(51) - 1,0) + 1)))" + }, + { + "Id": 47, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(45),var(75))" + }, + { + "Id": 48, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(47))*(max(var(53) - 1,0) + 1)))" + }, + { + "Id": 49, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(48))*(max(var(53)-1,0)+1)))" + }, + { + "Id": 50, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(49))*(max(var(53)-1,0)+1)))" + }, + { + "Id": 51, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(var(50)+var(52),2)" + }, + { + "Id": 52, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(78) - var(53) - 1,0)" + }, + { + "Id": 53, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(2 - var(47),1)*var(46)" + }, + { + "Id": 54, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(var(51),var(45))" + }, + { + "Id": 55, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "0" + }, + { + "Id": 56, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(81))*(max(var(61) - 1,0) + 1)))" + }, + { + "Id": 57, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(55),var(79))" + }, + { + "Id": 58, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(57))*(max(var(63) - 1,0) + 1)))" + }, + { + "Id": 59, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(58))*(max(var(63)-1,0)+1)))" + }, + { + "Id": 60, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(59))*(max(var(63)-1,0)+1)))" + }, + { + "Id": 61, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(var(60)+var(62),2)" + }, + { + "Id": 62, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(82) - var(63) - 1,0)" + }, + { + "Id": 63, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(2 - var(57),1)*var(56)" + }, + { + "Id": 64, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(var(61),var(55))" + }, + { + "Id": 65, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "0" + }, + { + "Id": 66, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(85))*(max(var(71) - 1,0) + 1)))" + }, + { + "Id": 67, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(65),var(83))" + }, + { + "Id": 68, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(67))*(max(var(73) - 1,0) + 1)))" + }, + { + "Id": 69, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(68))*(max(var(73)-1,0)+1)))" + }, + { + "Id": 70, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,2 - ((2 - var(69))*(max(var(73)-1,0)+1)))" + }, + { + "Id": 71, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(var(70)+var(72),2)" + }, + { + "Id": 72, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(86) - var(73) - 1,0)" + }, + { + "Id": 73, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(2 - var(67),1)*var(66)" + }, + { + "Id": 74, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "min(var(71),var(65))" + }, + { + "Id": 75, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2 - var(76)" + }, + { + "Id": 76, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2" + }, + { + "Id": 77, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(7),var(7))" + }, + { + "Id": 78, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(10),var(10))" + }, + { + "Id": 79, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2 - var(80)" + }, + { + "Id": 80, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2" + }, + { + "Id": 81, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(51),var(51))" + }, + { + "Id": 82, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(54),var(54))" + }, + { + "Id": 83, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2 - var(84)" + }, + { + "Id": 84, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2" + }, + { + "Id": 85, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,var(61))" + }, + { + "Id": 86, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(0,var(64))" + }, + { + "Id": 87, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2 - var(88)" + }, + { + "Id": 88, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2" + }, + { + "Id": 89, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(7),var(7))" + }, + { + "Id": 90, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(10),var(10))" + }, + { + "Id": 91, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2 - var(92)" + }, + { + "Id": 92, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "2" + }, + { + "Id": 93, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(31),0)" + }, + { + "Id": 94, + "RangeFrom": 0, + "RangeTo": 2, + "Formula": "max(var(34),0)" + } + ], + "Relationships": [ + { + "Id": 2, + "FromVariable": 3, + "ToVariable": 4, + "Type": "Activator" + }, + { + "Id": 3, + "FromVariable": 4, + "ToVariable": 5, + "Type": "Activator" + }, + { + "Id": 4, + "FromVariable": 5, + "ToVariable": 6, + "Type": "Activator" + }, + { + "Id": 5, + "FromVariable": 6, + "ToVariable": 7, + "Type": "Activator" + }, + { + "Id": 6, + "FromVariable": 8, + "ToVariable": 7, + "Type": "Activator" + }, + { + "Id": 8, + "FromVariable": 3, + "ToVariable": 9, + "Type": "Inhibitor" + }, + { + "Id": 9, + "FromVariable": 9, + "ToVariable": 4, + "Type": "Inhibitor" + }, + { + "Id": 10, + "FromVariable": 9, + "ToVariable": 5, + "Type": "Inhibitor" + }, + { + "Id": 11, + "FromVariable": 9, + "ToVariable": 6, + "Type": "Inhibitor" + }, + { + "Id": 12, + "FromVariable": 9, + "ToVariable": 8, + "Type": "Inhibitor" + }, + { + "Id": 14, + "FromVariable": 7, + "ToVariable": 10, + "Type": "Activator" + }, + { + "Id": 30, + "FromVariable": 22, + "ToVariable": 21, + "Type": "Inhibitor" + }, + { + "Id": 32, + "FromVariable": 27, + "ToVariable": 28, + "Type": "Activator" + }, + { + "Id": 33, + "FromVariable": 28, + "ToVariable": 29, + "Type": "Activator" + }, + { + "Id": 34, + "FromVariable": 29, + "ToVariable": 30, + "Type": "Activator" + }, + { + "Id": 35, + "FromVariable": 30, + "ToVariable": 31, + "Type": "Activator" + }, + { + "Id": 36, + "FromVariable": 32, + "ToVariable": 31, + "Type": "Activator" + }, + { + "Id": 38, + "FromVariable": 27, + "ToVariable": 33, + "Type": "Inhibitor" + }, + { + "Id": 39, + "FromVariable": 33, + "ToVariable": 28, + "Type": "Inhibitor" + }, + { + "Id": 40, + "FromVariable": 33, + "ToVariable": 29, + "Type": "Inhibitor" + }, + { + "Id": 41, + "FromVariable": 33, + "ToVariable": 30, + "Type": "Inhibitor" + }, + { + "Id": 42, + "FromVariable": 33, + "ToVariable": 32, + "Type": "Inhibitor" + }, + { + "Id": 44, + "FromVariable": 31, + "ToVariable": 34, + "Type": "Activator" + }, + { + "Id": 47, + "FromVariable": 37, + "ToVariable": 38, + "Type": "Activator" + }, + { + "Id": 48, + "FromVariable": 38, + "ToVariable": 39, + "Type": "Activator" + }, + { + "Id": 49, + "FromVariable": 39, + "ToVariable": 40, + "Type": "Activator" + }, + { + "Id": 50, + "FromVariable": 40, + "ToVariable": 41, + "Type": "Activator" + }, + { + "Id": 51, + "FromVariable": 42, + "ToVariable": 41, + "Type": "Activator" + }, + { + "Id": 53, + "FromVariable": 37, + "ToVariable": 43, + "Type": "Inhibitor" + }, + { + "Id": 54, + "FromVariable": 43, + "ToVariable": 38, + "Type": "Inhibitor" + }, + { + "Id": 55, + "FromVariable": 43, + "ToVariable": 39, + "Type": "Inhibitor" + }, + { + "Id": 56, + "FromVariable": 43, + "ToVariable": 40, + "Type": "Inhibitor" + }, + { + "Id": 57, + "FromVariable": 43, + "ToVariable": 42, + "Type": "Inhibitor" + }, + { + "Id": 59, + "FromVariable": 41, + "ToVariable": 44, + "Type": "Activator" + }, + { + "Id": 62, + "FromVariable": 47, + "ToVariable": 48, + "Type": "Activator" + }, + { + "Id": 63, + "FromVariable": 48, + "ToVariable": 49, + "Type": "Activator" + }, + { + "Id": 64, + "FromVariable": 49, + "ToVariable": 50, + "Type": "Activator" + }, + { + "Id": 65, + "FromVariable": 50, + "ToVariable": 51, + "Type": "Activator" + }, + { + "Id": 66, + "FromVariable": 52, + "ToVariable": 51, + "Type": "Activator" + }, + { + "Id": 68, + "FromVariable": 47, + "ToVariable": 53, + "Type": "Inhibitor" + }, + { + "Id": 69, + "FromVariable": 53, + "ToVariable": 48, + "Type": "Inhibitor" + }, + { + "Id": 70, + "FromVariable": 53, + "ToVariable": 49, + "Type": "Inhibitor" + }, + { + "Id": 71, + "FromVariable": 53, + "ToVariable": 50, + "Type": "Inhibitor" + }, + { + "Id": 72, + "FromVariable": 53, + "ToVariable": 52, + "Type": "Inhibitor" + }, + { + "Id": 74, + "FromVariable": 51, + "ToVariable": 54, + "Type": "Activator" + }, + { + "Id": 77, + "FromVariable": 57, + "ToVariable": 58, + "Type": "Activator" + }, + { + "Id": 78, + "FromVariable": 58, + "ToVariable": 59, + "Type": "Activator" + }, + { + "Id": 79, + "FromVariable": 59, + "ToVariable": 60, + "Type": "Activator" + }, + { + "Id": 80, + "FromVariable": 60, + "ToVariable": 61, + "Type": "Activator" + }, + { + "Id": 81, + "FromVariable": 62, + "ToVariable": 61, + "Type": "Activator" + }, + { + "Id": 83, + "FromVariable": 57, + "ToVariable": 63, + "Type": "Inhibitor" + }, + { + "Id": 84, + "FromVariable": 63, + "ToVariable": 58, + "Type": "Inhibitor" + }, + { + "Id": 85, + "FromVariable": 63, + "ToVariable": 59, + "Type": "Inhibitor" + }, + { + "Id": 86, + "FromVariable": 63, + "ToVariable": 60, + "Type": "Inhibitor" + }, + { + "Id": 87, + "FromVariable": 63, + "ToVariable": 62, + "Type": "Inhibitor" + }, + { + "Id": 89, + "FromVariable": 61, + "ToVariable": 64, + "Type": "Activator" + }, + { + "Id": 92, + "FromVariable": 67, + "ToVariable": 68, + "Type": "Activator" + }, + { + "Id": 93, + "FromVariable": 68, + "ToVariable": 69, + "Type": "Activator" + }, + { + "Id": 94, + "FromVariable": 69, + "ToVariable": 70, + "Type": "Activator" + }, + { + "Id": 95, + "FromVariable": 70, + "ToVariable": 71, + "Type": "Activator" + }, + { + "Id": 96, + "FromVariable": 72, + "ToVariable": 71, + "Type": "Activator" + }, + { + "Id": 98, + "FromVariable": 67, + "ToVariable": 73, + "Type": "Inhibitor" + }, + { + "Id": 99, + "FromVariable": 73, + "ToVariable": 68, + "Type": "Inhibitor" + }, + { + "Id": 100, + "FromVariable": 73, + "ToVariable": 69, + "Type": "Inhibitor" + }, + { + "Id": 101, + "FromVariable": 73, + "ToVariable": 70, + "Type": "Inhibitor" + }, + { + "Id": 102, + "FromVariable": 73, + "ToVariable": 72, + "Type": "Inhibitor" + }, + { + "Id": 104, + "FromVariable": 71, + "ToVariable": 74, + "Type": "Activator" + }, + { + "Id": 106, + "FromVariable": 76, + "ToVariable": 75, + "Type": "Inhibitor" + }, + { + "Id": 107, + "FromVariable": 80, + "ToVariable": 79, + "Type": "Inhibitor" + }, + { + "Id": 108, + "FromVariable": 84, + "ToVariable": 83, + "Type": "Inhibitor" + }, + { + "Id": 109, + "FromVariable": 88, + "ToVariable": 87, + "Type": "Inhibitor" + }, + { + "Id": 110, + "FromVariable": 92, + "ToVariable": 91, + "Type": "Inhibitor" + }, + { + "Id": 111, + "FromVariable": 24, + "ToVariable": 8, + "Type": "Activator" + }, + { + "Id": 113, + "FromVariable": 21, + "ToVariable": 3, + "Type": "Activator" + }, + { + "Id": 114, + "FromVariable": 78, + "ToVariable": 52, + "Type": "Activator" + }, + { + "Id": 115, + "FromVariable": 75, + "ToVariable": 47, + "Type": "Activator" + }, + { + "Id": 117, + "FromVariable": 82, + "ToVariable": 62, + "Type": "Activator" + }, + { + "Id": 118, + "FromVariable": 79, + "ToVariable": 57, + "Type": "Activator" + }, + { + "Id": 120, + "FromVariable": 86, + "ToVariable": 72, + "Type": "Activator" + }, + { + "Id": 121, + "FromVariable": 83, + "ToVariable": 67, + "Type": "Activator" + }, + { + "Id": 123, + "FromVariable": 90, + "ToVariable": 32, + "Type": "Activator" + }, + { + "Id": 124, + "FromVariable": 87, + "ToVariable": 27, + "Type": "Activator" + }, + { + "Id": 126, + "FromVariable": 94, + "ToVariable": 42, + "Type": "Activator" + }, + { + "Id": 127, + "FromVariable": 91, + "ToVariable": 37, + "Type": "Activator" + }, + { + "Id": 129, + "FromVariable": 64, + "ToVariable": 86, + "Type": "Activator" + }, + { + "Id": 130, + "FromVariable": 61, + "ToVariable": 85, + "Type": "Activator" + }, + { + "Id": 131, + "FromVariable": 71, + "ToVariable": 81, + "Type": "Activator" + }, + { + "Id": 132, + "FromVariable": 74, + "ToVariable": 82, + "Type": "Activator" + }, + { + "Id": 133, + "FromVariable": 64, + "ToVariable": 78, + "Type": "Activator" + }, + { + "Id": 134, + "FromVariable": 61, + "ToVariable": 77, + "Type": "Activator" + }, + { + "Id": 135, + "FromVariable": 51, + "ToVariable": 81, + "Type": "Activator" + }, + { + "Id": 136, + "FromVariable": 54, + "ToVariable": 82, + "Type": "Activator" + }, + { + "Id": 137, + "FromVariable": 51, + "ToVariable": 23, + "Type": "Activator" + }, + { + "Id": 138, + "FromVariable": 54, + "ToVariable": 24, + "Type": "Activator" + }, + { + "Id": 139, + "FromVariable": 7, + "ToVariable": 77, + "Type": "Activator" + }, + { + "Id": 140, + "FromVariable": 10, + "ToVariable": 78, + "Type": "Activator" + }, + { + "Id": 141, + "FromVariable": 7, + "ToVariable": 89, + "Type": "Activator" + }, + { + "Id": 142, + "FromVariable": 10, + "ToVariable": 90, + "Type": "Activator" + }, + { + "Id": 143, + "FromVariable": 31, + "ToVariable": 23, + "Type": "Activator" + }, + { + "Id": 144, + "FromVariable": 34, + "ToVariable": 24, + "Type": "Activator" + }, + { + "Id": 145, + "FromVariable": 31, + "ToVariable": 93, + "Type": "Activator" + }, + { + "Id": 146, + "FromVariable": 34, + "ToVariable": 94, + "Type": "Activator" + }, + { + "Id": 147, + "FromVariable": 41, + "ToVariable": 89, + "Type": "Activator" + }, + { + "Id": 148, + "FromVariable": 44, + "ToVariable": 90, + "Type": "Activator" + }, + { + "Id": 1, + "FromVariable": 1, + "ToVariable": 3, + "Type": "Activator" + }, + { + "Id": 15, + "FromVariable": 1, + "ToVariable": 10, + "Type": "Activator" + }, + { + "Id": 149, + "FromVariable": 95, + "ToVariable": 1, + "Type": "Activator" + }, + { + "Id": 7, + "FromVariable": 2, + "ToVariable": 9, + "Type": "Activator" + }, + { + "Id": 13, + "FromVariable": 7, + "ToVariable": 2, + "Type": "Inhibitor" + }, + { + "Id": 112, + "FromVariable": 23, + "ToVariable": 2, + "Type": "Activator" + }, + { + "Id": 31, + "FromVariable": 25, + "ToVariable": 27, + "Type": "Activator" + }, + { + "Id": 45, + "FromVariable": 25, + "ToVariable": 34, + "Type": "Activator" + }, + { + "Id": 153, + "FromVariable": 95, + "ToVariable": 25, + "Type": "Activator" + }, + { + "Id": 37, + "FromVariable": 26, + "ToVariable": 33, + "Type": "Activator" + }, + { + "Id": 43, + "FromVariable": 31, + "ToVariable": 26, + "Type": "Inhibitor" + }, + { + "Id": 125, + "FromVariable": 89, + "ToVariable": 26, + "Type": "Activator" + }, + { + "Id": 46, + "FromVariable": 35, + "ToVariable": 37, + "Type": "Activator" + }, + { + "Id": 60, + "FromVariable": 35, + "ToVariable": 44, + "Type": "Activator" + }, + { + "Id": 154, + "FromVariable": 95, + "ToVariable": 35, + "Type": "Activator" + }, + { + "Id": 52, + "FromVariable": 36, + "ToVariable": 43, + "Type": "Activator" + }, + { + "Id": 58, + "FromVariable": 41, + "ToVariable": 36, + "Type": "Inhibitor" + }, + { + "Id": 128, + "FromVariable": 93, + "ToVariable": 36, + "Type": "Activator" + }, + { + "Id": 61, + "FromVariable": 45, + "ToVariable": 47, + "Type": "Activator" + }, + { + "Id": 75, + "FromVariable": 45, + "ToVariable": 54, + "Type": "Activator" + }, + { + "Id": 150, + "FromVariable": 95, + "ToVariable": 45, + "Type": "Activator" + }, + { + "Id": 67, + "FromVariable": 46, + "ToVariable": 53, + "Type": "Activator" + }, + { + "Id": 73, + "FromVariable": 51, + "ToVariable": 46, + "Type": "Inhibitor" + }, + { + "Id": 116, + "FromVariable": 77, + "ToVariable": 46, + "Type": "Activator" + }, + { + "Id": 76, + "FromVariable": 55, + "ToVariable": 57, + "Type": "Activator" + }, + { + "Id": 90, + "FromVariable": 55, + "ToVariable": 64, + "Type": "Activator" + }, + { + "Id": 151, + "FromVariable": 95, + "ToVariable": 55, + "Type": "Activator" + }, + { + "Id": 82, + "FromVariable": 56, + "ToVariable": 63, + "Type": "Activator" + }, + { + "Id": 88, + "FromVariable": 61, + "ToVariable": 56, + "Type": "Inhibitor" + }, + { + "Id": 119, + "FromVariable": 81, + "ToVariable": 56, + "Type": "Activator" + }, + { + "Id": 91, + "FromVariable": 65, + "ToVariable": 67, + "Type": "Activator" + }, + { + "Id": 105, + "FromVariable": 65, + "ToVariable": 74, + "Type": "Activator" + }, + { + "Id": 152, + "FromVariable": 95, + "ToVariable": 65, + "Type": "Activator" + }, + { + "Id": 97, + "FromVariable": 66, + "ToVariable": 73, + "Type": "Activator" + }, + { + "Id": 103, + "FromVariable": 71, + "ToVariable": 66, + "Type": "Inhibitor" + }, + { + "Id": 122, + "FromVariable": 85, + "ToVariable": 66, + "Type": "Activator" + } + ] + }, + "Layout": { + "Variables": [ + { + "Id": 95, + "Name": "AC", + "Type": "Constant", + "ContainerId": 0, + "PositionX": 880.7, + "PositionY": 422.62857142857143, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 1, + "Name": "LET-23", + "Type": "MembraneReceptor", + "ContainerId": 1, + "PositionX": 878.7883124044147, + "PositionY": 573.0092058505688, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 2, + "Name": "lin-12", + "Type": "MembraneReceptor", + "ContainerId": 1, + "PositionX": 770.9115781789793, + "PositionY": 688.8715426082731, + "CellX": null, + "CellY": null, + "Angle": -90 + }, + { + "Id": 3, + "Name": "signalact", + "Type": "Default", + "ContainerId": 1, + "PositionX": 877.5333333333333, + "PositionY": 622.4571428571428, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 4, + "Name": "SEM-5", + "Type": "Default", + "ContainerId": 1, + "PositionX": 877.5333333333333, + "PositionY": 655.3142857142857, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 5, + "Name": "LET-60", + "Type": "Default", + "ContainerId": 1, + "PositionX": 877.5333333333333, + "PositionY": 688.1714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 6, + "Name": "signal", + "Type": "Default", + "ContainerId": 1, + "PositionX": 877.5333333333333, + "PositionY": 721.0285714285715, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 7, + "Name": "MPK-1", + "Type": "Default", + "ContainerId": 1, + "PositionX": 877.5333333333333, + "PositionY": 753.8857142857142, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 8, + "Name": "relsig", + "Type": "Default", + "ContainerId": 1, + "PositionX": 925.0333333333333, + "PositionY": 721.0285714285715, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 9, + "Name": "lst", + "Type": "Default", + "ContainerId": 1, + "PositionX": 830.0333333333333, + "PositionY": 688.1714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 10, + "Name": "ROM-1", + "Type": "Default", + "ContainerId": 1, + "PositionX": 849.0333333333333, + "PositionY": 771.6285714285714, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 21, + "Name": "lin-3", + "Type": "Default", + "ContainerId": 6, + "PositionX": 849.0333333333333, + "PositionY": 903.7714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 22, + "Name": "lin-15", + "Type": "Default", + "ContainerId": 6, + "PositionX": 849.0333333333333, + "PositionY": 985.9142857142857, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 23, + "Name": "LSext", + "Type": "Default", + "ContainerId": 6, + "PositionX": 817.3666666666667, + "PositionY": 920.2, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 24, + "Name": "Relay", + "Type": "Default", + "ContainerId": 6, + "PositionX": 912.3666666666667, + "PositionY": 903.7714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 25, + "Name": "LET-23", + "Type": "MembraneReceptor", + "ContainerId": 7, + "PositionX": 1128.7883124043487, + "PositionY": 573.009205857059, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 26, + "Name": "lin-12", + "Type": "MembraneReceptor", + "ContainerId": 7, + "PositionX": 1020.9115781789799, + "PositionY": 688.8715426082731, + "CellX": null, + "CellY": null, + "Angle": -90 + }, + { + "Id": 27, + "Name": "signalact", + "Type": "Default", + "ContainerId": 7, + "PositionX": 1127.5333333333333, + "PositionY": 622.4571428571428, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 28, + "Name": "SEM-5", + "Type": "Default", + "ContainerId": 7, + "PositionX": 1127.5333333333333, + "PositionY": 655.3142857142857, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 29, + "Name": "LET-60", + "Type": "Default", + "ContainerId": 7, + "PositionX": 1127.5333333333333, + "PositionY": 688.1714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 30, + "Name": "signal", + "Type": "Default", + "ContainerId": 7, + "PositionX": 1127.5333333333333, + "PositionY": 721.0285714285715, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 31, + "Name": "MPK-1", + "Type": "Default", + "ContainerId": 7, + "PositionX": 1127.5333333333333, + "PositionY": 753.8857142857142, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 32, + "Name": "relsig", + "Type": "Default", + "ContainerId": 7, + "PositionX": 1175.0333333333333, + "PositionY": 721.0285714285715, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 33, + "Name": "lst", + "Type": "Default", + "ContainerId": 7, + "PositionX": 1080.0333333333333, + "PositionY": 688.1714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 34, + "Name": "ROM-1", + "Type": "Default", + "ContainerId": 7, + "PositionX": 1099.0333333333333, + "PositionY": 771.6285714285714, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 35, + "Name": "LET-23", + "Type": "MembraneReceptor", + "ContainerId": 8, + "PositionX": 1378.788312404349, + "PositionY": 573.009205857059, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 36, + "Name": "lin-12", + "Type": "MembraneReceptor", + "ContainerId": 8, + "PositionX": 1270.9115781789796, + "PositionY": 688.8715426082729, + "CellX": null, + "CellY": null, + "Angle": -90 + }, + { + "Id": 37, + "Name": "signalact", + "Type": "Default", + "ContainerId": 8, + "PositionX": 1377.5333333333333, + "PositionY": 622.4571428571428, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 38, + "Name": "SEM-5", + "Type": "Default", + "ContainerId": 8, + "PositionX": 1377.5333333333333, + "PositionY": 655.3142857142857, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 39, + "Name": "LET-60", + "Type": "Default", + "ContainerId": 8, + "PositionX": 1377.5333333333333, + "PositionY": 688.1714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 40, + "Name": "signal", + "Type": "Default", + "ContainerId": 8, + "PositionX": 1377.5333333333333, + "PositionY": 721.0285714285715, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 41, + "Name": "MPK-1", + "Type": "Default", + "ContainerId": 8, + "PositionX": 1377.5333333333333, + "PositionY": 753.8857142857142, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 42, + "Name": "relsig", + "Type": "Default", + "ContainerId": 8, + "PositionX": 1425.0333333333333, + "PositionY": 721.0285714285715, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 43, + "Name": "lst", + "Type": "Default", + "ContainerId": 8, + "PositionX": 1330.0333333333333, + "PositionY": 688.1714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 44, + "Name": "ROM-1", + "Type": "Default", + "ContainerId": 8, + "PositionX": 1349.0333333333333, + "PositionY": 771.6285714285714, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 45, + "Name": "LET-23", + "Type": "MembraneReceptor", + "ContainerId": 9, + "PositionX": 628.7883124043155, + "PositionY": 573.0092058603041, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 46, + "Name": "lin-12", + "Type": "MembraneReceptor", + "ContainerId": 9, + "PositionX": 520.9115781789789, + "PositionY": 688.8715426082726, + "CellX": null, + "CellY": null, + "Angle": -90 + }, + { + "Id": 47, + "Name": "signalact", + "Type": "Default", + "ContainerId": 9, + "PositionX": 627.5333333333333, + "PositionY": 622.4571428571428, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 48, + "Name": "SEM-5", + "Type": "Default", + "ContainerId": 9, + "PositionX": 627.5333333333333, + "PositionY": 655.3142857142857, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 49, + "Name": "LET-60", + "Type": "Default", + "ContainerId": 9, + "PositionX": 627.5333333333333, + "PositionY": 688.1714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 50, + "Name": "signal", + "Type": "Default", + "ContainerId": 9, + "PositionX": 627.5333333333333, + "PositionY": 721.0285714285715, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 51, + "Name": "MPK-1", + "Type": "Default", + "ContainerId": 9, + "PositionX": 627.5333333333333, + "PositionY": 753.8857142857142, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 52, + "Name": "relsig", + "Type": "Default", + "ContainerId": 9, + "PositionX": 675.0333333333333, + "PositionY": 721.0285714285715, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 53, + "Name": "lst", + "Type": "Default", + "ContainerId": 9, + "PositionX": 580.0333333333333, + "PositionY": 688.1714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 54, + "Name": "ROM-1", + "Type": "Default", + "ContainerId": 9, + "PositionX": 599.0333333333333, + "PositionY": 771.6285714285714, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 55, + "Name": "LET-23", + "Type": "MembraneReceptor", + "ContainerId": 10, + "PositionX": 378.78831240432384, + "PositionY": 573.0092058594964, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 56, + "Name": "lin-12", + "Type": "MembraneReceptor", + "ContainerId": 10, + "PositionX": 270.9115781789792, + "PositionY": 688.8715426082726, + "CellX": null, + "CellY": null, + "Angle": -90 + }, + { + "Id": 57, + "Name": "signalact", + "Type": "Default", + "ContainerId": 10, + "PositionX": 377.5333333333333, + "PositionY": 622.4571428571428, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 58, + "Name": "SEM-5", + "Type": "Default", + "ContainerId": 10, + "PositionX": 377.5333333333333, + "PositionY": 655.3142857142857, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 59, + "Name": "LET-60", + "Type": "Default", + "ContainerId": 10, + "PositionX": 377.5333333333333, + "PositionY": 688.1714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 60, + "Name": "signal", + "Type": "Default", + "ContainerId": 10, + "PositionX": 377.5333333333333, + "PositionY": 721.0285714285715, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 61, + "Name": "MPK-1", + "Type": "Default", + "ContainerId": 10, + "PositionX": 377.5333333333333, + "PositionY": 753.8857142857142, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 62, + "Name": "relsig", + "Type": "Default", + "ContainerId": 10, + "PositionX": 425.0333333333333, + "PositionY": 721.0285714285715, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 63, + "Name": "lst", + "Type": "Default", + "ContainerId": 10, + "PositionX": 330.0333333333333, + "PositionY": 688.1714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 64, + "Name": "ROM-1", + "Type": "Default", + "ContainerId": 10, + "PositionX": 349.0333333333333, + "PositionY": 771.6285714285714, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 65, + "Name": "LET-23", + "Type": "MembraneReceptor", + "ContainerId": 11, + "PositionX": 128.78831240433428, + "PositionY": 573.0092058584796, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 66, + "Name": "lin-12", + "Type": "MembraneReceptor", + "ContainerId": 11, + "PositionX": 20.91157817897927, + "PositionY": 688.8715426082725, + "CellX": null, + "CellY": null, + "Angle": -90 + }, + { + "Id": 67, + "Name": "signalact", + "Type": "Default", + "ContainerId": 11, + "PositionX": 127.53333333333333, + "PositionY": 622.4571428571428, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 68, + "Name": "SEM-5", + "Type": "Default", + "ContainerId": 11, + "PositionX": 127.53333333333333, + "PositionY": 655.3142857142857, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 69, + "Name": "LET-60", + "Type": "Default", + "ContainerId": 11, + "PositionX": 127.53333333333333, + "PositionY": 688.1714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 70, + "Name": "signal", + "Type": "Default", + "ContainerId": 11, + "PositionX": 127.53333333333333, + "PositionY": 721.0285714285715, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 71, + "Name": "MPK-1", + "Type": "Default", + "ContainerId": 11, + "PositionX": 127.53333333333333, + "PositionY": 753.8857142857142, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 72, + "Name": "relsig", + "Type": "Default", + "ContainerId": 11, + "PositionX": 175.03333333333333, + "PositionY": 721.0285714285715, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 73, + "Name": "lst", + "Type": "Default", + "ContainerId": 11, + "PositionX": 80.03333333333333, + "PositionY": 688.1714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 74, + "Name": "ROM-1", + "Type": "Default", + "ContainerId": 11, + "PositionX": 99.03333333333333, + "PositionY": 771.6285714285714, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 75, + "Name": "lin-3", + "Type": "Default", + "ContainerId": 12, + "PositionX": 599.0333333333333, + "PositionY": 903.7714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 76, + "Name": "lin-15", + "Type": "Default", + "ContainerId": 12, + "PositionX": 599.0333333333333, + "PositionY": 985.9142857142857, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 77, + "Name": "LSext", + "Type": "Default", + "ContainerId": 12, + "PositionX": 567.3666666666667, + "PositionY": 920.2, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 78, + "Name": "Relay", + "Type": "Default", + "ContainerId": 12, + "PositionX": 662.3666666666667, + "PositionY": 903.7714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 79, + "Name": "lin-3", + "Type": "Default", + "ContainerId": 13, + "PositionX": 349.0333333333333, + "PositionY": 903.7714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 80, + "Name": "lin-15", + "Type": "Default", + "ContainerId": 13, + "PositionX": 349.0333333333333, + "PositionY": 985.9142857142857, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 81, + "Name": "LSext", + "Type": "Default", + "ContainerId": 13, + "PositionX": 317.3666666666667, + "PositionY": 920.2, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 82, + "Name": "Relay", + "Type": "Default", + "ContainerId": 13, + "PositionX": 412.3666666666667, + "PositionY": 903.7714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 83, + "Name": "lin-3", + "Type": "Default", + "ContainerId": 14, + "PositionX": 99.03333333333333, + "PositionY": 903.7714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 84, + "Name": "lin-15", + "Type": "Default", + "ContainerId": 14, + "PositionX": 99.03333333333333, + "PositionY": 985.9142857142857, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 85, + "Name": "LSext", + "Type": "Default", + "ContainerId": 14, + "PositionX": 67.36666666666667, + "PositionY": 920.2, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 86, + "Name": "Relay", + "Type": "Default", + "ContainerId": 14, + "PositionX": 162.36666666666667, + "PositionY": 903.7714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 87, + "Name": "lin-3", + "Type": "Default", + "ContainerId": 15, + "PositionX": 1099.0333333333333, + "PositionY": 903.7714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 88, + "Name": "lin-15", + "Type": "Default", + "ContainerId": 15, + "PositionX": 1099.0333333333333, + "PositionY": 985.9142857142857, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 89, + "Name": "LSext", + "Type": "Default", + "ContainerId": 15, + "PositionX": 1067.3666666666665, + "PositionY": 920.2, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 90, + "Name": "Relay", + "Type": "Default", + "ContainerId": 15, + "PositionX": 1162.3666666666668, + "PositionY": 903.7714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 91, + "Name": "lin-3", + "Type": "Default", + "ContainerId": 16, + "PositionX": 1349.0333333333333, + "PositionY": 903.7714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 92, + "Name": "lin-15", + "Type": "Default", + "ContainerId": 16, + "PositionX": 1349.0333333333333, + "PositionY": 985.9142857142857, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 93, + "Name": "LSext", + "Type": "Default", + "ContainerId": 16, + "PositionX": 1317.3666666666665, + "PositionY": 920.2, + "CellX": null, + "CellY": null, + "Angle": 0 + }, + { + "Id": 94, + "Name": "Relay", + "Type": "Default", + "ContainerId": 16, + "PositionX": 1412.3666666666668, + "PositionY": 903.7714285714286, + "CellX": null, + "CellY": null, + "Angle": 0 + } + ], + "Containers": [ + { + "Id": 1, + "Name": "", + "Size": 1, + "PositionX": 3, + "PositionY": 2 + }, + { + "Id": 6, + "Name": "", + "Size": 1, + "PositionX": 3, + "PositionY": 3 + }, + { + "Id": 7, + "Name": "", + "Size": 1, + "PositionX": 4, + "PositionY": 2 + }, + { + "Id": 8, + "Name": "", + "Size": 1, + "PositionX": 5, + "PositionY": 2 + }, + { + "Id": 9, + "Name": "", + "Size": 1, + "PositionX": 2, + "PositionY": 2 + }, + { + "Id": 10, + "Name": "", + "Size": 1, + "PositionX": 1, + "PositionY": 2 + }, + { + "Id": 11, + "Name": "", + "Size": 1, + "PositionX": 0, + "PositionY": 2 + }, + { + "Id": 12, + "Name": "", + "Size": 1, + "PositionX": 2, + "PositionY": 3 + }, + { + "Id": 13, + "Name": "", + "Size": 1, + "PositionX": 1, + "PositionY": 3 + }, + { + "Id": 14, + "Name": "", + "Size": 1, + "PositionX": 0, + "PositionY": 3 + }, + { + "Id": 15, + "Name": "", + "Size": 1, + "PositionX": 4, + "PositionY": 3 + }, + { + "Id": 16, + "Name": "", + "Size": 1, + "PositionX": 5, + "PositionY": 3 + } + ] + } +} \ No newline at end of file