Skip to content

Commit d578b8a

Browse files
committed
feat: save to BMA json format
1 parent 45f3577 commit d578b8a

File tree

4 files changed

+307
-152
lines changed

4 files changed

+307
-152
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ HerbCore = "2b23ba43-8213-43cb-b5ea-38c12b45bd45"
1313
HerbGrammar = "4ef9e186-2fe5-4b24-8de7-9f7291f24af7"
1414
HerbSearch = "3008d8e8-f9aa-438a-92ed-26e9c7b4829f"
1515
MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078"
16+
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
1617
MetaGraphsNext = "fa8bd995-216d-47f1-8a91-f3b68fbeb377"
1718
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1819
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
1920
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
2021

2122
[weakdeps]
2223
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
23-
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
2424

2525
[extensions]
2626
JSONExt = ["JSON", "MacroTools"]
@@ -35,8 +35,8 @@ HerbCore = "0.3.4"
3535
HerbGrammar = "0.6"
3636
HerbSearch = "0.4.1"
3737
JSON = "0.21.4"
38-
MacroTools = "0.5.16"
3938
MLStyle = "0.4.17"
39+
MacroTools = "0.5.16"
4040
MetaGraphsNext = "0.7"
4141
Random = "1.10"
4242
SciMLBase = "2.74.1"

ext/JSONExt.jl

Lines changed: 8 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,159 +1,18 @@
11
module JSONExt
2+
import GraphDynamicalSystems.QualitativeNetwork
23
import JSON
34

4-
using GraphDynamicalSystems: Asynchronous, QualitativeNetwork, default_target_function
5-
using Graphs: SimpleDiGraph, add_edge!, add_vertex!
6-
using MacroTools: @capture, postwalk
7-
using MetaGraphsNext: MetaGraph, inneighbor_labels
8-
9-
function nested_dicts_keys_to_lowercase(d)
10-
if d isa AbstractDict
11-
return Dict([lowercase(k) => nested_dicts_keys_to_lowercase(v) for (k, v) in d])
12-
elseif d isa AbstractVector
13-
return [nested_dicts_keys_to_lowercase(v) for v in d]
14-
else
15-
return d
16-
end
17-
end
18-
19-
function sanitize_formula(f)
20-
# surround variable names with quotes
21-
return replace(f, r"var\(([^\)]+)\)" => s"var(\"\1\")")
22-
end
23-
24-
function entity_name_from_in_neighbors(entity, in_neighbors)
25-
# the formulas can reference their incoming edges
26-
# with either the name of the neighbor entity or
27-
# its id
28-
e_id = tryparse(Int, entity)
29-
30-
entity_name = [
31-
Symbol("$(name)_$id") for
32-
(id, name, _) in in_neighbors if isnothing(e_id) ? name == entity : id == e_id
33-
]
34-
35-
if length(entity_name) != 1
36-
error(
37-
"""
38-
Error while constructing name for entity: $entity, with in neighbors: \
39-
$in_neighbors. There are more than one incoming neighbor entities with the same \
40-
name. To fix this error, remove the erroneous relationships from the JSON file, \
41-
or reference the entity by id (like `var(3)`).
42-
""",
43-
)
44-
end
45-
return only(entity_name)
46-
end
47-
48-
function create_target_function(
49-
variable::Dict,
50-
in_neighbor_ids::Vector{Int},
51-
id_to_name::Dict,
52-
mg::MetaGraph,
53-
)
54-
formula = Meta.parse(sanitize_formula(variable["formula"]))
55-
in_neighbor_names = getindex.((id_to_name,), in_neighbor_ids)
56-
in_neighbor_types = getindex.((mg.edge_data,), in_neighbor_ids, (variable["id"],))
57-
in_neighbors = zip(in_neighbor_ids, in_neighbor_names, in_neighbor_types)
58-
59-
if isnothing(formula) # default target function
60-
if length(in_neighbor_ids) == 0
61-
@warn "$(variable["name"]) has no inputs, defaulting formula to lowest value ($(variable["rangefrom"]))."
62-
return variable["rangefrom"]
63-
else
64-
activators = [
65-
Symbol("$(name)_$id") for
66-
(id, name, ty) in in_neighbors if ty == "Activator"
67-
]
68-
inhibitors = [
69-
Symbol("$(name)_$id") for
70-
(id, name, ty) in in_neighbors if ty == "Inhibitor"
71-
]
72-
return default_target_function(
73-
variable["rangefrom"],
74-
variable["rangeto"],
75-
activators,
76-
inhibitors,
77-
)
78-
end
79-
else # custom target function
80-
return postwalk(
81-
x ->
82-
@capture(x, var(v_String)) ?
83-
:($(entity_name_from_in_neighbors(v, in_neighbors))) : x,
84-
formula,
85-
)
86-
end
87-
end
88-
89-
function to_from_variable_id(r, from_to)
90-
k = "$(from_to)variable"
91-
k_w_id = k * "id"
92-
93-
if haskey(r, k)
94-
return r[k]
95-
elseif haskey(r, k_w_id)
96-
return r[k_w_id]
97-
else
98-
error("""
99-
Neither alternative key was found to retrieve the edge variable id. The \
100-
model file is not using the expected structure for BMA models.
101-
""")
102-
end
103-
end
5+
using AbstractTrees: PostOrderDFS
6+
using GraphDynamicalSystems: QualitativeNetwork, bma_dict_to_qn, qn_to_bma_dict
1047

1058
function QualitativeNetwork(bma_file_path::AbstractString)
1069
json_def = JSON.parse(read(bma_file_path, String))
10710

108-
json_def = nested_dicts_keys_to_lowercase(json_def)
109-
model = json_def["model"]
110-
variables = model["variables"]
111-
relationships = model["relationships"]
112-
113-
id_to_name = Dict([v["id"] => v["name"] for v in variables])
114-
names = [Symbol("$(v["name"])_$(v["id"])") for v in variables]
115-
mg = MetaGraph(SimpleDiGraph(), Int, Union{Expr,Integer,Symbol}, String)
116-
117-
foreach(variables) do v
118-
id = v["id"]
119-
name = v["name"]
120-
# adding an empty expression: :()
121-
# because we need to construct the interaction graph
122-
# first before parsing the functions correctly
123-
added = add_vertex!(mg, id, :())
124-
if !added
125-
error(
126-
"""
127-
Failed to add the entity (\"$name\", id: #$id) from the input file while \
128-
constructing the QN. Check that there is only one entity in the model with \
129-
the id #$id.
130-
""",
131-
)
132-
end
133-
end
134-
135-
foreach(relationships) do r
136-
from = to_from_variable_id(r, "from")
137-
to = to_from_variable_id(r, "to")
138-
type_of_edge = r["type"]
139-
added = add_edge!(mg, from, to, type_of_edge)
140-
if !added
141-
@warn """
142-
Encountered a duplicate relationship between entities (from: \
143-
$(id_to_name[from]), #$from; to: $(id_to_name[to]), #$to) while constructing \
144-
the QN.
145-
"""
146-
end
147-
end
148-
149-
formulas = Union{Expr,Integer,Symbol}[
150-
create_target_function(v, collect(inneighbor_labels(mg, v["id"])), id_to_name, mg) for v in variables
151-
]
11+
return bma_dict_to_qn(json_def)
12+
end
15213

153-
# @show formulas
154-
# formulas = Union{Expr,Integer,Symbol}[v["formula"] for v in variables]
155-
domains = [v["rangefrom"]:v["rangeto"] for v in variables]
156-
#
157-
return QualitativeNetwork(names, formulas, domains; schedule = Asynchronous)
14+
function JSON.json(qn::QualitativeNetwork)
15+
return JSON.json(qn_to_bma_dict(qn))
15816
end
17+
15918
end

0 commit comments

Comments
 (0)