solution_repr.py 31.3 KB
Newer Older
1
# -*- coding: utf-8 -*-
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Copyright (C) 2017  IRISA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# The original code contained here was initially developed by:
#
#     Pierre Vignet.
#     IRISA
#     Dyliss team
#     IRISA Campus de Beaulieu
#     35042 RENNES Cedex, FRANCE
24
25
26
from __future__ import unicode_literals
from __future__ import print_function

27
# Standard imports
28
29
30
31
import datetime as dt
from collections import defaultdict
import networkx as nx
import itertools as it
32
import re
33
import matplotlib.pyplot as plt
34
from logging import DEBUG
35

36
# Library imports
37
38
from cadbiom.models.guard_transitions.translators.chart_xml \
    import MakeModelFromXmlFile
39
40
41
42
43
44
45
46
from cadbiom.models.biosignal.translators.gt_visitors import compile_cond
from cadbiom.models.guard_transitions.analyser.ana_visitors import TableVisitor
from cadbiom.models.biosignal.sig_expr import *
from cadbiom.models.guard_transitions.analyser.ana_visitors import SigExpIdCollectVisitor

import cadbiom.commons as cm

LOGGER = cm.logger()
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

LOG_DIR = "./logs/"
BIO_MOLDELS_DIR = "./bio_models/"
GRAPHS_DIR = "./graphs/"

"""
Bx  Ax
% h2 h00
% h3
% h0 h1
% hlast
Bx  Ax
% h2
% h3 h00
% h0 h1
%
% hlast
Bx  Ax
% h2
% h3 h00
% h0 h1
% hlast
%
%
Bx  Ax
% h2 h00
% h3
% h0 h1
% hlast
%
%
%
"""
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

class Reporter(object):
    """Error reporter.

    .. note:: Link the lexer to the model allows to avoid error in Reporter
        like:  "-> dec -> Undeclared event or state"
        In practice this is time consuming and useless for what we want to do.
        See parse_condition()
    """

    def __init__(self):
        self.error = False
        self.mess = ""
        pass

    def display(self, e):
        self.error = True
        if "Undeclared event or state" not in e:
            LOGGER.debug("\t" + self.mess + " -> " + e)

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

def load_solutions(file):
    """Open a file with many solution/MACs.

    :param: File name
    :type: <str>
    :return:A tuple of "frontier places" and a list of events in each step.
        ("Bx Ax", [[u'h2', u'h00'], [u'h3'], [u'h0', u'h1'], [u'hlast']])
    :rtype: <tuple <str>, <list>>
    """

    sol_steps = defaultdict(list)
    sol = ""
    with open(file, 'r') as fd:
        for line in fd:
115
            LOGGER.debug("Load_solutions :: line: " + line)
116
117
118
119
120
121
            # Remove possible \t separator from first line (frontier solution)
            line = line.rstrip('\n').rstrip('\t').replace('\t', ' ')
            # TODO: remove last space ' ' => beware, may be informative...
            # (start transitions have no event names: ori="__start__0")
            line = line.rstrip(' ')
            if line == '' or line[0] == '=':
122
                # Blank line
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
                # Skip blank lines and solution separator in some files (=====)
                continue
            elif line[0] != '%':
                if sol == line:
                    # Same frontier places
                    yield sol, sol_steps[sol]

                    # reinit sol
                    sol_steps[sol] = list()
                    continue
                elif sol == '':
                    # First sol
                    sol = line
                else:
                    # Yield previous sol
                    yield sol, sol_steps[sol]
                    sol = line

            elif line[0] == '%':
                # Remove step with only "% "
                step = line.lstrip('% ')

                if step != '':
                    sol_steps[sol].append(step.split(' '))

        # Yield last sol
        yield sol, sol_steps[sol]


def get_transitions(file):
    """Get all transitions in a file model (bcx format).

    :param: Model in bcx format.
    :type: <str>
    :return: A dictionnary of events as keys, and transitions as values.
        Since many transitions can define an event, values are lists.
        Each transition is a tuple with: origin node, final node, attributes
        like label and condition.
        {u'h00': [('Ax', 'n1', {u'label': u'h00[]'}),]
    :rtype: <dict <list <tuple <str>, <str>, <dict <str>: <str>>>>
    """

    parser = MakeModelFromXmlFile(file)

167
168
169
170
171
#    print(dir(parser))
#    print(type(parser))
    #<type 'instance'>
    #['__doc__', '__init__', '__module__', 'get_model', 'handler', 'model', 'parser'

172
173
174
175
176
177
    g = (trans for transition in parser.handler.top_pile.transitions
         for trans in transition)

    transitions = defaultdict(list)

    for trans in g:
178
179
180
181

        # Get the names of clocks
        # Some event have many clocks (like _h_2755) for the same
        # ori/ext entities, so we have to extract them
182
        events = re.findall('(_h_[0-9\.]+)', trans.event)
183
184
185
186
187
188
189
190
191
192
193
194
        for event in events:
            # LOGGER.debug("NEW trans", event)

            # Handle multiple transitions for 1 event
            transitions[event].append(
                (
                    trans.ori.name, trans.ext.name,
                    {
                        'label': event, #+ '[' + trans.condition + ']',
                        'condition': trans.condition,
                    }
                )
195
196
            )

197
198
199
200
201
    LOGGER.info("{} transitions loaded".format(len(transitions)))
    # Return a dict instead of defaultdict to avoid later confusions
    #(masked errors) by searching a transition that was not in the model...
    return dict(transitions)

202

203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def get_frontier_places(transitions):
    """Return frontier places of a model (deducted from its transitions).

    :param arg1: Model's transitions.
        {u'h00': [('Ax', 'n1', {u'label': u'h00[]'}),]
    :type arg1: <dict>
        keys: names of events
        values: list of transitions as tuples (with in/output, and label).
    :return: Set of frontier places.
    :rtype: <set>
    """

    # Get transitions in events
    g = tuple(trans for event in transitions.values() for trans in event)

    # Get input nodes & output nodes
    input_places = {trans[0] for trans in g}
    output_places = {trans[1] for trans in g}

    # Get all places that are not in transitions in the "output" place
    return input_places - output_places


226
227
def rec(tree, inhibitors_nodes):
    """
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
    tree = ('H', 'v', (
        ('F', 'v', 'G'),
        '^',
        (
            ('A', 'v', 'B'),
            '^',
            ('C', 'v', ('D', '^', 'E'))
        )
    ))
    """

#    print("TREE", tree, type(tree), dir(tree))

    if isinstance(tree, str):  # terminal node
        path = [tree]
        solutions = [path]
        return solutions
    if isinstance(tree, SigNotExpr):
        LOGGER.debug("NOT OPERAND: {}, {}".\
            format(
                tree.operand,
                type(tree.operand)
            )
        )
        try:
            current_inhibitors = get_places_from_condition(tree.operand.__str__())
            inhibitors_nodes.update(current_inhibitors)
            LOGGER.debug("INHIBITORS found: " + str(current_inhibitors))

            path = [tree.operand.name]
            solutions = [path]
            return solutions
        except AttributeError:
            tree = tree.operand

    if isinstance(tree, SigIdentExpr):
        path = [tree.name]
        solutions = [path]
        return solutions



    lch = tree.left_h
    op  = tree.operator
    rch = tree.right_h
#    print('OZCJSH:', lch, op, rch, sep='\t\t')
    lpaths = rec(lch, inhibitors_nodes)
    rpaths = rec(rch, inhibitors_nodes)
#    print('VFUENK:', lpaths, rpaths)
    if op == 'or':  # or
#        ret = [*lpaths, *rpaths]
        ret = list(it.chain(lpaths, rpaths))
#        print('RET:', ret)
        return ret
    else:  # and
        assert op == 'and'
#        print(list(it.product(lpaths, rpaths)))
#        raw_input('test')

        ret = list(l + r for l, r in it.product(lpaths, rpaths))
#        print('RET:', ret)
        return ret


def get_places_from_condition(condition):
    """Parse condition string and return all places, regardless of operators.

    :param: Condition string.
    :type: <str>
    :return: Set of places.
    :rtype: <set>
300
301
302
303
304
305
306
307
    """

    replacement = ['and', 'or', 'not', '(', ')']

    for chr in replacement:
        condition = condition.replace(chr, ' ')

    # Must be exempt of unauthorized chars
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
    return {elem for elem in condition.split(' ')
            if elem != ''}


def parse_condition(condition, all_nodes, inhibitors_nodes):
    """

    """

    LOGGER.debug("CONDITION: " + condition)
    # Error Reporter
    err = Reporter()
    tvi = TableVisitor(err)
    # Link the lexer to the model allows to avoid error in Reporter
    # like:  "-> dec -> Undeclared event or state"
    # In practice this is time consuming and useless for what we want to do
    #parser = MakeModelFromXmlFile(BIO_MOLDELS_DIR + "Whole NCI-PID database translated into CADBIOM formalism(and).bcx")
    #parser.get_model().accept(tvi)
    symb_tab = tvi.tab_symb
    # Get tree object from condition string
    cond_sexpr = compile_cond(condition, symb_tab, err)
    # Get all possible paths from the condition
    possible_paths = rec(cond_sexpr, inhibitors_nodes)

    # Prune possible paths according to:
    # - Inhibitor nodes that must be removed because they will never
    # be in the graph.
    # - All nodes in transitions (ori -> ext) because we know all transitions
    # in the graph, so we know which entities can be choosen to validate a path.
    # - All frontier places, that are known entities that can be in conditions
    # (not only in ori/ext) of transitions.
    # So: authorized nodes = frontier_places + transition_nodes - inhibitor nodes
    valid_paths = {tuple(path) for path in possible_paths
                   if (set(path) - inhibitors_nodes).issubset(all_nodes)}

    # Debugging only
    if LOGGER.getEffectiveLevel() == DEBUG:
        LOGGER.debug("INHIBIT NODES: " + str(inhibitors_nodes))
        LOGGER.debug("ALL NODES: " + str(all_nodes))
        LOGGER.debug("POSSIBLE PATHS: " + str(possible_paths))
        LOGGER.debug("VALID PATHS: " + str(valid_paths))

        for path in possible_paths:
            pruned_places = set(path) - inhibitors_nodes
            isinsubset = pruned_places.issubset(all_nodes)
            LOGGER.debug(
                "PRUNED PATH: {}, VALID: {}".format(
                    pruned_places,
                    isinsubset
                )
            )

    assert len(valid_paths) > 0
    if len(valid_paths) > 1:
        LOGGER.warning("Multiple valid paths for: {}:\n{}".format(condition,
                                                                  valid_paths))
    return valid_paths


    # condition expressions contains only node ident
    icv = SigExpIdCollectVisitor()
    lst1 = cond_sexpr.accept(icv)
    print(cond_sexpr)
    print(type(cond_sexpr))
    print(dir(cond_sexpr))
    print("LISTE", lst1)
#    <class 'cadbiom.models.biosignal.sig_expr.SigSyncBinExpr'>
#    'accept', 'get_signals', 'get_ultimate_signals', 'is_bot', 'is_clock',
# 'is_const', 'is_const_false', 'is_ident', 'left_h', 'operator', 'right_h', 'test_equal']

    print(cond_sexpr.get_signals())
#    print(cond_sexpr.get_ultimate_signals())
    print("LEFT", cond_sexpr.left_h)
    print("OPERATOR", cond_sexpr.operator)
    print("RIGHT", cond_sexpr.right_h)


#    ret = treeToTab(cond_sexpr)
#    [set([('((formule', True)])]
#    print("treeToTab", ret)
#    print(type(ret))
#    print(dir(ret))
390
391


392
def build_graph(solution, steps, transitions):
393
394
395
    """Build a graph for the given solution.

        - Get & make all needed edges
396
        - Build graph
397
398
399
400
401
402

    :param arg1: Frontier places.
    :param arg2: List of steps (with events in each step).
    :param arg3: A dictionnary of events as keys, and transitions as values
        (see get_transitions()).
    :type arg1: <str>
403
    :type arg2: <list <list>>
404
    :type arg3: <dict <list <tuple <str>, <str>, <dict <str>: <str>>>>
405
406
407
408
409
410
411
    :return:
        - Networkx graph object.
        - Nodes corresponding to transitions with conditions.
        - All nodes in the model
        - Edges between transition node and nodes in condition
        - Normal transitions without condition
    :rtype: <networkx.classes.digraph.DiGraph>, <list>, <list>, <list>, <list>
412
413
414
415
416
417
418
419
    """

    def filter_transitions(step_event):
        """ Insert a transittion in a transition event if there is a condition.

        => Insert a node in a edge.
        => Link all nodes involved in the condition with this new node.

420
        :param: A list of events (transitions) (from a step in a solution).
421
422
423
            [('Ax', 'n1', {u'label': u'h00[]'}),]
        :type: <tuple>
        :return: Fill lists of edges:
424
425
            edges_with_cond: link to a transition node for
                transition with condition.
426
427
            transition_nodes: add new nodes corresponding to transitions with
                conditions.
428
429
430
            edges_in_cond: Add all edges to nodes linked to a transition
                via the condition (group of nodes involved in the path
                choosen by the solver).
431
432
433
            edges: transition untouched if there is no condition.
        :rtype: None
        """
434
435
436
        assert len(step_event) != 0 # Todo: useful ?

        inhibitors_nodes = set() # Inactivated nodes in paths of conditions
437
438
439
440
441
442
443
444

        for trans in step_event:
            attributes = trans[2]
            ori = trans[0]
            ext = trans[1]
            event = attributes['label'].split('[')[0]

            if attributes['condition'] != '':
445

446
447
448
449
450
451
452
453
                # Add the transition as node
                transition_nodes.append(
                    (
                        event,
                        {
                            'label': attributes['label'],
                            'name': attributes['label'],
                            'color': 'blue',
454
                            'condition': attributes['condition'],
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
                        }
                    )
                )

                # Origin => transition node
                edges_with_cond.append(
                    (
                        ori, event,
                        {
                            'label': ori + '-' + event,
                        }
                    )
                )

                # Transition node => ext
                edges_with_cond.append(
                    (
                        event, ext,
                        {
                            'label': event + '-' + ext,
                        }
                    )
                )

                # Add all transitions to nodes linked via the condition
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
                valid_paths = parse_condition(
                    attributes['condition'],
                    all_nodes,
                    inhibitors_nodes
                )
                for i, path in enumerate(valid_paths):
                    for node in path:
                        edges_in_cond.append(
                            (
                                node, event,
                                {
                                    'label': '{} ({})'.format(
                                        event,
                                        i
                                    ),
#                                    'label': '{} [{}] ({})'.format(
#                                        event,
#                                        ', '.join(path),
#                                        i
#                                    ), #node + '-' + event,
                                    'color': 'red' if node in inhibitors_nodes else 'green',
                                }
                            )
503
504
505
506
507
508
                        )
            else:
                # Normal edges
                edges.append(trans)


509
    # Get & make all needed edges ##############################################
510
    LOGGER.info("BUILD GRAPH FOR SOL: " + str(solution))
511
512
513
514
515
516
517
    LOGGER.debug("STEPS: " + str(steps))
#
#    print(transitions['_h_2755'])
#    print(transitions['_h_4716'])
#    print(transitions['_h_2206'])
#    print(transitions['_h_3426'])
#    exit()
518

519
    frontier_places  = solution.split(' ')
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
    edges_with_cond  = list() # Edges between ori <-> transition node <-> ext
    edges_in_cond    = list() # Edges between transition node and nodes in condition
    transition_nodes = list() # Nodes inserted because of condition in transition
    edges            = list() # Normal transitions without condition

    # Get all nodes in all transitions (origin & ext)
    all_transitions = (transitions[step_event] for step_event in it.chain(*steps))
    transitions_ori_ext = (tuple((trans[0], trans[1])) for trans in it.chain(*all_transitions))
    all_nodes = set(it.chain(*transitions_ori_ext)) | set(frontier_places)

    # Parse all conditions in transitions;
    # add nodes in conditions and transition nodes
    [filter_transitions(transitions[step_event])
        for step_event in it.chain(*steps)]

#    print("edges without cond", edges)
#    print("edges with cond", edges_with_cond)
#    print("transition nodes added", transition_nodes)
#    raw_input("PAUSE")
539
540
541

    # Make Graph ###############################################################
    G = nx.DiGraph()
542
543
    # Add all nodes (some frontier places are in this set)
    G.add_nodes_from(all_nodes, color='grey')
544
    # Add fontier places
545
546
    G.add_nodes_from(frontier_places, color='red')
    # Add all transition nodes
547
548
549
550
551
552
553
554
555
556
    G.add_nodes_from(transition_nodes, color='blue')

    # Node attribute ?
    # print(G.node['h1'])

    # Add all edges
    G.add_edges_from(edges)
    G.add_edges_from(edges_with_cond)
    G.add_edges_from(edges_in_cond)

557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
    return G, transition_nodes, all_nodes, edges_in_cond, edges


def draw_graph(solution, solution_index, G,
               transition_nodes, all_nodes,
               edges_in_cond, edges):
    """Draw graph with colors and export it to graphml format.

    .. note:: Legend:
        - red: frontier places (in solution variable),
        - white: middle edges,
        - blue: transition edges

    :param arg1: Solution string (mostly a set of frontier places).
    :param arg2: Index of the solution in the Cadbiom result file
        (used to distinguish exported filenames).
    :param arg3: Networkx graph object.
    :param arg4: Nodes corresponding to transitions with conditions.
    :param arg5: All nodes in the model
    :param arg6: Edges between transition node and nodes in condition
    :param arg7: Normal transitions without condition
    :type arg1: <str>
579
    :type arg2: <int> or <str>
580
581
582
583
584
585
    :type arg3: <networkx.classes.digraph.DiGraph>
    :type arg4: <list>
    :type arg5: <list>
    :type arg6: <list>
    :type arg7: <list>
    """
586
587
588
589
590
591
592
593

    # Drawing ##################################################################
    # draw_circular(G, **kwargs) On a circle.
    # draw_random(G, **kwargs)   Uniformly at random in the unit square.
    # draw_spectral(G, **kwargs) Eigenvectors of the graph Laplacian.
    # draw_spring(G, **kwargs)   Fruchterman-Reingold force-directed algorithm.
    # draw_shell(G, **kwargs)    Concentric circles.
    # draw_graphviz(G[, prog])   Draw networkx graph with graphviz layout.
594
595
    unzip = lambda l: list(zip(*l))

596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
    pos = nx.circular_layout(G)

    # Legend of conditions in transition nodes
    f = plt.figure(1)
    ax = f.add_subplot(1,1,1)
    text = '\n'.join(
        node_dict['label'] for node_dict in unzip(transition_nodes)[1]
    )
    ax.text(0, 0, text, style='italic', fontsize=10,
            bbox={'facecolor':'white', 'alpha':0.5, 'pad':10})

    # Draw nodes:
    # - red: frontier places (in solution variable),
    # - white: middle edges,
    # - blue: transition edges
611
    frontier_places  = set(solution.split(' '))
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
    def color_map(node):
        # print("color for:", node)
        if node in frontier_places: # Test first (see cond below)
            return 'red'
        if node in unziped_transition_nodes:
            return 'blue'
        if node in all_nodes: # some /all frontier places are in this set
            return 'grey'
        else:
            return 'white'

    # Get a list of transition nodes (without dictionnary of attributes)
    unziped_transition_nodes = unzip(transition_nodes)[0]

    # Color nodes
627
628
629
630
631
632
633
634
635
636
637
638
639
640
    colors = [color_map(node) for node in G.nodes_iter()]
    nx.draw(G, pos=pos, with_labels=True,
            node_color=colors, node_size=1000, alpha=0.5, ax=ax)

    # Draw edges involved in transitions with conditions
    nx.draw_networkx_edges(G, pos, edgelist=edges_in_cond,
                           edge_color='b', width=2, alpha=0.5)

    # Draw labels for normal transitions (move pos to the end of the arrow)
    # ex: [('Ax', 'n1', {u'condition': u'', u'label': u'h00[]'}),]
    edges_labels = {(edge[0], edge[1]): edge[2]['label'] for edge in edges}
    nx.draw_networkx_edge_labels(G, pos, edges_labels, label_pos=0.3)

    # Save & show
641
    date = dt.datetime.now().strftime("%H-%M-%S")
642
643
644
645
    plt.legend()
#    plt.savefig(GRAPHS_DIR + date + '_' + sol[:75] + ".svg", format="svg")
#    plt.show()

646
647
    nx.write_graphml(
        G,
648
649
650
        "{}{}_{}_{}.graphml".format(
            GRAPHS_DIR, date, solution_index, solution[:75]
        )
651
    )
652
653
654
655
656


def process_solutions(sol_steps, transitions):
    """Build a graph per solution"""

657
    for sol_index, (sol, steps) in enumerate(sol_steps):
658

659
        draw_graph(sol, sol_index, *build_graph(sol, steps, transitions))
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733


def test_main():
    """Test"""

    # chart_model.py
    # chart_xml.py
    parser = MakeModelFromXmlFile(BIO_MOLDELS_DIR + "mini_test_publi.bcx")
    print(type(parser.parser))
    print(dir(parser))
    print("HANDLER")
    print(dir(parser.handler))
    print(dir(parser.parser))
    print(dir(parser.get_model()))
    print("ICI")
#    print(parser.get_model().get_simple_node())
    print(parser.handler.node_dict)
    print(parser.handler.top_pile)
    print(parser.handler.pile_dict)
    print(parser.handler.transition.event)
    print(type(parser.handler.top_pile))

    transitions = dict()
    for transition in parser.handler.top_pile.transitions:
        # print(type(transition)) => list
        for trans in transition:
            # 'action', 'activated', 'clean', 'clock', 'condition', 'event',
            # 'ext', 'ext_coord', 'fact_ids', 'get_influencing_places',
            # 'get_key', 'is_me', 'macro_node', 'name', 'note', 'ori',
            # 'ori_coord', 'remove', 'search_mark', 'selected', 'set_action',
            # 'set_condition', 'set_event', 'set_name', 'set_note'

# {'name': '', 'clock': None, 'selected': False, 'activated': False,
# 'search_mark': False, 'note': '', 'ext': <cadbiom.models.guard_transitions.chart_model.CSimpleNode object at 0x7f391c7406d0>,
# 'ext_coord': 0.0, 'ori': <cadbiom.models.guard_transitions.chart_model.CSimpleNode object at 0x7f391c740650>,
# 'action': u'', 'macro_node': <cadbiom.models.guard_transitions.chart_model.CTopNode object at 0x7f391c7db490>,
# 'ori_coord': 0.0, 'event': u'h5', 'condition': u'', 'fact_ids': []}
            # print(dir(trans))
            print("NEW trans", trans.event)
#            print(trans.__dict__)
#            print(trans.name, trans.clock, trans.selected, trans.activated,
#                  trans.search_mark, trans.note, trans.ext, trans.ext_coord,
#                  trans.ori, trans.action, trans.macro_node, trans.ori_coord,
#                  trans.event, trans.condition, trans.fact_ids
#                  )

            transitions[trans.event] = trans.condition

#print("ORI", trans.ori.__dict__)
#{'name': 'n4', 'yloc': 0.906099768906, 'selected': False,
#'father': <cadbiom.models.guard_transitions.chart_model.CTopNode object at 0x7f09eed91490>,
#'xloc': 0.292715433748, 'search_mark': False, 'was_activated': False,
#'incoming_trans': [<cadbiom.models.guard_transitions.chart_model.CTransition object at 0x7f09eecf67d0>],
#'model': <cadbiom.models.guard_transitions.chart_model.ChartModel object at 0x7f09ef2cf3d0>,
#'outgoing_trans': [<cadbiom.models.guard_transitions.chart_model.CTransition object at 0x7f09eecf6850>],
#'activated': False, 'hloc': 1.0}
            print("ORI", trans.ori.name)
            try:
                print("ori INCO", [(tr.event, tr.condition) for tr in trans.ori.incoming_trans])
            except: pass
            try:
                print("ori OUTGO", [(tr.event, tr.condition) for tr in trans.ori.outgoing_trans])
            except: pass
            print("EXT", trans.ext.name)
            try:
                print("ext INCO", [(tr.event, tr.condition) for tr in trans.ext.incoming_trans])
            except: pass
            try:
                print("ext OUTGO", [(tr.event, tr.condition) for tr in trans.ext.outgoing_trans])
            except: pass
    print(transitions)



734
735
def sol_digging(sol_steps, transitions):
    """Get steps and all transitions and write all data for each step in a file.
736

737
738
739
740
741
742
743
744
745
746
747
748
749
750
    This is an exemple to quickly search all transition attributes involved in a
    complete MAC.

    .. note:: Output file: output.txt

    :param arg1: List of steps involved in a solution. See load_solutions().
    :param arg2: A dictionnary of events as keys, and transitions as values.
        Since many transitions can define an event, values are lists.
        Each transition is a tuple with: origin node, final node, attributes
        like label and condition.
        {u'h00': [('Ax', 'n1', {u'label': u'h00[]'}),]
        See get_transitions().
    :type arg1: <list>
    :type arg2: <dict <list <tuple <str>, <str>, <dict <str>: <str>>>>
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
    """

    def write_transition_def(step_event):
        """Write each event on a new line"""

        # Many transitions per event (ex: complex dissociation)
        for trans in step_event:

            # ori="JUN_nucl_gene" ext="JUN_nucl" event="_h_391"
            line = 'ori="{}" ext="{}" event="{}" condition="{}"'.format(
                trans[0],
                trans[1],
                trans[2]['label'].split('[')[0],
                trans[2]['condition']
            )
            fd.write(line + '\n')


    with open("output.txt", "w") as fd:
        for sol, steps in sol_steps:

            # Solution header
            fd.write(sol + '\n')

            for step in steps:

                for event in step:

                    step_event = transitions[event]
                    # print(step_event)
                    if len(step_event) == 0:
                        fd.write("ERROR for event: " + event + '\n')
                    else:
                        write_transition_def(transitions[event])

                # Step separation
                fd.write('\n')
            # Sol separation
            fd.write('\n====================================================\n')

791

792
def main(model_file, solution_file):
793
    """Entry point for parse_trajectories"""
794
795
796
797
798
799
800

    process_solutions(
        load_solutions(solution_file),
        get_transitions(model_file)
    )


801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
def graph_isomorph_test(model_file_1, model_file_2, make_graphs=False):
    """Entry point for model consistency checking.

    This functions checks if the 2 given models have the same topology,
    nodes & edges attributes/roles.

    .. note:: cf graphmatcher
        https://networkx.github.io/documentation/development/reference/generated/networkx.algorithms.isomorphism.categorical_edge_match.html

    :param arg1: File for the model 1.
    :param arg2: File for the model 2.
    :type arg1: <str>
    :type arg2: <str>
    """

    import networkx.algorithms.isomorphism as iso

    # Load transitions in the models
    # Transitions structure format:
    # {u'h00': [('Ax', 'n1', {u'label': u'h00[]'}),]
    transitions_1 = get_transitions(model_file_1)
    transitions_2 = get_transitions(model_file_2)

    # Get all frontier places in the models
    # (places that are never in output position in all transitions)
    front_places_1 = " ".join(get_frontier_places(transitions_1))
    front_places_2 = " ".join(get_frontier_places(transitions_2))

    # Build graphs & get networkx object
    # We give all events in the model as a list of steps
    # So we simulate a cadbiom solution (with all events in the model).
    res_1 = build_graph(front_places_1, [transitions_1.keys()], transitions_1)
    G1 = res_1[0]
    res_2 = build_graph(front_places_2, [transitions_2.keys()], transitions_2)
    G2 = res_2[0]

    # Draw graph
    if make_graphs:
        draw_graph(front_places_1, "first", *res_1)
        draw_graph(front_places_2, "second", *res_2)

    # Checking
    LOGGER.info("Topology checking: " +
                str(nx.is_isomorphic(G1, G2)))
    nm = iso.categorical_node_match('color', 'grey')
    LOGGER.info("Nodes checking: " +
                str(nx.is_isomorphic(G1, G2, node_match=nm)))
    em = iso.categorical_edge_match('color', '')
    LOGGER.info("Edges checking: " +
                str(nx.is_isomorphic(G1, G2, edge_match=em)))


853
if __name__ == "__main__":
854
855
856
857
858
859
860
861
862
863
#    cond = "((((CXCL10_CXCR3_intToMb and not(CCL11_CXCR3_intToMb)))or((CXCL10_CXCR3_intToMb and not(CXCL13_CXCR3_intToMb))))or(CXCL9_11_CXCR3_active_intToMb))or(CXCL4_CXCR3_intToMb)"
#    cond = "((((A and not(B)))or((A and not(C))))or(D))or(E)"
#    cond = "(A and not(B))"
#    cond = "(A and not(B))"
#    cond = "(((((A)or(B))or(C))or(B))or(D))or(E)"
#    cond = "((((not(ATF2_JUND_macroH2A_nucl))or(Fra1_JUND_active_nucl))or(Fra1_JUN_active_nucl))or(TCF4_betacatenin_active_nucl))or(JUN_FOS_active_nucl)"
#    cond = "((A and B and C)) and (not((D and E and F and G)))"
#    ret = parse_condition(cond)
#    print(ret)

864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
    process_solutions(load_solutions(LOG_DIR + "../run/Whole NCI-PID database translated into CADBIOM formalism(and)_SRP9_cam_complete.txt"),
                get_transitions(BIO_MOLDELS_DIR + "Whole NCI-PID database translated into CADBIOM formalism(and).bcx"))
    exit()


    #sort_solutions("/media/DATA/Projets/dyliss_tgf/cadbiom/run/Whole NCI-PID database translated into CADBIOM formalism(and)_SRP9_cam_complete.txt")
    #sort_solutions("/media/DATA/Projets/dyliss_tgf/cadbiom/run/Whole NCI-PID database translated into CADBIOM formalism(and)_SRP9_cam.txt")
    #sort_solutions("/media/DATA/Projets/dyliss_tgf/cadbiom/run/pid_and_clock_no_perm_p21corrected_start_SRP9_complete.txt")
    #exit()

#    cond = "((((not(ATF2_JUND_macroH2A_nucl))or(Fra1_JUND_active_nucl))or(Fra1_JUN_active_nucl))or(TCF4_betacatenin_active_nucl))or(JUN_FOS_active_nucl)"
#    ret = parse_condition(cond)
#    print(ret)
#    exit()

#    sol_steps = load_solutions(LOG_DIR + "../run/pid_and_clock_SRP9_cam_complete_o.txt")
#    g = [sol_step for sol_step in sol_steps]
#    print(g)
#    exit()

884
    sol_digging(load_solutions(LOG_DIR + "../run/pid_and_clock_no_perm_p21corrected_start_SRP9_complete.txt"),
885
886
887
             get_transitions(BIO_MOLDELS_DIR + "Whole NCI-PID database translated into CADBIOM formalism(and).bcx"))
    exit()

888
    sol_digging(load_solutions(LOG_DIR + "sols_new_solver.txt"),
889
890
891
892
893
894
895
896
897
898
             get_transitions(BIO_MOLDELS_DIR + "mini_test_publi.bcx"))
    exit()
    process_solutions(load_solutions(LOG_DIR + "sols_new_solver.txt"),
                      get_transitions(BIO_MOLDELS_DIR + "mini_test_publi.bcx"))
    exit()

#    build_graph(load_solutions(LOG_DIR + "../run/pid_and_clock_SRP9_cam_complete_o.txt"),
#                get_transitions(BIO_MOLDELS_DIR + "pid_and_clock.bcx"))
    process_solutions(load_solutions(LOG_DIR + "../run/pid_and_clock_SRP9_cam_complete.txt"),
                get_transitions(BIO_MOLDELS_DIR + "pid_and_clock.bcx"))