1 % (c) 2019-2023 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4 :- module(bliss_interface, [init_bliss_interface/0,
5 init_directed_graph/0,
6 init_undirected_graph/0,
7 add_node/2,
8 add_node/3,
9 add_edge/2,
10 find_automorphisms/1,
11 get_canonical_form/1,
12 graph_to_dot_file/1]).
13
14 :- use_module(library(random), [random/3]).
15
16 %% find_automorphisms(-PermGroup).
17 % Find graph automorphisms yielding a permutation group,
18 % each permutation in the group is given by a generator which is represented in cycle notation.
19
20 %% get_canonical_form(-CanonicalForm).
21 % Fetch the canonical labeling of the graph in cycle notation.
22
23 foreign_resource('bliss_interface', [init_directed_graph_wrapper,
24 init_undirected_graph_wrapper,
25 add_node_wrapper,
26 add_edge_wrapper,
27 find_automorphisms,
28 get_canonical_form]).
29
30 foreign(init_directed_graph_wrapper, c, init_directed_graph_wrapper).
31 foreign(init_undirected_graph_wrapper, c, init_undirected_graph_wrapper).
32 foreign(add_node_wrapper, c, add_node_wrapper(+integer, [-integer])).
33 foreign(add_edge_wrapper, c, add_edge_wrapper(+integer, +integer)).
34 foreign(find_automorphisms, c, find_automorphisms([-term])).
35 foreign(get_canonical_form, c, get_canonical_form([-term])).
36
37 :- dynamic is_initialised/0, amount_of_nodes/1, dot_edge_atom/1, is_directed_graph/0, node_color/2, node_label/2, color_code/2.
38 :- volatile is_initialised/0, amount_of_nodes/1, dot_edge_atom/1, is_directed_graph/0, node_color/2, node_label/2, color_code/2.
39
40 %% init_bliss_interface.
41 init_bliss_interface :-
42 \+ is_initialised,
43 reset_state,
44 !,
45 Error = '~n! Could not load ~w library!~n! Check that it is available in the lib folder of ProB.~n~n',
46 catch(load_foreign_resource(library(bliss_interface)),_,
47 (format(user_error, Error, [library(bliss_interface)]), fail)),
48 asserta(is_initialised).
49 init_bliss_interface.
50
51 %% init_directed_graph.
52 % Clear graph and start creating a directed graph.
53 init_directed_graph :-
54 reset_state,
55 asserta(is_directed_graph),
56 init_directed_graph_wrapper.
57
58 %% init_undirected_graph.
59 % Clear graph and start creating an undirected graph.
60 init_undirected_graph :-
61 reset_state,
62 init_undirected_graph_wrapper.
63
64 %% add_node(+Color, -Id).
65 % Create a new node with the given colour and return the id.
66 add_node(Color, Id) :-
67 inc_nodes,
68 add_node_wrapper(Color, Id),
69 asserta(node_color(Id,Color)).
70
71 %% add_node(+Label, +Color, -Id).
72 % Label is used for dot graph visualization.
73 add_node(Label, Color, Id) :-
74 add_node(Color, Id),
75 asserta(node_label(Id,Label)).
76
77 %% add_edge(+Id1, +Id2).
78 % Create an edge between two ids.
79 add_edge(Id1, Id2) :-
80 amount_of_nodes(Nodes),
81 Id1 >= 0,
82 Id2 >= 0,
83 Id1 < Nodes,
84 Id2 < Nodes,
85 add_dot_edge_atom(Id1, Id2),
86 add_edge_wrapper(Id1, Id2).
87
88 %% graph_to_dot_file(+FilePath).
89 graph_to_dot_file(FilePath) :-
90 open(FilePath, write, S, [encoding(utf8)]),
91 dot_file_header(S),
92 dot_node_attributes(S),
93 dot_file_edges(S),
94 write(S, '}'),
95 close(S).
96
97 inc_nodes :-
98 retract(amount_of_nodes(Old)),
99 New is Old + 1,
100 asserta(amount_of_nodes(New)).
101
102 add_dot_edge_atom(Id1, Id2) :-
103 number_to_atom(Id1, AId1),
104 number_to_atom(Id2, AId2),
105 atom_concat(AId1, ' -> ', T1),
106 atom_concat(T1, AId2, T2),
107 atom_concat(T2, ';', DotEdgeAtom),
108 ( dot_edge_atom(DotEdgeAtom)
109 -> true
110 ; asserta(dot_edge_atom(DotEdgeAtom))
111 ).
112
113 reset_state :-
114 retractall(is_directed_graph),
115 retractall(amount_of_nodes(_)),
116 retractall(node_color(_,_)),
117 retractall(node_label(_,_)),
118 retractall(dot_edge_atom(_)),
119 asserta(amount_of_nodes(0)).
120
121 dot_node_attributes(S) :-
122 findall((Id,Color), node_color(Id, Color), NodeColors),
123 retractall(color_code(_,_)),
124 dot_node_attributes(NodeColors, S).
125
126 dot_node_attributes([], _).
127 dot_node_attributes([(Id,Color)|T], S) :-
128 get_color_code_for_id(Id, Color, ColorCode),
129 number_to_atom(Id, AId),
130 atom_concat(AId, ' [style="filled" color="', T1),
131 atom_concat(T1, ColorCode, T2),
132 ( node_label(Id, Label)
133 -> atom_concat(T2, '" label="', T3),
134 atom_concat(T3, Label, T4),
135 atom_concat(T4, '"]', DotNodeColor)
136 ; atom_concat(T2, '"]', DotNodeColor)
137 ),
138 write(S, DotNodeColor),
139 nl(S),
140 dot_node_attributes(T, S).
141
142 get_color_code_for_id(Id, Color, ColorCode) :-
143 node_color(Id, Color),
144 ( color_code(Color, TColorCode)
145 -> ColorCode = TColorCode
146 ; random(100000, 999999, RGB),
147 number_to_atom(RGB, ARGB),
148 atom_concat('#', ARGB, ColorCode),
149 asserta(color_code(Color, ColorCode))
150 ).
151
152 number_to_atom(Number, Atom) :-
153 number_codes(Number, C),
154 atom_codes(Atom, C).
155
156 dot_file_edges(S) :-
157 findall(DotEdgeAtom, dot_edge_atom(DotEdgeAtom), DotEdgeAtoms),
158 dot_file_edges(DotEdgeAtoms, S).
159
160 dot_file_edges([], _).
161 dot_file_edges([DotEdgeAtom|T], S) :-
162 write(S, DotEdgeAtom),
163 nl(S),
164 dot_file_edges(T, S).
165
166 dot_file_header(S) :-
167 is_directed_graph,
168 !,
169 write(S, 'digraph bliss_graph {'),
170 nl(S).
171 dot_file_header(S) :-
172 write(S, 'graph bliss_graph {'),
173 nl(S).