|
1 | 1 | module JSONExt |
| 2 | +import GraphDynamicalSystems.QualitativeNetwork |
2 | 3 | import JSON |
3 | 4 |
|
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 |
104 | 7 |
|
105 | 8 | function QualitativeNetwork(bma_file_path::AbstractString) |
106 | 9 | json_def = JSON.parse(read(bma_file_path, String)) |
107 | 10 |
|
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 |
152 | 13 |
|
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)) |
158 | 16 | end |
| 17 | + |
159 | 18 | end |
0 commit comments