package kappa-library

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file replay.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
(******************************************************************************)
(*  _  __ * The Kappa Language                                                *)
(* | |/ / * Copyright 2010-2020 CNRS - Harvard Medical School - INRIA - IRIF  *)
(* | ' /  *********************************************************************)
(* | . \  * This file is distributed under the terms of the                   *)
(* |_|\_\ * GNU Lesser General Public License Version 3                       *)
(******************************************************************************)

type state = {
  graph: Edges.t;
  time: float;
  event: int;
  connected_components: Agent.SetMap.Set.t Mods.IntMap.t option;
}

type summary = { unary_distances: (int * int) option }

let init_state ~with_connected_components =
  {
    graph = Edges.empty ~with_connected_components;
    time = 0.;
    event = 0;
    connected_components =
      (if with_connected_components then
         Some Mods.IntMap.empty
       else
         None);
  }

let cc_of_agent ag e work =
  let rec fold_arity_list f x arity acc =
    if x = arity then
      acc
    else
      fold_arity_list f (succ x) arity (f acc x)
  in

  let add_agent a e (work, morphism, todos) =
    let aid = Agent.id a in
    let atype = Agent.sort a in
    let arity = Edges.get_sites aid e in
    let w_agent, work' = Pattern.new_node work atype in
    let todos' = fold_arity_list (fun acc x -> (aid, x) :: acc) 0 arity todos in
    let work'' =
      fold_arity_list
        (fun w x ->
          try
            let internal = Edges.get_internal aid x e in
            Pattern.new_internal_state w (w_agent, x) internal
          with Failure _ -> w)
        0 arity work'
    in
    w_agent, work'', (aid, w_agent) :: morphism, todos'
  in

  let add_links (work, morphism, todos) (aid, x) e =
    let _, w_agent = List.find (fun (id, _) -> id = aid) morphism in
    let not_agent (id, s) = not (id = aid && x = s) in
    match Edges.link_destination aid x e with
    | None ->
      let work' = Pattern.new_free work (w_agent, x) in
      let todos' = List.filter not_agent todos in
      work', morphism, todos'
    | Some (b, y) ->
      let bid = Agent.id b in
      let not_agents (id, s) =
        not_agent (id, s) && not (id = Agent.id b && s = y)
      in
      (try
         let _, wb_agent = List.find (fun (id, _) -> id = bid) morphism in
         let work' = Pattern.new_link work (w_agent, x) (wb_agent, y) in
         let todos' = List.filter not_agents todos in
         work', morphism, todos'
       with Not_found ->
         let wb_agent, work', morphism', todos' =
           add_agent b e (work, morphism, todos)
         in
         let work'' = Pattern.new_link work' (w_agent, x) (wb_agent, y) in
         let todos'' = List.filter not_agents todos' in
         work'', morphism', todos'')
  in

  let rec working_todo (work, morphism, todo) =
    match todo with
    | [] -> morphism, work
    | port :: _ ->
      let work', morphism', todo' = add_links (work, morphism, todo) port e in
      working_todo (work', morphism', todo')
  in

  let _, w, m, t = add_agent ag e (work, [], []) in
  working_todo (w, m, t)

let cc_of_state ~debug_mode s env =
  let cc_of_root agent e' =
    let work = Pattern.begin_new e' in
    let morphism, work' = cc_of_agent agent s.graph work in
    let en, _, c, i = Pattern.finish_new ~debug_mode work' in
    en, List.map (fun (cid, (aid, _)) -> cid, aid) morphism, c, i
  in
  match s.connected_components with
  | Some cc_maps ->
    Mods.IntMap.fold
      (fun root cc_map (e, acc) ->
        Agent.SetMap.Set.fold
          (fun agent (e', acc') ->
            if Agent.id agent = root then (
              let en, r, c, i = cc_of_root agent e' in
              en, (r, c, i) :: acc'
            ) else
              e', acc')
          cc_map (e, acc))
      cc_maps (env, [])
  | None -> env, []

let break_apart_cc graph ccs = function
  | None -> ccs
  | Some (origin_cc, new_cc) ->
    let set = Mods.IntMap.find_default Agent.SetMap.Set.empty origin_cc ccs in
    if Agent.SetMap.Set.is_empty set then
      ccs
    else (
      let nset, oset' =
        Agent.SetMap.Set.partition
          (fun (x, _) -> Edges.get_connected_component x graph = Some new_cc)
          set
      in
      Mods.IntMap.add new_cc nset (Mods.IntMap.add origin_cc oset' ccs)
    )

let merge_cc ccs = function
  | None -> ccs
  | Some (cc1, cc2) ->
    let set1 = Mods.IntMap.find_default Agent.SetMap.Set.empty cc1 ccs in
    (match Mods.IntMap.pop cc2 ccs with
    | None, _ -> ccs
    | Some set2, ccs' ->
      Mods.IntMap.add cc1 (Agent.SetMap.Set.union set1 set2) ccs')

let do_negative_part ((a, _), s) (graph, ccs) =
  match Edges.link_destination a s graph with
  | None -> Edges.remove_free a s graph, ccs
  | Some ((a', _), s') ->
    let graph', cc_change = Edges.remove_link a s a' s' graph in
    ( graph',
      (match ccs with
      | None -> None
      | Some ccs -> Some (break_apart_cc graph' ccs cc_change)) )

let do_action sigs ((graph, ccs) as pack) = function
  | Instantiation.Create (((id, ty) as ag), _graphs) ->
    ( snd @@ Edges.add_agent ~id sigs ty graph,
      Option_util.map (Mods.IntMap.add id (Agent.SetMap.Set.singleton ag)) ccs )
  | Instantiation.Mod_internal (((a, _), s), i) ->
    Edges.add_internal a s i graph, ccs
  | Instantiation.Bind (((a1, s1) as x1), ((a2, s2) as x2))
  | Instantiation.Bind_to (((a1, s1) as x1), ((a2, s2) as x2)) ->
    let graph', ccs' = do_negative_part x2 (do_negative_part x1 pack) in
    let graph'', cc_change = Edges.add_link a1 s1 a2 s2 graph' in
    ( graph'',
      (match ccs' with
      | None -> None
      | Some ccs' -> Some (merge_cc ccs' cc_change)) )
  | Instantiation.Free (((a, _), s) as x) ->
    let graph', ccs' = do_negative_part x pack in
    Edges.add_free a s graph', ccs'
  | Instantiation.Remove ((id, ty) as a) ->
    let graph', ccs' =
      Tools.recti
        (fun st s -> do_negative_part (a, s) st)
        pack (Signature.arity sigs ty)
    in
    (match ccs' with
    | None -> Edges.remove_agent id graph', None
    | Some ccs' ->
      (match Mods.IntMap.pop id ccs' with
      | None, _ -> assert false
      | Some x, ccs'' ->
        let () = assert (Agent.SetMap.Set.is_singleton x) in
        Edges.remove_agent id graph', Some ccs''))

let involved_agents l =
  List_util.map_option
    (function
      | Instantiation.Is_Here a -> Some a
      | Instantiation.Is_Free _ | Instantiation.Has_Internal _
      | Instantiation.Is_Bound _ | Instantiation.Is_Bound_to _
      | Instantiation.Has_Binding_type _ ->
        None)
    l

let store_distances r graph = function
  | [] | [ _ ] | _ :: _ :: _ :: _ -> None
  | [ cc1; cc2 ] ->
    let cc1_ags = involved_agents cc1 in
    let cc2_ags = involved_agents cc2 in
    (match Edges.are_connected graph cc1_ags cc2_ags with
    | None -> None
    | Some path -> Some (r, List.length path))

let test_pass_on graph = function
  | Instantiation.Is_Here ag -> Edges.is_agent ag graph
  | Instantiation.Has_Internal ((ag, s), st) ->
    Edges.is_agent ag graph && Edges.is_internal st (Agent.id ag) s graph
  | Instantiation.Is_Free (ag, s) ->
    Edges.is_agent ag graph && Edges.is_free (Agent.id ag) s graph
  | Instantiation.Is_Bound (ag, s) ->
    Edges.is_agent ag graph && not (Edges.is_free (Agent.id ag) s graph)
  | Instantiation.Is_Bound_to ((ag, s), (ag', s')) ->
    Edges.is_agent ag graph && Edges.is_agent ag' graph
    && Edges.link_exists (Agent.id ag) s (Agent.id ag') s' graph
  | Instantiation.Has_Binding_type ((ag, s), (ag_ty, s')) ->
    Edges.is_agent ag graph
    &&
    (match Edges.link_destination (Agent.id ag) s graph with
    | None -> false
    | Some ((_, dst_ag_ty), dst_s) -> dst_ag_ty = ag_ty && dst_s = s')

let tests_pass_on graph tests =
  List.for_all (test_pass_on graph) (List.concat tests)

let is_step_triggerable_on_edges graph = function
  | Trace.Subs _ | Trace.Init _ | Trace.Pert _ | Trace.Dummy _ -> true
  | Trace.Rule (_r, event, _info) ->
    tests_pass_on graph event.Instantiation.tests
  | Trace.Obs (_, tests, _) -> tests_pass_on graph tests

let is_step_triggerable state = is_step_triggerable_on_edges state.graph

(* There is a subtelty when executing a sequence of actions. Indeed,
   whenever a rule both creates and removes agents, there is currently
   no guarantee that the creation actions are placed before the removal
   actions in [event.Instantiation.actions]. This can be an issue in a
   case where an event performs the following two actions for example:
   ["create agent with id 8", "remove agent with id 8"]. In this case,
   agent id 8 is not available when the creation action is performed and
   so the [Edges] module throws an exception.

   As a temporary fix, we make sure that all deletion actions are
   executed first. This implicitly assumes that a step deleting an agent
   cannot perform any other action involving this agent.

   TODO: Shouldn't we rather ensure that actions are properly sorted in
   the trace file in the first place? *)
let do_actions sigs st actions =
  let is_removal =
    let open Instantiation in
    function
    | Remove _ -> true
    | Create _ | Mod_internal _ | Bind _ | Bind_to _ | Free _ -> false
  in
  let removals, others = List.partition is_removal actions in
  let do_in_order actions st = List.fold_left (do_action sigs) st actions in
  st |> do_in_order removals |> do_in_order others

let do_step sigs state = function
  | Trace.Subs _ -> state, { unary_distances = None }
  | Trace.Rule (kind, event, info) ->
    let unary_distances =
      if state.connected_components = None then
        None
      else
        store_distances kind state.graph event.Instantiation.tests
    in
    let pregraph, connected_components =
      do_actions sigs
        (state.graph, state.connected_components)
        event.Instantiation.actions
    in
    let graph =
      List.fold_left
        (fun graph ((id, _), s) -> Edges.add_free id s graph)
        pregraph event.Instantiation.side_effects_dst
    in
    ( {
        graph;
        connected_components;
        time = info.Trace.Simulation_info.story_time;
        event = info.Trace.Simulation_info.story_event;
      },
      { unary_distances } )
  | Trace.Pert (_, event, info) ->
    let pregraph, connected_components =
      do_actions sigs
        (state.graph, state.connected_components)
        event.Instantiation.actions
    in
    let graph =
      List.fold_left
        (fun graph ((id, _), s) -> Edges.add_free id s graph)
        pregraph event.Instantiation.side_effects_dst
    in
    ( {
        graph;
        connected_components;
        time = info.Trace.Simulation_info.story_time;
        event = info.Trace.Simulation_info.story_event;
      },
      { unary_distances = None } )
  | Trace.Init actions ->
    let graph, connected_components =
      do_actions sigs (state.graph, state.connected_components) actions
    in
    ( { graph; connected_components; time = state.time; event = state.event },
      { unary_distances = None } )
  | Trace.Obs (_, _, info) ->
    ( {
        graph = state.graph;
        time = info.Trace.Simulation_info.story_time;
        event = info.Trace.Simulation_info.story_event;
        connected_components = state.connected_components;
      },
      { unary_distances = None } )
  | Trace.Dummy _ -> state, { unary_distances = None }
OCaml

Innovation. Community. Security.