1 | % (c) 2018-2022 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 | ||
5 | :- module(synthesis_util, [get_distinguishing_state_or_trans/8, | |
6 | get_valid_transitions_for_operation/5, | |
7 | get_invariant_violating_transition/6, | |
8 | get_valid_and_invalid_equality_predicates_for_operation/6, | |
9 | get_distinct_states/5, | |
10 | get_valid_and_invalid_equality_predicates_for_invariants/4, | |
11 | get_violated_and_valid_machine_invariants/4, | |
12 | get_invariant_violating_example/4, | |
13 | get_involved_variables/4, | |
14 | get_invariant_violating_vars_from_examples/3, | |
15 | get_single_constant_or_set_operator_id/3, | |
16 | get_input_output_nodes_from_bindings/3, | |
17 | get_output_nodes_from_bindings/2, | |
18 | get_location_var_node_by_info_term/3, | |
19 | get_location_var_node_by_name/3, | |
20 | get_var_node_by_info_term/3, | |
21 | get_operation_params_from_ids/2, | |
22 | set_operation_params_domain/2, | |
23 | get_guard_list_from_operation/2, | |
24 | get_current_vars_from_example/2, | |
25 | get_input_nodes_from_bindings/2, | |
26 | get_varname_from_node_info/2, | |
27 | get_varname_from_id_node/2, | |
28 | get_valid_states/3, | |
29 | get_machinevars_by_name/2, | |
30 | get_precondition_from_operation_body/2, | |
31 | get_binding_for_name_or_value/3, | |
32 | get_valid_inputs_for_operation/3, | |
33 | b_get_typing_predicate_for_vars/2, | |
34 | b_get_typed_invariant_from_machine/1, | |
35 | b_compute_expression_nowf_no_bindings/2, | |
36 | expand_state_cache_if_guard_and_solution/6, | |
37 | add_distinguishing_states_from_cache/6, | |
38 | conjunct_previous_invariant_or_guard_if_necessary/4, | |
39 | check_transitions_against_machine_operations/4, | |
40 | check_transition_against_machine_operations/5, | |
41 | check_negative_inputs_against_operation_precondition/2, | |
42 | replace_preds_by_subst_if_neccessary/3, | |
43 | replace_precondition_of_operation/3, | |
44 | contains_equivalent_state/3, | |
45 | find_typed_identifier_uses_with_info/2, | |
46 | delete_numbers_from_atom/2, | |
47 | create_equality_nodes_from_example/2, | |
48 | adapt_machine_code_for_operations/2, | |
49 | % b_synthesis | |
50 | b_get_machinevars_with_operation_params/1, | |
51 | remove_prime/2, | |
52 | parallel_to_conjunction/2, | |
53 | prime_or_unprime_variables_in_ast/4, | |
54 | parse_and_typecheck_value/2, | |
55 | keep_asserted_initial_examples/6, | |
56 | prepare_solution/6, | |
57 | finalize_synthesized_action/3, | |
58 | split_input_output_tuples/3, | |
59 | prepare_synthesized_guards_for_action/2, | |
60 | % | |
61 | mark_examples_as_initial/4, | |
62 | reduce_input_to_violating_vars/3, | |
63 | is_ast/1, | |
64 | zip/3, | |
65 | set_type_if_necessary/2]). | |
66 | ||
67 | :- use_module(synthesis(logging)). | |
68 | ||
69 | :- use_module(probsrc(bsyntaxtree)). | |
70 | :- use_module(probsrc(error_manager), [add_error/3]). | |
71 | :- use_module(probsrc(b_interpreter), [b_test_boolean_expression_cs/5,b_compute_expression_nowf/6]). | |
72 | :- use_module(probsrc(solver_interface), [solve_predicate/3, solve_predicate/5]). | |
73 | :- use_module(smt_solvers_interface(smt_solvers_interface), [smt_solve_predicate/4]). | |
74 | :- use_module(probsrc(btypechecker), [prime_identifier/2,is_primed_id/1,btype/7]). | |
75 | :- use_module(probsrc(bmachine)). | |
76 | :- use_module(extrasrc(before_after_predicates), | |
77 | [before_after_predicate_with_equalities/3,before_after_predicate_for_operation/2]). | |
78 | :- use_module(probsrc(parsercall), [parse/3]). | |
79 | :- use_module(probsrc(translate), [translate_bexpression/2, | |
80 | translate_bexpression_to_codes/2, | |
81 | translate_subst_or_bexpr/2, | |
82 | translate_machine/3, | |
83 | generate_typing_predicates/2]). | |
84 | ||
85 | :- use_module(library(lists)). | |
86 | :- use_module(library(sets), [subtract/3,intersection/3]). | |
87 | ||
88 | :- use_module(extension('plspec/plspec/plspec_core')). | |
89 | ||
90 | :- set_prolog_flag(double_quotes, codes). | |
91 | ||
92 | b_get_machinevars_with_operation_params(MachineVarsWithParams) :- | |
93 | b_get_machine_variables(MachineVars), | |
94 | b_get_machinevars_with_operation_params_aux(MachineVars, MachineVarsWithParams). | |
95 | b_get_machinevars_with_operation_params_aux(MachineVars, MachineVarsWithParams) :- | |
96 | % TODO: fix this spider warning, do not access dynamic additional_machinevars/1 in this module | |
97 | catch(b_synthesis:additional_machinevars(AdditionalMachineVars), error(existence_error(_,_),_), fail), | |
98 | !, | |
99 | append(MachineVars, AdditionalMachineVars, MachineVarsWithParams). | |
100 | b_get_machinevars_with_operation_params_aux(MachineVars, MachineVars). | |
101 | ||
102 | parse_and_typecheck_value(Value, ValueAst) :- | |
103 | atom_codes(Value, L), | |
104 | parse(expression, L, Parsed), | |
105 | btype(bsynthesis, Parsed, openenv(_,env_empty), _, TempAst, tr(_,_,_,_), tr(_,_,_,_)), | |
106 | set_type_if_necessary(TempAst, ValueAst). | |
107 | ||
108 | get_machinevars_by_name(VarNames, CurrentVars) :- | |
109 | b_get_machinevars_with_operation_params(MachineVarsWithParams), | |
110 | get_machinevars_by_name_aux(VarNames, MachineVarsWithParams, [], CurrentVars). | |
111 | get_machinevars_by_name_aux([], _, Acc, Acc). | |
112 | get_machinevars_by_name_aux([VarName|T], MachineVars, Acc, CurrentVars) :- | |
113 | member(b(identifier(VarName),Type,Info), MachineVars), | |
114 | !, | |
115 | get_machinevars_by_name_aux(T, MachineVars, [b(identifier(VarName),Type,Info)|Acc], CurrentVars). | |
116 | get_machinevars_by_name_aux([VarName|_], _, _, _) :- | |
117 | add_error(get_machinevars_by_name, 'This is not a machine variable: ',[VarName]), | |
118 | !, | |
119 | fail. | |
120 | ||
121 | % True if the given set of states contains an equivalent state. | |
122 | contains_equivalent_state(State, States, EquivalentState) :- | |
123 | length(State, VarAmount), | |
124 | contains_equivalent_state(VarAmount, State, States, EquivalentState). | |
125 | contains_equivalent_state(_, _, [], _) :- | |
126 | !, | |
127 | fail. | |
128 | contains_equivalent_state(VarAmount, State, [ExampleState|_], EquivalentState) :- | |
129 | length(ExampleState, ExampleVarAmount), | |
130 | VarAmount = ExampleVarAmount, | |
131 | create_equality_nodes_from_example(State, StateEqualityNodes), | |
132 | create_equality_nodes_from_example(ExampleState, ExampleStateEqualityNodes), | |
133 | get_distinct_independent_types_constraint_for_enums(DistinctEnumSetsConstraint), | |
134 | ( DistinctEnumSetsConstraint == b(truth,pred,[]) | |
135 | -> | |
136 | append([StateEqualityNodes,ExampleStateEqualityNodes], EqualityNodes) | |
137 | ; append([DistinctEnumSetsConstraint,StateEqualityNodes,ExampleStateEqualityNodes], EqualityNodes) | |
138 | ), | |
139 | conjunct_predicates(EqualityNodes, Equality), | |
140 | solve_predicate(Equality, _, Solution), | |
141 | Solution = solution(_), | |
142 | !, | |
143 | EquivalentState = ExampleState. | |
144 | contains_equivalent_state(VarAmount, State, [_|T], EquivalentState) :- | |
145 | contains_equivalent_state(VarAmount, State, T, EquivalentState). | |
146 | ||
147 | get_distinct_independent_types_constraint_for_enums(Constraint) :- | |
148 | findall(MachineSetName, b_get_machine_set(MachineSetName), MachineSetNames), | |
149 | get_distinct_independent_types_constraint_for_enums(MachineSetNames, [], ConstraintList), | |
150 | conjunct_predicates(ConstraintList, Constraint). | |
151 | ||
152 | get_distinct_independent_types_constraint_for_enums([], Acc, Acc). | |
153 | get_distinct_independent_types_constraint_for_enums([MachineSetName|T], Acc, ConstraintList) :- | |
154 | b_get_named_machine_set(MachineSetName, SetValues), | |
155 | findall(SetValueNode, ( member(SetValue, SetValues), | |
156 | SetValueNode = b(identifier(SetValue),global(MachineSetName),[]) | |
157 | ), SetValueNodes), | |
158 | findall(Inequality, ( member(Node1, SetValueNodes), | |
159 | member(Node2, SetValueNodes), | |
160 | Node1 \= Node2, | |
161 | Inequality = b(not_equal(Node1,Node2),pred,[]) | |
162 | ), Inequalities), | |
163 | get_distinct_independent_types_constraint_for_enums(T, [Inequalities|Acc], ConstraintList). | |
164 | ||
165 | % seq(_), string_set, integer_set, bool_set,.. | |
166 | % Also needed in location_vars_to_program. | |
167 | get_single_constant_or_set_operator_id(seq-Type, Out, b(seq(Set),set(seq(NodeType)),[])) :- | |
168 | get_single_constant_or_set_operator_id(Type, _, Set), | |
169 | Set = b(_,TInnerType,_), | |
170 | TInnerType =.. [_|InnerType], | |
171 | NodeType =.. InnerType, | |
172 | unique_typed_id("o", set(seq(NodeType)), TempOut), | |
173 | % we add the internal type of infinite set constants here | |
174 | % so we are able to call this predicate when converting location variables to ASTs later on | |
175 | add_texpr_infos(TempOut, [synthesis(type,seq-Type)], Out). | |
176 | get_single_constant_or_set_operator_id(constant-Type, Out, ConstantNode) :- | |
177 | get_constant_node_or_id(constant-Type, ConstantNode), | |
178 | unique_typed_id("o", Type, TempOut), | |
179 | add_texpr_infos(TempOut, [synthesis(type,Type)], Out). | |
180 | ||
181 | get_constant_node_or_id(string_set, b(string_set,set(string),[])). | |
182 | get_constant_node_or_id(bool_set, b(bool_set,set(boolean),[])). | |
183 | get_constant_node_or_id(integer_set, b(integer_set('INTEGER'),set(integer),[])). | |
184 | get_constant_node_or_id(integer_set_min_max, b(integer_set('INT'),set(integer),[])). | |
185 | get_constant_node_or_id(natural1, b(integer_set('NATURAL1'),set(integer),[])). | |
186 | get_constant_node_or_id(natural, b(integer_set('NATURAL'),set(integer),[])). | |
187 | get_constant_node_or_id(implementable_natural1, b(integer_set('NAT1'),set(integer),[])). | |
188 | get_constant_node_or_id(implementable_natural, b(integer_set('NAT'),set(integer),[])). | |
189 | get_constant_node_or_id(constant-Type, Constant) :- | |
190 | unique_typed_id("constant", Type, TempConstant), | |
191 | add_texpr_infos(TempConstant, [synthesis(enumerated_constant),synthesis(type,Type)], Constant). | |
192 | ||
193 | add_distinguishing_states_from_cache(action, _, PosInput, NegInput, PosInput, NegInput) :- | |
194 | !. | |
195 | add_distinguishing_states_from_cache(_, (DistIn,DistOut), PosInput, NegInput, NewPosInput, NewNegInput) :- | |
196 | append(DistIn, PosInput, TempPosInput), | |
197 | append(DistOut, NegInput, TempNegInput), | |
198 | remove_dups(TempPosInput, NewPosInput), | |
199 | remove_dups(TempNegInput, NewNegInput). | |
200 | ||
201 | b_test_boolean_expression_wf_no_bindings(BooleanExpr, Result) :- | |
202 | find_identifier_uses(BooleanExpr, [], UsedIdNames), | |
203 | findall(bind(UsedIdName,_), member(UsedIdName, UsedIdNames), Bindings), | |
204 | b_test_boolean_expression_cs(BooleanExpr, Bindings, Result,'synthesis',0). | |
205 | ||
206 | b_compute_expression_nowf_no_bindings(Expression, Result) :- | |
207 | find_identifier_uses(Expression, [], UsedIdNames), | |
208 | findall(bind(UsedIdName,_), member(UsedIdName, UsedIdNames), Bindings), | |
209 | b_compute_expression_nowf(Expression, [], Bindings, Result,'synthesis',0). | |
210 | ||
211 | % If the domain is enumerated from 1 to n we suppose set(couple(integer,_)) is a sequence. | |
212 | is_seq(b(Node,Type,Info)) :- | |
213 | ( Type = set(couple(integer,_)) | |
214 | ; Type = seq(_) | |
215 | ), | |
216 | b_test_boolean_expression_wf_no_bindings(b(equal(b(domain(b(Node,Type,Info)),set(integer),[]), | |
217 | b(interval(b(integer(1),integer,[]), | |
218 | b(size(b(Node,Type,Info)),integer,[])),set(integer),[])),pred,[]), []). | |
219 | ||
220 | get_distinguishing_state_or_trans(DistTransitionCache, CurrentVars, Type, ValidInvariantsOrGuards, AST1, | |
221 | AST2, NewDistTransitionCache, Distinguishing) :- | |
222 | % add typing of machine variables to the invariant for consistency with AtelierB and to find a solution referring to | |
223 | % all machine variables | |
224 | b_get_typed_invariant_from_machine(TypedInvariant), | |
225 | % add WD-constraints for sequences | |
226 | find_typed_identifier_uses(AST1, AST1Nodes), | |
227 | find_typed_identifier_uses(AST2, AST2Nodes), | |
228 | findall(Seq, ( member(Seq, AST1Nodes), | |
229 | is_seq(Seq) | |
230 | ), Seq1), | |
231 | findall(Seq, ( member(Seq, AST2Nodes), | |
232 | is_seq(Seq) | |
233 | ), Seq2), | |
234 | append(Seq1, Seq2, TSequences), | |
235 | remove_dups(TSequences, Sequences), | |
236 | maplist(sequence_well_definedness_constraint, Sequences, SeqConstraintList), | |
237 | conjunct_predicates(SeqConstraintList, SeqConstraints), | |
238 | get_distinct_independent_types_constraint_for_enums(DistinctEnumSetsConstraint), | |
239 | preferences:set_preference(randomise_enumeration_order, true), | |
240 | get_distinguishing_state_or_trans_aux(DistinctEnumSetsConstraint, DistTransitionCache, Type, ValidInvariantsOrGuards, | |
241 | b(conjunct(SeqConstraints,b(conjunct(b(negation( | |
242 | b(equivalence(AST1,AST2),pred,[])),pred,[]),TypedInvariant),pred,[])),pred,[]), Distinguishing), | |
243 | expand_transition_cache_if_solution(Type, DistTransitionCache, CurrentVars, Distinguishing, NewDistTransitionCache), | |
244 | synthesis_log(distinguishing_transition_cache_changed(DistTransitionCache,NewDistTransitionCache)). | |
245 | % When synthesizing an action only search for a distinguishing transition that doesn't violate the | |
246 | % so far synthesized guard (in case there is one). | |
247 | % Furthermore, we exclude already visited distinguishing transitions using the cache in order to | |
248 | % prevent cycles when a distinguishing transition's output is changed by the user | |
249 | % (otherwise we would find the same transition over and over again because the one with an adapted | |
250 | % output is added to the examples). | |
251 | get_distinguishing_state_or_trans_aux(DistinctEnumSetsConstraint, DistTransitionCache, action, ValidInvariantsOrGuards, Constraint, Distinguishing) :- | |
252 | ValidInvariantsOrGuards \= any, | |
253 | ValidInvariantsOrGuards \= none, | |
254 | !, | |
255 | DistConstraint = b(conjunct(DistinctEnumSetsConstraint,b(conjunct(b(conjunct(Constraint,ValidInvariantsOrGuards),pred,[]),DistTransitionCache),pred,[])),pred,[]), | |
256 | synthesis_log(distinguishing_constraint(action,DistConstraint)), | |
257 | solve_distinguishing_constraint_with_fallback(DistConstraint, TDistinguishing), | |
258 | get_solution_or_none(TDistinguishing, Distinguishing). | |
259 | get_distinguishing_state_or_trans_aux(DistinctEnumSetsConstraint, DistTransitionCache, action, _, Constraint, Distinguishing) :- | |
260 | !, | |
261 | DistConstraint = b(conjunct(DistinctEnumSetsConstraint,b(conjunct(Constraint,DistTransitionCache),pred,[])),pred,[]), | |
262 | synthesis_log(distinguishing_constraint(action,Constraint)), | |
263 | solve_distinguishing_constraint_with_fallback(DistConstraint, TDistinguishing), | |
264 | get_solution_or_none(TDistinguishing, Distinguishing). | |
265 | % DistTransitionCache is empty when synthesizing guard/invariant | |
266 | get_distinguishing_state_or_trans_aux(DistinctEnumSetsConstraint, _, Type, _, Constraint, Distinguishing) :- | |
267 | synthesis_log(distinguishing_constraint(Type,Constraint)), | |
268 | solve_distinguishing_constraint_with_fallback(b(conjunct(DistinctEnumSetsConstraint,Constraint),pred,[]), TDistinguishing), | |
269 | get_solution_or_none(TDistinguishing, Distinguishing). | |
270 | ||
271 | % Solving the distinguishing constraint maybe causes an integer overflow due to infinite domains which | |
272 | % possibly ends in not finding any solution, i.e., neither true nor false. | |
273 | % Therefore, we try different solvers if no solution can be found. | |
274 | % Of course, we do not want to enumerate infinite domains, but there is no reasonable domain restriction | |
275 | % if for instance the invariant defines a variable to be an element of INTEGER without an upper bound. | |
276 | % The distinguishing constraint is of the form "not(P1 <=> P2) & I", whereat P1,P2 are two synthesized | |
277 | % programs and I is the current machine invariant. When synthesizing a substitution with already known | |
278 | % guards G the constraint results to "not(P1 <=> P2) & I & G". | |
279 | solve_distinguishing_constraint_with_fallback(DistConstraint, Solution) :- | |
280 | solve_predicate(DistConstraint, _, ProBSolution), | |
281 | % ProB either found a solution or a contradiction | |
282 | synthesis_log(solve_distinguishing_constraint(DistConstraint,'ProB')), | |
283 | ProBSolution \= no_solution_found(_), | |
284 | !, | |
285 | Solution = ProBSolution. | |
286 | solve_distinguishing_constraint_with_fallback(DistConstraint, Z3Solution) :- | |
287 | % ProB could not make a decision and answered no_solution_found/1, then we ask z3 | |
288 | synthesis_log(solve_distinguishing_constraint(DistConstraint,'Z3')), | |
289 | smt_solve_predicate(z3, DistConstraint, _, Z3Solution). | |
290 | % Solution \= no_solution_found(_) , ! , Solution = Z3Solution. | |
291 | %%%%% Maybe too much overhead to ask a third solver for the distinguishing constraint since two solvers were already not able to find a solution. | |
292 | % solve_distinguishing_constraint_with_fallback(DistConstraint,ProBZ3Solution) :- | |
293 | % synthesis_log(solve_distinguishing_constraint(DistConstraint,'Combined ProB and Z3')) , | |
294 | % preferences:set_preference(smt_supported_interpreter,true) , | |
295 | % Z3 also did not find a solution, then we ask ProB/Z3 combined as a last try | |
296 | % solve_predicate(DistConstraint,_,ProBZ3Solution) , | |
297 | % preferences:set_preference(smt_supported_interpreter,false). | |
298 | get_solution_or_none(solution(Solution), solution(Solution)). | |
299 | get_solution_or_none(Solution, none) :- | |
300 | Solution \= solution(_). | |
301 | ||
302 | b_get_typed_invariant_from_machine(TypedInvariant) :- | |
303 | b_get_typing_predicate(TypingPredicate), | |
304 | b_get_invariant_from_machine(Invariant), | |
305 | conjunct_predicates([TypingPredicate,Invariant], TypedInvariant). | |
306 | ||
307 | % Return typing predicate for the machine variables. | |
308 | b_get_typing_predicate(TypingPredicate) :- | |
309 | b_get_machinevars_with_operation_params(MachineVars), | |
310 | findall(NonSeq, ( member(NonSeq, MachineVars), | |
311 | NonSeq \= b(_,seq(_),_) | |
312 | ), NonSeqMachineVars), | |
313 | b_get_typing_predicate_for_vars(NonSeqMachineVars, TypingPredicate). | |
314 | ||
315 | % Return typing predicate for specific machine variables. | |
316 | b_get_typing_predicate_for_vars(CurrentVars, TypingPredicate) :- | |
317 | generate_typing_predicates(CurrentVars, TypingPredicatesList), | |
318 | conjunct_predicates(TypingPredicatesList, TypingPredicate). | |
319 | ||
320 | :- spec_pre(expand_transition_cache_if_solution/5, [atom,ast,list(ast),one_of([atom,solution_compound]),var]). | |
321 | :- spec_invariant(expand_transition_cache_if_solution/5, [atom,ast,list(ast),one_of([atom,solution_compound]),ast]). | |
322 | :- spec_post(expand_transition_cache_if_solution/5, [atom,ast,list(ast),one_of([atom,solution_compound]),var], | |
323 | [atom,ast,list(ast),one_of([atom,solution_compound]),ast]). | |
324 | expand_transition_cache_if_solution(action, DistTransitionCache, CurrentVars, solution(Solution), NewDistTransitionCache) :- | |
325 | !, | |
326 | get_input_output_nodes_from_bindings(Solution, Input, Output), | |
327 | exclude_transition_constraint(Input, Output, CurrentVars, Exclusion), | |
328 | NewDistTransitionCache = b(conjunct(DistTransitionCache,Exclusion),pred,[]). | |
329 | expand_transition_cache_if_solution(action, DistTransitionCache, _, _, DistTransitionCache). | |
330 | expand_transition_cache_if_solution(Type, DistTransitionCache, _, _, DistTransitionCache) :- | |
331 | Type \= action. | |
332 | ||
333 | :- spec_pre(expand_state_cache_if_guard_and_solution/6, [atom,solution_compound,pair(ast_examples,ast_examples), | |
334 | ast_examples,ast_examples,var]). | |
335 | :- spec_invariant(expand_state_cache_if_guard_and_solution/6, [atom,solution_compound,pair(ast_examples,ast_examples), | |
336 | ast_examples,ast_examples,pair(ast_examples,ast_examples)]). | |
337 | :- spec_post(expand_state_cache_if_guard_and_solution/6, [atom,solution_compound,pair(ast_examples,ast_examples), | |
338 | ast_examples,ast_examples,var], | |
339 | [atom,solution_compound,pair(ast_examples,ast_examples), | |
340 | ast_examples,ast_examples,pair(ast_examples,ast_examples)]). | |
341 | expand_state_cache_if_guard_and_solution(guard, solution(_), (UsedPositives,UsedNegatives), PosInput, | |
342 | NegInput, (PositiveStates,NegativeStates)) :- | |
343 | append(UsedPositives, PosInput, TPositiveStates), | |
344 | append(UsedNegatives, NegInput, TNegativeStates), | |
345 | remove_dups(TPositiveStates, PositiveStates), | |
346 | remove_dups(TNegativeStates, NegativeStates). | |
347 | expand_state_cache_if_guard_and_solution(_, _, DistStateCache, _, _, DistStateCache). | |
348 | ||
349 | :- spec_pre(exclude_transition_constraint/4, [list(ast),list(ast),list(ast),var]). | |
350 | :- spec_invariant(exclude_transition_constraint/4, [list(ast),list(ast),list(ast),ast]). | |
351 | :- spec_post(exclude_transition_constraint/4, [list(ast),list(ast),list(ast),var], | |
352 | [list(ast),list(ast),list(ast),ast]). | |
353 | exclude_transition_constraint(Input, Output, CurrentVars, b(negation(EqualityConjunction),pred,[])) :- | |
354 | b_get_machinevars_with_operation_params(MachineVars), | |
355 | maplist(get_varname_from_id_node, CurrentVars, CurrentIDs), | |
356 | create_equality_nodes_from_examples(CurrentIDs, Input, Output, MachineVars, EqualityNodes), | |
357 | conjunct_predicates(EqualityNodes, EqualityConjunction). | |
358 | ||
359 | sequence_well_definedness_constraint(SeqIDNode, WDConstraint) :- | |
360 | % well-defined sequence: domain enumerated from 1..n | |
361 | WDConstraint = b(equal(b(domain(SeqIDNode),set(integer),[]), | |
362 | b(interval(b(integer(1),integer,[]),b(size(SeqIDNode),integer,[])),set(integer),[])),pred,[]). | |
363 | ||
364 | is_ast(b(_,_,_)). | |
365 | ||
366 | delete_numbers_from_atom(NumberAtom, Atom) :- | |
367 | atom_codes(NumberAtom, Codes), | |
368 | delete_numbers_from_codelist(Codes, NewCodes), | |
369 | atom_codes(Atom, NewCodes). | |
370 | delete_numbers_from_codelist([], []). | |
371 | delete_numbers_from_codelist([Code|R], [Code|NR]) :- | |
372 | ( Code > 57 | |
373 | ; Code < 48 | |
374 | ), | |
375 | !, | |
376 | delete_numbers_from_codelist(R, NR). | |
377 | delete_numbers_from_codelist([_|R], NR) :- | |
378 | delete_numbers_from_codelist(R, NR). | |
379 | ||
380 | % Returns a list of all guards or 'any' if none is given. | |
381 | :- spec_pre(get_guard_list_from_operation/2, [atom,var]). | |
382 | :- spec_invariant(get_guard_list_from_operation/2, [atom,one_of([ast,atom])]). | |
383 | :- spec_post(get_guard_list_from_operation/2, [atom,var], [atom,one_of([ast,atom])]). | |
384 | get_guard_list_from_operation(Operation, ListOfGuards) :- | |
385 | b_get_machine_operation(Operation, _, OperationParameters, OpBody), | |
386 | get_guard_list_from_operation_aux(Operation, OpBody, GuardList), | |
387 | recreate_guards_for_operation_parameters(OperationParameters, OperationParameterGuards), | |
388 | append(GuardList, OperationParameterGuards, ListOfGuards). | |
389 | % No given guards. | |
390 | get_guard_list_from_operation(_, any). | |
391 | ||
392 | get_guard_list_from_operation_aux(Operation, OpBody, GuardList) :- | |
393 | get_guard_list_from_operation_aux2(Operation, OpBody, Guard), | |
394 | conjunction_to_list(Guard, GuardList). | |
395 | get_guard_list_from_operation_aux(_, _, []). | |
396 | ||
397 | get_guard_list_from_operation_aux2(_, b(precondition(Guard,_),_,_), Guard). | |
398 | get_guard_list_from_operation_aux2(Operation, b(rlevent(Operation,_,_,_,Guard,_,_,_,_,_,_),_,_), Guard). | |
399 | get_guard_list_from_operation_aux2(_, b(any(_,Guard,_),subst,_), Guard). | |
400 | ||
401 | % ProB removes typing guards for operations parameters which we need for synthesis to create constants for operation parameters | |
402 | recreate_guards_for_operation_parameters(OperationParameters, OperationParameterGuards) :- | |
403 | findall(SetAst, b_get_machine_set(_, SetAst), IndependentTypes), | |
404 | recreate_guards_for_operation_parameters(OperationParameters, IndependentTypes, [], OperationParameterGuards). | |
405 | recreate_guards_for_operation_parameters([], _, Acc, Acc). | |
406 | recreate_guards_for_operation_parameters([OperationParameter|T], IndependentTypes, Acc, OperationParameterGuards) :- | |
407 | get_texpr_type(OperationParameter, Type), | |
408 | member(b(SetName,set(Type),Info), IndependentTypes), | |
409 | Membership = b(member(OperationParameter,b(SetName,set(Type),Info)),pred,[]), | |
410 | recreate_guards_for_operation_parameters(T, IndependentTypes, [Membership|Acc], OperationParameterGuards). | |
411 | ||
412 | :- spec_pre(conjunct_previous_invariant_or_guard_if_necessary/4, [atom,one_of([atom,ast,list(ast)]),ast,var]). | |
413 | :- spec_invariant(conjunct_previous_invariant_or_guard_if_necessary/4, [atom,one_of([atom,ast,list(ast)]),ast,ast]). | |
414 | :- spec_post(conjunct_previous_invariant_or_guard_if_necessary/4, [atom,one_of([atom,ast,list(ast)]),ast,var], | |
415 | [atom,one_of([atom,ast,list(ast)]),ast,ast]). | |
416 | conjunct_previous_invariant_or_guard_if_necessary(invariant, ValidInvariants, ProgramAST, SolutionAST) :- | |
417 | !, | |
418 | % mark synthesized invariants in the info to be able to split synthesized invariants from those that have already been valid beforehand | |
419 | % we want to avoid redundancy when generating typing invariants later on | |
420 | conjunction_to_list(ProgramAST, ProgramASTList), | |
421 | findall(MarkedConjunct, ( member(Conjunct, ProgramASTList), | |
422 | add_texpr_infos(Conjunct, [synthesized_code], MarkedConjunct) | |
423 | ), MarkedConjunctList), | |
424 | conjunct_predicates(MarkedConjunctList, MarkedProgramAst), | |
425 | append(ValidInvariants, [MarkedProgramAst], InvariantsList), % ValidInvariants prior to synthesized ones since they may contain typing invariants | |
426 | conjunct_predicates(InvariantsList, SolutionAST). | |
427 | conjunct_previous_invariant_or_guard_if_necessary(guard, Guards, ProgramAST, SolutionAST) :- | |
428 | Guards \= any, | |
429 | !, | |
430 | % search for valid guards that are already given by the machine ( NewPreCond => OldPreCond ) | |
431 | find_typed_identifier_uses(ProgramAST, [], ProgIDs), | |
432 | findall(Pre, ( member(Pre, Guards), | |
433 | find_typed_identifier_uses(Pre, PreIDs), | |
434 | append(ProgIDs, PreIDs, TIDs), | |
435 | remove_dups(TIDs, IDs), | |
436 | b_test_boolean_expression_wf_no_bindings(b(forall(IDs,ProgramAST,Pre),pred,[used_ids([])]), []) | |
437 | ), InvalidPreConds), | |
438 | sets:subtract(Guards, InvalidPreConds, ValidPreConds), | |
439 | append(ValidPreConds, [ProgramAST], PreCondsList), % ValidPreConds prior to synthesized ones since they may contain predicates setting the type of operation parameters | |
440 | conjunct_predicates(PreCondsList, SolutionAST). | |
441 | conjunct_previous_invariant_or_guard_if_necessary(action, Guard, ProgramAST, SolutionAST) :- | |
442 | \+ member(Guard, [any,none]), | |
443 | !, | |
444 | SolutionAST = b(precondition(Guard,ProgramAST),subst,[]). | |
445 | conjunct_previous_invariant_or_guard_if_necessary(_, _, ProgramAST, ProgramAST). | |
446 | ||
447 | % Replace equal by assign_single_id and conjunctions by parallel substitution. | |
448 | :- spec_pre(replace_preds_by_subst_if_neccessary/3, [atom,ast,var]). | |
449 | :- spec_invariant(replace_preds_by_subst_if_neccessary/3, [atom,ast,ast]). | |
450 | :- spec_post(replace_preds_by_subst_if_neccessary/3, [atom,ast,var], [atom,ast,ast]). | |
451 | replace_preds_by_subst_if_neccessary(action, TSolutionAST, SolutionAST) :- | |
452 | conjunction_to_list(TSolutionAST, EqualityList), | |
453 | maplist(equality_to_assignment, EqualityList, AssignmentList), | |
454 | SolutionAST = b(parallel(AssignmentList),subst,[]). | |
455 | replace_preds_by_subst_if_neccessary(_, SolutionAST, SolutionAST). | |
456 | ||
457 | equality_to_assignment(b(equal(Primed,Assignment),pred,Info), b(assign_single_id(Primed,Assignment),subst,Info)). | |
458 | ||
459 | % Replace parallel substitution by conjunction predicates and assignments by equalities. | |
460 | :- spec_pre(parallel_to_conjunction/2, [ast,var]). | |
461 | :- spec_invariant(parallel_to_conjunction/2, [ast,ast]). | |
462 | :- spec_post(parallel_to_conjunction/2, [ast,var], [ast,ast]). | |
463 | parallel_to_conjunction(b(Node,Type,Info), b(Node,Type,Info)) :- | |
464 | ground(Node), | |
465 | Type \= subst. | |
466 | parallel_to_conjunction(b(Node,subst,_), Conjunction) :- | |
467 | ground(Node), | |
468 | Node = parallel(Assignments), | |
469 | findall(Equality, ( member(Assignment, Assignments), | |
470 | equality_to_assignment(Equality, Assignment) | |
471 | ), EqualityNodes), | |
472 | conjunct_predicates(EqualityNodes, Conjunction). | |
473 | %% | |
474 | % Get the variables involved in an invariant violation if currently all machine variables are considered, | |
475 | % i.e., they have not been reduced by now. | |
476 | get_involved_variables(_, PosInput, _, VarNames) :- | |
477 | examples_already_reduced(PosInput), | |
478 | !, | |
479 | PosInput = [Example|_], | |
480 | % the examples already have been reduced by the user | |
481 | maplist(get_varname_from_node_info, Example, VarNames). | |
482 | get_involved_variables(Guards, PosInput, NegInput, VarNames) :- | |
483 | get_involved_variables_aux(Guards, PosInput, NegInput, TempVarNames), | |
484 | remove_dups(TempVarNames, VarNames), | |
485 | % only consider machine variables | |
486 | % b_get_machinevars_with_operation_params(MachineVars) , | |
487 | % maplist(get_varname_from_id_node,MachineVars,MachineVarNames) , | |
488 | % sets:intersection(VarNamesNoDups,MachineVarNames,VarNames) , | |
489 | synthesis_log(involved_variables(VarNames)). | |
490 | % check the invariants first | |
491 | get_involved_variables_aux(_, _, NegInput, VarNames) :- | |
492 | get_violated_and_valid_machine_invariants(NegInput, _, ViolatingVars, _), | |
493 | ViolatingVars \= [], | |
494 | !, | |
495 | findall(Name, member(b(identifier(Name),_,_), ViolatingVars), VarNames). | |
496 | % if no invariant violating vars, check the guards | |
497 | get_involved_variables_aux(Guards, PosInput, NegInput, VarNames) :- | |
498 | Guards \= [], | |
499 | append(PosInput, NegInput, Input), | |
500 | check_states_on_guards(Input, Guards, ViolatedGuards), | |
501 | findall(UsedIDs, ( member(Guard, ViolatedGuards), | |
502 | find_identifier_uses(Guard, [], UsedIDs) | |
503 | ), NestedList), | |
504 | append(NestedList, UsedIDs), | |
505 | UsedIDs \= [], | |
506 | remove_dups(UsedIDs, VarNames). | |
507 | % otherwise, consider all variables used in the examples | |
508 | get_involved_variables_aux(_, PosInput, _, VarNames) :- | |
509 | PosInput = [Example|_], | |
510 | maplist(get_varname_from_node_info, Example, VarNames). | |
511 | ||
512 | check_states_on_guards(State, Guards, ViolatedGuards) :- | |
513 | maplist(create_equality_nodes_from_example, State, StateEqualities), | |
514 | findall(Guard, ( member(Guard, Guards), | |
515 | check_states_on_guard(StateEqualities, Guard) | |
516 | ), ValidGuards), | |
517 | subtract(Guards, ValidGuards, ViolatedGuards). | |
518 | check_states_on_guard([], _). | |
519 | check_states_on_guard([StateEquality|T], Guard) :- | |
520 | conjunct_predicates([Guard|StateEquality], CheckGuard), | |
521 | b_test_boolean_expression_wf_no_bindings(CheckGuard, []), | |
522 | !, | |
523 | check_states_on_guard(T, Guard). | |
524 | ||
525 | :- spec_pre(get_violated_and_valid_machine_invariants/4, [list(ast),var,var,var]). | |
526 | :- spec_invariant(get_violated_and_valid_machine_invariants/4, [list(ast),list(ast),list(ast),list(ast)]). | |
527 | :- spec_post(get_violated_and_valid_machine_invariants/4, [list(ast),var,var,var], | |
528 | [list(ast),list(ast),list(ast),list(ast)]). | |
529 | get_violated_and_valid_machine_invariants(Input, ViolatedInvariants, ViolatingVars, ValidInvariants) :- | |
530 | % according to specific operation | |
531 | get_invariants_for_current_vars(Input, _, InvariantList), | |
532 | % consider the machine properties in case an invariant refers to machine constants | |
533 | b_get_properties_from_machine(MachineProperties), | |
534 | maplist(get_violated_and_valid_machine_invariants_aux(MachineProperties, InvariantList), Input, TempViolatedInvariants), | |
535 | tools:flatten(TempViolatedInvariants, TempViolatedInvariants2), | |
536 | remove_dups(TempViolatedInvariants2, ViolatedInvariants), | |
537 | conjunct_predicates(ViolatedInvariants, TempPred), | |
538 | find_typed_identifier_uses_with_info(TempPred, TempViolatingVars), | |
539 | b_get_machinevars_with_operation_params(MachineVars), | |
540 | % filter only machine variables from TempViolatingVars and do not consider machine constants that may occur within an invariant | |
541 | % but do not define the machine state which only refers to machine variables | |
542 | findall(MachineVar, ( member(MachineVar, TempViolatingVars), | |
543 | get_varname_from_id_node(MachineVar, MachineVarName), | |
544 | member(b(identifier(MachineVarName),_,_), MachineVars) | |
545 | ), ViolatingVars), | |
546 | % all other machine variables are valid considering the current machine variables | |
547 | b_get_invariant_from_machine(MachineInvariant), | |
548 | conjunction_to_list(MachineInvariant, MachineInvariantList), | |
549 | subtract(MachineInvariantList, ViolatedInvariants, ValidInvariants). | |
550 | ||
551 | get_violated_and_valid_machine_invariants_aux(MachineProperties, InvariantList, Input, ViolatedInvariants) :- | |
552 | create_equality_nodes_from_example(Input, EqualityNodes), | |
553 | conjunct_predicates([MachineProperties|EqualityNodes], EqualityPredWithMachineProperties), | |
554 | get_violated_and_valid_machine_invariants_aux2(EqualityPredWithMachineProperties, InvariantList, ViolatedInvariants). | |
555 | ||
556 | get_violated_and_valid_machine_invariants_aux2(_, [], []). | |
557 | get_violated_and_valid_machine_invariants_aux2(EqualityNodes, [SingleInv|T], [SingleInv|NT]) :- | |
558 | conjunct_predicates([EqualityNodes,SingleInv], InvPred), | |
559 | \+ b_test_boolean_expression_wf_no_bindings(InvPred, []), | |
560 | !, | |
561 | get_violated_and_valid_machine_invariants_aux2(EqualityNodes, T, NT). | |
562 | get_violated_and_valid_machine_invariants_aux2(EqualityNodes, [_|T], NT) :- | |
563 | get_violated_and_valid_machine_invariants_aux2(EqualityNodes, T, NT). | |
564 | ||
565 | % Get the machine invariants considering at least one of the current variables derived from the examples. | |
566 | get_invariants_for_current_vars(Examples, TypedIDs, InvariantList) :- | |
567 | Examples = [Example|_], | |
568 | maplist(get_varname_from_node_info, Example, VarNames), | |
569 | get_machinevars_by_name(VarNames, TypedIDs), | |
570 | b_get_invariant_from_machine(MachineInvariant), | |
571 | conjunction_to_list(MachineInvariant, CompleteInvariantList), | |
572 | findall(Invariant, ( member(Invariant, CompleteInvariantList), | |
573 | find_identifier_uses(Invariant, [], UsedIDs), | |
574 | intersection(UsedIDs, VarNames, Intersection), | |
575 | Intersection \= [] | |
576 | ), InvariantList). | |
577 | ||
578 | % Check if the given examples are already reduced by the user, i.e., they refer to less variables than | |
579 | % there are machine variables. | |
580 | examples_already_reduced(Examples) :- | |
581 | Examples = [Example|_], | |
582 | length(Example, VarAmount), | |
583 | b_get_machinevars_with_operation_params(MachineVars), | |
584 | length(MachineVars, MachineVarAmount), | |
585 | VarAmount < MachineVarAmount. | |
586 | ||
587 | % The examples are tuples of variable name and pretty value from the java ui. | |
588 | get_invariant_violating_vars_from_examples([], [], []) :- | |
589 | !. | |
590 | get_invariant_violating_vars_from_examples(Positive, Negative, ViolatingVarNames) :- | |
591 | % split examples that are tuples of input and output state | |
592 | split_input_output_tuples(Positive, PosInput, PosOutput), | |
593 | split_input_output_tuples(Negative, NegInput, NegOutput), | |
594 | append([PosInput,PosOutput,NegInput,NegOutput], Input), | |
595 | get_violated_and_valid_machine_invariants(Input, _, ViolatingVars, _), | |
596 | !, | |
597 | maplist(get_varname_from_id_node, ViolatingVars, ViolatingVarNames). | |
598 | ||
599 | % Get another transition that violates the invariant, exclude given input/output example. | |
600 | :- spec_pre(get_invariant_violating_transition/6, [atom,list(ast),list(ast),list(ast),list(ast),var]). | |
601 | :- spec_invariant(get_invariant_violating_transition/6, [atom,list(ast),list(ast),list(ast),list(ast),pair(ast,ast)]). | |
602 | :- spec_post(get_invariant_violating_transition/6, [atom,list(ast),list(ast),list(ast),list(ast),var], | |
603 | [atom,list(ast),list(ast),list(ast),list(ast),pair(ast,ast)]). | |
604 | get_invariant_violating_transition(Operation, UsedVars, ViolatedInvariants, Input, Output, Counter) :- | |
605 | b_get_machine_operation(Operation, _, _, TOpBody), | |
606 | % don't create contradiction in case the guard is equivalent | |
607 | % to an invariant which we will negate in the following | |
608 | remove_pre_condition_from_body(TOpBody, OpBody), | |
609 | before_after_predicate_with_equalities(OpBody, UsedVars, BABody), | |
610 | % find_identifier_uses(OpBody,[],BodyIDs) , | |
611 | findall(V, ( member(b(identifier(V),_,_), UsedVars) | |
612 | ), BodyIDs), | |
613 | b_get_machinevars_with_operation_params(MachineVars), | |
614 | create_equality_nodes_from_examples(BodyIDs, Input, Output, MachineVars, TNodeList), | |
615 | % negate equalities to exclude this transition | |
616 | findall(Inequality, ( create_texpr(negation(Equality), pred, [], Inequality), | |
617 | member(Equality, TNodeList) | |
618 | ), NodeList), | |
619 | conjunct_predicates(ViolatedInvariants, Invariant), | |
620 | maplist(get_varname_from_id_node, MachineVars, MachineVarNames), | |
621 | prime_or_unprime_variables_in_ast(prime, MachineVarNames, Invariant, PrimedInvariant), | |
622 | create_texpr(negation(Invariant), pred, [], NInvariant), | |
623 | create_texpr(negation(PrimedInvariant), pred, [], NPrimedInvariant), | |
624 | conjunct_predicates([BABody,NInvariant,NPrimedInvariant|NodeList], BAExclusion), | |
625 | solve_predicate(BAExclusion, _, Solution), | |
626 | get_invariant_violating_transition_aux(Solution, Counter). | |
627 | ||
628 | get_invariant_violating_transition_aux(Solution, none) :- | |
629 | Solution \= solution(_). | |
630 | get_invariant_violating_transition_aux(solution(Solution), (CounterInput,CounterOutput)) :- | |
631 | % split by input and output state | |
632 | get_input_output_nodes_from_bindings(Solution, CounterInput, CounterOutput). | |
633 | ||
634 | get_invariant_violating_example(_, [], [], _) :- | |
635 | fail. | |
636 | get_invariant_violating_example(ViolatedInvariants, [PosInput|TIn], [PosOutput|TOut], Example) :- | |
637 | maplist(check_invariant_on_state(PosInput, PosOutput), ViolatedInvariants), | |
638 | !, | |
639 | % transition doesn't violate an invariant | |
640 | get_invariant_violating_example(ViolatedInvariants, TIn, TOut, Example). | |
641 | % invariant violating transition found | |
642 | get_invariant_violating_example(_, [PosInput|_], [PosOutput|_], (PosInput,PosOutput)). | |
643 | ||
644 | % Is true if the given invariant is true for the pair of input/output states. | |
645 | check_invariant_on_state(Input, Output, InvariantAst) :- | |
646 | create_equality_nodes_from_examples(Input, Output, EqualityNodes), | |
647 | conjunct_predicates([InvariantAst|EqualityNodes], InvariantTransitionPred), | |
648 | b_test_boolean_expression_wf_no_bindings(InvariantTransitionPred, _). | |
649 | ||
650 | %% Get the input and/or output nodes from a solution of the distinguishing constraint. | |
651 | :- spec_pre(get_input_output_nodes_from_bindings/3, [solution_bindings,var,var]). | |
652 | :- spec_invariant(get_input_output_nodes_from_bindings/3, [solution_bindings,list(ast),list(ast)]). | |
653 | :- spec_post(get_input_output_nodes_from_bindings/3, [solution_bindings,var,var], [solution_bindings,list(ast),list(ast)]). | |
654 | get_input_output_nodes_from_bindings(Solution, Input, Output) :- | |
655 | get_input_nodes_from_bindings(Solution, Input), | |
656 | get_output_nodes_from_bindings(Solution, Output). | |
657 | ||
658 | :- spec_pre(get_input_nodes_from_bindings/2, [solution_bindings,var]). | |
659 | :- spec_invariant(get_input_nodes_from_bindings/2, [solution_bindings,list(ast)]). | |
660 | :- spec_post(get_input_nodes_from_bindings/2, [solution_bindings,var], [solution_bindings,list(ast)]). | |
661 | get_input_nodes_from_bindings(Solution, Input) :- | |
662 | findall(Binding, ( member(Binding, Solution), | |
663 | get_varname_from_binding(Binding, VarName), | |
664 | \+ is_primed_id(VarName) | |
665 | ), Bindings), | |
666 | example_bindings_to_ast_nodes(Bindings, Input). | |
667 | ||
668 | :- spec_pre(get_output_nodes_from_bindings/2, [solution_bindings,var]). | |
669 | :- spec_invariant(get_output_nodes_from_bindings/2, [solution_bindings,list(ast)]). | |
670 | :- spec_post(get_output_nodes_from_bindings/2, [solution_bindings,var], [solution_bindings,list(ast)]). | |
671 | get_output_nodes_from_bindings(Solution, Output) :- | |
672 | get_output_nodes_from_bindings_aux(Solution, NewSolution), | |
673 | example_bindings_to_ast_nodes(NewSolution, Output). | |
674 | get_output_nodes_from_bindings_aux([], []). | |
675 | get_output_nodes_from_bindings_aux([Binding|T], [Binding|NT]) :- | |
676 | get_varname_from_binding(Binding, VarName), | |
677 | is_primed_id(VarName), | |
678 | !, | |
679 | get_output_nodes_from_bindings_aux(T, NT). | |
680 | get_output_nodes_from_bindings_aux([_|T], NT) :- | |
681 | get_output_nodes_from_bindings_aux(T, NT). | |
682 | ||
683 | get_varname_from_binding(binding(VarName,_,_), VarName). | |
684 | get_varname_from_binding(bind(VarName,_), VarName). | |
685 | ||
686 | remove_pre_condition_from_body(b(Node,_,_), Body) :- | |
687 | functor(Node, Functor, Arity), | |
688 | Functor == rlevent, | |
689 | Arity == 11, | |
690 | arg(7, Node, Actions), | |
691 | !, | |
692 | create_texpr(parallel(Actions), subst, [], OpBody), | |
693 | remove_pre_condition_from_body(OpBody, Body). | |
694 | remove_pre_condition_from_body(b(any(_,_,TActions),subst,_), Body) :- | |
695 | ( is_list(TActions) -> | |
696 | Actions = TActions | |
697 | ; Actions = [TActions] | |
698 | ), | |
699 | !, | |
700 | create_texpr(parallel(Actions), subst, [], OpBody), | |
701 | remove_pre_condition_from_body(OpBody, Body). | |
702 | remove_pre_condition_from_body(PreBody, Body) :- | |
703 | PreBody = b(precondition(_,Body),_,_), | |
704 | !. | |
705 | remove_pre_condition_from_body(Body, Body). | |
706 | ||
707 | % prime/unprime all identifiers in VarsToConsider within an ast, if VarsToConsider is an empty list prime/unprime all variables | |
708 | :- spec_pre(prime_or_unprime_variables_in_ast/4, [atom,list(ast),ast,var]). | |
709 | :- spec_invariant(prime_or_unprime_variables_in_ast/4, [atom,list(ast),ast,ast]). | |
710 | :- spec_post(prime_or_unprime_variables_in_ast/4, [atom,list(ast),ast,var], [atom,list(ast),ast,ast]). | |
711 | prime_or_unprime_variables_in_ast(prime, VarsToConsider, b(identifier(ID),Type,Info), PrimedID) :- | |
712 | ( member(ID, VarsToConsider) | |
713 | ; VarsToConsider = [] | |
714 | ), | |
715 | !, | |
716 | prime_identifier(b(identifier(ID),Type,Info), PrimedID). | |
717 | prime_or_unprime_variables_in_ast(prime, VarsToConsider, b(identifier(ID),Type,Info), Ast) :- | |
718 | !, | |
719 | \+ member(ID, VarsToConsider), | |
720 | Ast = b(identifier(ID),Type,Info). | |
721 | prime_or_unprime_variables_in_ast(unprime, VarsToConsider, b(identifier(ID),Type,Info), UnprimedID) :- | |
722 | ( member(ID, VarsToConsider) | |
723 | ; VarsToConsider = [] | |
724 | ), | |
725 | !, | |
726 | ( is_primed_id(ID) | |
727 | -> | |
728 | remove_prime(b(identifier(ID),Type,Info), UnprimedID) | |
729 | ; UnprimedID = b(identifier(ID),Type,Info) | |
730 | ). | |
731 | prime_or_unprime_variables_in_ast(unprime, VarsToConsider, b(identifier(ID),Type,Info), Ast) :- | |
732 | !, | |
733 | \+ member(ID, VarsToConsider), | |
734 | Ast = b(identifier(ID),Type,Info). | |
735 | prime_or_unprime_variables_in_ast(_, _, b(Node,Type,Info), Ast) :- | |
736 | ( Node = integer(_) | |
737 | ; Node = value(_) | |
738 | ; Node = integer_set(_) | |
739 | ; Node = string(_) | |
740 | ; Node =.. [_] | |
741 | ), | |
742 | !, | |
743 | Ast = b(Node,Type,Info). | |
744 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, b(Node,Type,Info), b(NewNode,Type,Info)) :- | |
745 | Node = parallel(Assignments), | |
746 | maplist(prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider), Assignments, NewAssignments), | |
747 | NewNode = parallel(NewAssignments). | |
748 | % skip nodes like comprehension_set, set_extension or sequence_extension | |
749 | prime_or_unprime_variables_in_ast(_, _, b(Node,Type,Info), b(Node,Type,Info)) :- | |
750 | functor(Node, OpType, _), | |
751 | ( OpType = comprehension_set | |
752 | ; OpType = set_extension | |
753 | ; OpType = sequence_extension | |
754 | ), | |
755 | !. | |
756 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, b(Node,Type,Info), b(NewNode,Type,Info)) :- | |
757 | Node =.. [Op,In], | |
758 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, In, NewIn), | |
759 | NewNode =.. [Op,NewIn]. | |
760 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, b(exists(Ids,Pred),Type,Info), b(exists(NewIds,NewPred),Type,Info)) :- | |
761 | maplist(prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider), Ids, NewIds), | |
762 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, Pred, NewPred). | |
763 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, b(Node,Type,Info), b(NewNode,Type,Info)) :- | |
764 | Node =.. [Op,In1,In2], | |
765 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, In1, NewIn1), | |
766 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, In2, NewIn2), | |
767 | NewNode =.. [Op,NewIn1,NewIn2]. | |
768 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, b(forall(Ids,Left,Right),Type,Info), b(forall(NewIds,NewLeft,NewRight),Type,Info)) :- | |
769 | maplist(prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider), Ids, NewIds), | |
770 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, Left, NewLeft), | |
771 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, Right, NewRight). | |
772 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, b(Node,Type,Info), b(NewNode,Type,Info)) :- | |
773 | % e.g. if_then_else | |
774 | Node =.. [Op,In1,In2,In3], | |
775 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, In1, NewIn1), | |
776 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, In2, NewIn2), | |
777 | prime_or_unprime_variables_in_ast(PrimeType, VarsToConsider, In3, NewIn3), | |
778 | NewNode =.. [Op,NewIn1,NewIn2,NewIn3]. | |
779 | ||
780 | % Check that all negative inputs are invalid for the operation's precondition. | |
781 | check_negative_inputs_against_operation_precondition(_, []). % no negative input states are present | |
782 | check_negative_inputs_against_operation_precondition(Operation, NegInput) :- | |
783 | NegInput \= [], | |
784 | b_get_machine_operation(Operation, _, _, OpBody), | |
785 | OpBody = b(precondition(PreCondition,_),_,_), | |
786 | check_negative_inputs_against_operation_precondition_aux(PreCondition, NegInput). | |
787 | ||
788 | % fails if any negative input state is valid on the current precondition, that means, the precondition needs to be strengthened | |
789 | check_negative_inputs_against_operation_precondition_aux(_, []). | |
790 | check_negative_inputs_against_operation_precondition_aux(PreCondition, [NegState|T]) :- | |
791 | create_equality_nodes_from_example(NegState, EqualityNodes), | |
792 | conjunct_predicates([PreCondition|EqualityNodes], EqualityPred), | |
793 | \+ b_test_boolean_expression_wf_no_bindings(EqualityPred, _), | |
794 | check_negative_inputs_against_operation_precondition_aux(PreCondition, T). | |
795 | ||
796 | %% User adds a missing state transition. | |
797 | % Search for a machine operation that completely supports the missing state | |
798 | % or one where we just have to synthesize a relaxed guard. | |
799 | % VarNames refers to the variables used in the added states. | |
800 | check_transitions_against_machine_operations(VarNodes, InputExamples, OutputExamples, Result) :- | |
801 | % check each example againt the existing machine operations | |
802 | maplist(check_transition_against_machine_operations(VarNodes), InputExamples, OutputExamples, SingleResults), | |
803 | check_transitions_against_machine_operations_aux(SingleResults, Result), | |
804 | !. | |
805 | check_transitions_against_machine_operations_aux(SingleResults, Result) :- | |
806 | % an operation satisifies the transition but its guard needs to be relaxed | |
807 | member((OpName,guard), SingleResults), | |
808 | % and all other example transitions are also satisfied by this operation, | |
809 | maplist(operation_match(OpName), SingleResults), | |
810 | % therefore we synthesize a relaxed guard | |
811 | Result = (OpName,guard). | |
812 | check_transitions_against_machine_operations_aux(SingleResults, Result) :- | |
813 | % an operation satisifies all transitions, and thus, we don't have to synthesize anything | |
814 | member((OpName,done), SingleResults), | |
815 | maplist('='((OpName,done)), SingleResults), | |
816 | Result = (OpName,done). | |
817 | check_transitions_against_machine_operations_aux(_, no_match). | |
818 | ||
819 | operation_match(OpName, (OpName,_)). | |
820 | ||
821 | :- spec_pre(check_transition_against_machine_operations/4, [list(ast),list(ast),list(ast),var]). | |
822 | :- spec_invariant(check_transition_against_machine_operations/4, [list(ast),list(ast),list(ast), | |
823 | one_of([atom,pair(atom,atom)])]). | |
824 | :- spec_post(check_transition_against_machine_operations/4, [list(ast),list(ast),list(ast),var], | |
825 | [list(ast),list(ast),list(ast), | |
826 | one_of([atom,pair(atom,atom)])]). | |
827 | % Check a single transition against the existing machine operations. | |
828 | check_transition_against_machine_operations(VarNodes, Input, Output, Result) :- | |
829 | findall((Op,Res,Par,Body), b_get_machine_operation(Op, Res, Par, Body), Operations), | |
830 | check_transition_against_machine_operations(Operations, Input, Output, VarNodes, Result). | |
831 | check_transition_against_machine_operations([], _, _, _, no_match). | |
832 | check_transition_against_machine_operations([(_,_,_,b(skip,subst,_))|T], Input, Output, VarNodes, Result) :- | |
833 | check_transition_against_machine_operations(T, Input, Output, VarNodes, Result). | |
834 | % Skip if the desired action's and the given ones' variables doesn't match. | |
835 | check_transition_against_machine_operations([(_,_,_,OpBody)|T], Input, Output, VarNodes, Result) :- | |
836 | find_identifier_uses(OpBody, [], BodyIDs), | |
837 | findall(Name, member(b(identifier(Name),_,_), VarNodes), VarNames), | |
838 | % find distinguishing variables | |
839 | findall(DistName, ( member(DistName, BodyIDs), | |
840 | \+ member(DistName, VarNames) | |
841 | ), DistNames), | |
842 | DistNames \= [], | |
843 | !, | |
844 | check_transition_against_machine_operations(T, Input, Output, VarNodes, Result). | |
845 | check_transition_against_machine_operations([(OpName,_,_,OpBody)|T], Input, Output, VarNodes, Result) :- | |
846 | b_get_machinevars_with_operation_params(MachineVars), | |
847 | before_after_predicate_with_equalities(OpBody, MachineVars, BABody), | |
848 | find_identifier_uses(OpBody, [], BodyIDs), | |
849 | create_equality_nodes_from_examples(BodyIDs, Input, Output, VarNodes, NodeList), | |
850 | b_get_typed_invariant_from_machine(Invariant), | |
851 | conjunct_predicates([BABody,Invariant|NodeList], BABodyPred), | |
852 | check_transition_against_machine_operations_aux(BABodyPred, NodeList, [(OpName,_,_,OpBody)|T], Input, Output, VarNodes, Result). | |
853 | ||
854 | % Found operation that already executes the transition. | |
855 | check_transition_against_machine_operations_aux(BABodyPred, _, [(OpName,_,_,_)|_], _, _, _, (OpName,done)) :- | |
856 | solve_predicate(BABodyPred, _, solution(_)), | |
857 | !. | |
858 | % Only the guard of an operation does not fit. | |
859 | check_transition_against_machine_operations_aux(_, NodeList, [(OpName,_,_,OpBody)|_], _, _, _, (OpName,guard)) :- | |
860 | b_get_machinevars_with_operation_params(MachineVars), | |
861 | % remove guard and try if action itself supports the missing state | |
862 | OpBody = b(precondition(_PreCondition,Action),_,_), | |
863 | before_after_predicate_with_equalities(Action, MachineVars, BAAction), | |
864 | conjunct_predicates([BAAction|NodeList], BAActionPred), | |
865 | solve_predicate(BAActionPred, _, solution(_)), | |
866 | !. | |
867 | % The action also does not match so that we search for another operation. | |
868 | check_transition_against_machine_operations_aux(_, _, [_|T], Input, Output, VarNodes, Result) :- | |
869 | check_transition_against_machine_operations(T, Input, Output, VarNodes, Result). | |
870 | %% | |
871 | % Get valid transitions for an operation and return tuples holding pretty printed equality predicates of before/after states. | |
872 | % Furthermore, get invalid states if a precondition is given and return pretty printed equality predicates for this states. | |
873 | % We can directly use these pretty printed predicates to compute the states in ProB2 using FindStateCommand. | |
874 | get_valid_and_invalid_equality_predicates_for_operation(OperationName, ValidAmount, InvalidAmount, ValidPrettyEqualityTuples, InvalidPrettyEqualities, IgnoredIDs) :- | |
875 | preferences:set_preference(randomise_enumeration_order, true), | |
876 | b_get_machine_operation(OperationName, _, _, Substitution), | |
877 | find_identifier_uses(Substitution, [], TempUsedIDs), | |
878 | % filter only machine variables, an operation may access machine constants that we need to discard here | |
879 | b_get_machinevars_with_operation_params(MachineVars), | |
880 | findall(UsedID, ( member(UsedID, TempUsedIDs), | |
881 | member(b(identifier(UsedID),_,_), MachineVars) | |
882 | ), UsedIDs), | |
883 | findall(IgnoredID, ( member(b(identifier(IgnoredID),_,_), MachineVars), | |
884 | \+ member(IgnoredID, UsedIDs) | |
885 | ), IgnoredIDs), | |
886 | get_machinevars_by_name(UsedIDs, UsedMachineVars), | |
887 | get_valid_transitions_for_operation(UsedMachineVars, ValidAmount, OperationName, BeforeStates, AfterStates), | |
888 | maplist(create_equality_predicate_from_example, BeforeStates, BeforeEqualities), | |
889 | maplist(create_equality_predicate_from_example, AfterStates, AfterEqualities), | |
890 | maplist(translate_bexpression, BeforeEqualities, PrettyBeforeEqualities), | |
891 | maplist(translate_bexpression, AfterEqualities, PrettyAfterEqualities), | |
892 | zip(PrettyBeforeEqualities, PrettyAfterEqualities, ValidPrettyEqualityTuples), | |
893 | get_invalid_state_equalities_for_operation(OperationName, UsedMachineVars, InvalidAmount, Substitution, InvalidEqualities), | |
894 | maplist(translate_bexpression, InvalidEqualities, InvalidPrettyEqualities), | |
895 | !. | |
896 | ||
897 | get_invalid_state_equalities_for_operation(OperationName, MachineVars, Amount, b(precondition(Precondition,_),subst,_), InvalidEqualities) :- | |
898 | !, | |
899 | % additionally consider the machine properties since a precondition may access machine constants | |
900 | b_get_properties_from_machine(MachineProperties), | |
901 | % filter predicates that set the types of operation parameters from the actual preconditions, since we do not want to negate the typing of operation parameters in the following | |
902 | b_get_machine_operation(OperationName, _, OperationParams, _), | |
903 | split_typing_predicates_from_precondition(OperationParams, Precondition, TypingPredicates, PreconditionPredicate), | |
904 | conjunct_predicates([MachineProperties,TypingPredicates,b(negation(PreconditionPredicate),pred,[])], NegatedPrecondition), | |
905 | get_invalid_state_equalities_for_operation_aux(MachineVars, Amount, NegatedPrecondition, [], InvalidStates), | |
906 | maplist(create_equality_predicate_from_example, InvalidStates, InvalidEqualities). | |
907 | % no precondition is given | |
908 | get_invalid_state_equalities_for_operation(_, _, _, _, []). | |
909 | ||
910 | get_invalid_state_equalities_for_operation_aux(_, 0, _, Acc, Acc). | |
911 | get_invalid_state_equalities_for_operation_aux(MachineVars, Amount, PreconditionWithProperties, Acc, InvalidStates) :- | |
912 | exclude_accumulated_states(MachineVars, Acc, [], TExclusion), | |
913 | ( TExclusion = [] -> | |
914 | Exclusion = b(truth,pred,[]) | |
915 | ; Exclusion = TExclusion | |
916 | ), | |
917 | create_texpr(conjunct(Exclusion,PreconditionWithProperties), pred, [], Predicate), | |
918 | solve_predicate(Predicate, _, Solution), | |
919 | Solution = solution(SolutionBindings), | |
920 | !, | |
921 | % solutions may also refer to machine constants that we do not want to consider here since we search for machine states | |
922 | findall(Binding, ( member(Binding, SolutionBindings), | |
923 | Binding = binding(Name,_,_), | |
924 | member(b(identifier(Name),_,_), MachineVars) | |
925 | ), MachineVarBindings), | |
926 | example_bindings_to_ast_nodes(MachineVarBindings, InvalidState), | |
927 | NAmount is Amount-1, | |
928 | get_invalid_state_equalities_for_operation_aux(MachineVars, NAmount, PreconditionWithProperties, [InvalidState|Acc], InvalidStates). | |
929 | get_invalid_state_equalities_for_operation_aux(_, _, _, Acc, Acc). | |
930 | ||
931 | get_valid_transitions_for_operation(CurrentVars, Amount, OperationName, BeforeStates, AfterStates) :- | |
932 | Amount \= 0, | |
933 | get_valid_inputs_for_operation(OperationName, Amount, ValidInputStates), | |
934 | maplist(create_equality_nodes_from_example, ValidInputStates, ValidInputStateEqualities), | |
935 | before_after_predicate_for_operation(OperationName, BeforeAfterPredicate), | |
936 | get_valid_transitions_for_operation_aux(CurrentVars, ValidInputStateEqualities, BeforeAfterPredicate, [], [], BeforeStates, AfterStates). | |
937 | get_valid_transitions_for_operation(_, _, _, [], []). | |
938 | ||
939 | get_valid_transitions_for_operation_aux(_, [], _, BeforeAcc, AfterAcc, BeforeAcc, AfterAcc). | |
940 | get_valid_transitions_for_operation_aux(CurrentVars, [ValidInputStateEquality|T], BeforeAfterPredicate, BeforeAcc, AfterAcc, BeforeStates, AfterStates) :- | |
941 | conjunct_predicates(ValidInputStateEquality, InputEquality), | |
942 | solve_predicate(b(conjunct(InputEquality,BeforeAfterPredicate),pred,[]), _, Solution), | |
943 | Solution = solution(SolutionBindings), | |
944 | !, | |
945 | % solutions may also refer to machine constants that we do not want to consider here since we search for machine states | |
946 | findall(Primed, ( member(Primed, SolutionBindings), | |
947 | Primed = binding(Name,_,_), | |
948 | is_primed_id(Name), | |
949 | remove_prime(b(identifier(Name),_,_), UnprimedAst), | |
950 | member(UnprimedAst, CurrentVars) | |
951 | ), AfterBindings), | |
952 | sets:subtract(SolutionBindings, AfterBindings, TempBeforeBindings), | |
953 | findall(Unprimed, ( member(Unprimed, TempBeforeBindings), | |
954 | Unprimed = binding(Name,_,_), | |
955 | \+ is_primed_id(Name), | |
956 | member(b(identifier(Name),_,_), CurrentVars) | |
957 | ), BeforeBindings), | |
958 | example_bindings_to_ast_nodes(BeforeBindings, Before), | |
959 | example_bindings_to_ast_nodes(AfterBindings, After), | |
960 | get_valid_transitions_for_operation_aux(CurrentVars, T, BeforeAfterPredicate, [Before|BeforeAcc], [After|AfterAcc], BeforeStates, AfterStates). | |
961 | get_valid_transitions_for_operation_aux(CurrentVars, [_|T], BeforeAfterPredicate, BeforeAcc, AfterAcc, BeforeStates, AfterStates) :- | |
962 | get_valid_transitions_for_operation_aux(CurrentVars, T, BeforeAfterPredicate, BeforeAcc, AfterAcc, BeforeStates, AfterStates). | |
963 | ||
964 | exclude_accumulated_states(_, [], Acc, Exclusion) :- | |
965 | conjunct_predicates(Acc, Exclusion). | |
966 | exclude_accumulated_states(CurrentVars, [BeforeState|T], Acc, Exclusion) :- | |
967 | maplist(exclude_variable_state(CurrentVars), BeforeState, ExclusionList), | |
968 | conjunct_predicates(ExclusionList, Inequalities), | |
969 | exclude_accumulated_states(CurrentVars, T, [Inequalities|Acc], Exclusion). | |
970 | ||
971 | exclude_variable_state(MachineVars, b(Node,Type,Info), Inequality) :- | |
972 | member(synthesis(machinevar,VarName), Info), | |
973 | member(b(identifier(VarName),MachineVarType,MachineVarInfo), MachineVars), | |
974 | create_texpr(not_equal(b(Node,Type,Info),b(identifier(VarName),MachineVarType,MachineVarInfo)), pred, [], Inequality). | |
975 | ||
976 | % Filter predicates that set the types of operation parameters from the actual preconditions. | |
977 | split_typing_predicates_from_precondition(OperationParams, Precondition, TypingPredicate, PreconditionPredicate) :- | |
978 | maplist(get_texpr_id, OperationParams, OperationParamNames), | |
979 | conjunction_to_list(Precondition, PreconditionList), | |
980 | findall(TypingPred, ( member(TypingPred, PreconditionList), | |
981 | find_identifier_uses(TypingPred, [], UsedIDs), | |
982 | sets:intersection(UsedIDs, OperationParamNames, Intersection), | |
983 | Intersection \= [] | |
984 | ), TypingPredicatesList), | |
985 | sets:subtract(PreconditionList, TypingPredicatesList, ActualPreconditionList), | |
986 | conjunct_predicates(TypingPredicatesList, TypingPredicate), | |
987 | conjunct_predicates(ActualPreconditionList, PreconditionPredicate). | |
988 | ||
989 | % Can be used in both directions: Either VarName or Value has to be ground. | |
990 | :- spec_pre(get_binding_for_name_or_value/3, [solution_bindings,one_of([var,atom]),one_of([var,atom,compound])]). | |
991 | :- spec_invariant(get_binding_for_name_or_value/3, [solution_bindings,one_of([var,atom]),one_of([var,atom,compound])]). | |
992 | :- spec_post(get_binding_for_name_or_value/3, [solution_bindings,one_of([var,atom]),one_of([var,atom,compound])], | |
993 | [solution_bindings,one_of([var,atom]),one_of([var,atom,compound])]). | |
994 | get_binding_for_name_or_value(SolutionBindings, VarName, Value) :- | |
995 | ( member(binding(VarName,Value,_), SolutionBindings) | |
996 | ; | |
997 | member(bind(VarName,Value), SolutionBindings) | |
998 | ). | |
999 | ||
1000 | % search identifier nodes within an ast and keep each nodes information | |
1001 | find_typed_identifier_uses_with_info(AST, UsedIDs) :- | |
1002 | find_typed_identifier_uses_with_info_aux(AST, TempUsedIDs), | |
1003 | remove_dups(TempUsedIDs, UsedIDs), | |
1004 | !. | |
1005 | ||
1006 | find_typed_identifier_uses_with_info_aux(List, IDs) :- | |
1007 | is_list(List), | |
1008 | findall(ID, ( member(ID, List), | |
1009 | ID = b(identifier(_),_,_) | |
1010 | ), IDs). | |
1011 | find_typed_identifier_uses_with_info_aux(b(identifier(IDName),Type,Info), [b(identifier(IDName),Type,Info)]). | |
1012 | find_typed_identifier_uses_with_info_aux(b(Node,_,_), UsedIDs) :- | |
1013 | Node =.. [Name,List], | |
1014 | ( Name = set_extension | |
1015 | ; Name = parallel | |
1016 | ), | |
1017 | maplist(find_typed_identifier_uses_with_info_aux, List, UsedIDsNested), | |
1018 | tools:flatten(UsedIDsNested, UsedIDs). | |
1019 | find_typed_identifier_uses_with_info_aux(b(assign_single_id(_ID,Ast),_,_), UsedIDs) :- | |
1020 | find_typed_identifier_uses_with_info_aux(Ast, UsedIDs). | |
1021 | find_typed_identifier_uses_with_info_aux(b(Node,_,_), UsedIDs) :- | |
1022 | Node = if_then_else(Condition,TrueOut,FalseOut), | |
1023 | find_typed_identifier_uses_with_info_aux(Condition, ConditionIDs), | |
1024 | find_typed_identifier_uses_with_info_aux(TrueOut, TrueOutIDs), | |
1025 | find_typed_identifier_uses_with_info_aux(FalseOut, FalseOutIDs), | |
1026 | append([ConditionIDs,TrueOutIDs,FalseOutIDs], UsedIDs). | |
1027 | find_typed_identifier_uses_with_info_aux(b(Node,_,_), UsedIDs) :- | |
1028 | Node = forall(_Ids,Left,Right), | |
1029 | find_typed_identifier_uses_with_info_aux(Left, LeftIDs), | |
1030 | find_typed_identifier_uses_with_info_aux(Right, RightIDs), | |
1031 | append([LeftIDs,RightIDs], UsedIDs). | |
1032 | find_typed_identifier_uses_with_info_aux(b(Node,_,_), UsedIDs) :- | |
1033 | Node =.. [_,Arg], | |
1034 | find_typed_identifier_uses_with_info_aux(Arg, UsedIDs). | |
1035 | find_typed_identifier_uses_with_info_aux(b(Node,_,_), UsedIDs) :- | |
1036 | Node =.. [_,Arg1,Arg2], | |
1037 | find_typed_identifier_uses_with_info_aux(Arg1, UsedIDs1), | |
1038 | find_typed_identifier_uses_with_info_aux(Arg2, UsedIDs2), | |
1039 | append(UsedIDs1, UsedIDs2, UsedIDs). | |
1040 | find_typed_identifier_uses_with_info_aux(b(Node,_,_), UsedIDs) :- | |
1041 | functor(Node, Functor, Arity), | |
1042 | Functor = rlevent, | |
1043 | Arity = 5, | |
1044 | arg(5, Node, Body), | |
1045 | find_typed_identifier_uses_with_info_aux(Body, UsedIDs). | |
1046 | find_typed_identifier_uses_with_info_aux(_, []). | |
1047 | ||
1048 | % Get current identifier names from an example before creating equality nodes to the example. | |
1049 | :- spec_pre(create_equality_nodes_from_examples/3, [ast_example,ast_example,var]). | |
1050 | :- spec_invariant(create_equality_nodes_from_examples/3, [ast_example,ast_example,ast_example]). | |
1051 | :- spec_post(create_equality_nodes_from_examples/3, [ast_example,ast_example,var], | |
1052 | [ast_example,ast_example,ast_example]). | |
1053 | create_equality_nodes_from_examples(Input, Output, EqualityNodes) :- | |
1054 | b_get_machinevars_with_operation_params(MachineVars), | |
1055 | maplist(get_varname_from_node_info, Input, CurrentIDs), | |
1056 | create_equality_nodes_from_examples(CurrentIDs, Input, Output, MachineVars, EqualityNodes). | |
1057 | % We need to set variables to values from example by creating equality nodes | |
1058 | % that we can add to the before after predicate. | |
1059 | create_equality_nodes_from_examples([], _, _, _, []). | |
1060 | create_equality_nodes_from_examples([VarName|T], Input, Output, VarNodes, [Node1,Node2|NodeList]) :- | |
1061 | member(VarNode, VarNodes), | |
1062 | VarNode = b(identifier(VarName),_,_), | |
1063 | member(InputNode, Input), | |
1064 | get_varname_from_node_info(InputNode, VarName), | |
1065 | member(OutputNode, Output), | |
1066 | get_varname_from_node_info(OutputNode, VarName), | |
1067 | Node1 = b(equal(VarNode,InputNode),pred,[]), | |
1068 | prime_identifier(VarNode, PrimedVarNode), | |
1069 | Node2 = b(equal(PrimedVarNode,OutputNode),pred,[]), | |
1070 | create_equality_nodes_from_examples(T, Input, Output, VarNodes, NodeList). | |
1071 | ||
1072 | create_equality_nodes_from_example(ExampleState, EqualityNodes) :- | |
1073 | create_equality_nodes_from_example(ExampleState, [], EqualityNodes). | |
1074 | create_equality_nodes_from_example([], Acc, Acc). | |
1075 | create_equality_nodes_from_example([ValueAst|T], Acc, EqualityNodes) :- | |
1076 | get_varname_from_node_info(ValueAst, VarName), | |
1077 | ValueAst = b(_,VarType,_), | |
1078 | % note: following enforces variables to be real machine variables | |
1079 | % (we consider operation parameters to be part of our I/O "state" in synthesis by marking them with machinevar/1 in AST info) | |
1080 | %get_machinevars_by_name([VarName], [MachineVar]), | |
1081 | Var = b(identifier(VarName),VarType,[]), | |
1082 | Equality = b(equal(Var,ValueAst),pred,[]), | |
1083 | create_equality_nodes_from_example(T, [Equality|Acc], EqualityNodes). | |
1084 | ||
1085 | create_equality_predicate_from_example(ExampleState, EqualityPredicate) :- | |
1086 | create_equality_nodes_from_example(ExampleState, EqualityNodes), | |
1087 | conjunct_predicates(EqualityNodes, EqualityPredicate). | |
1088 | ||
1089 | get_location_var_node_by_info_term(LocationVars, SynthesisTerm, LocationVar) :- | |
1090 | member(LocationVar, LocationVars), | |
1091 | LocationVar = b(identifier(_),integer,Info), | |
1092 | member(SynthesisTerm, Info). | |
1093 | ||
1094 | get_var_node_by_info_term(LocationVars, SynthesisTerm, LocationVar) :- | |
1095 | member(LocationVar, LocationVars), | |
1096 | get_texpr_info(LocationVar, Info), | |
1097 | member(SynthesisTerm, Info). | |
1098 | ||
1099 | get_location_var_node_by_name(LocationVarName, LocationVars, LocationVarNode) :- | |
1100 | member(LocationVarNode, LocationVars), | |
1101 | LocationVarNode = b(identifier(LocationVarName),integer,_). | |
1102 | ||
1103 | % Compute some valid input states for a given operation by considering the invariant as well as the operation's precondition. | |
1104 | get_valid_inputs_for_operation(OperationName, C, ValidStates) :- | |
1105 | preferences:get_preference(randomise_enumeration_order, RandEnumOrder), | |
1106 | preferences:set_preference(randomise_enumeration_order, true), | |
1107 | b_get_machine_operation(OperationName, _, _, Substitution), | |
1108 | get_precondition_from_operation_body(Substitution, Precondition), | |
1109 | % TODO: use complete invariant for UI | |
1110 | b_get_typed_invariant_from_machine(Invariant), | |
1111 | % consider the machine properties since the guard may access machine constants | |
1112 | b_get_properties_from_machine(MachineProperties), | |
1113 | conjunct_predicates([Invariant,Precondition,MachineProperties], PreconditionInvariant), | |
1114 | solve_predicate(PreconditionInvariant, _, 1, [force_evaluation], SolutionTerm), | |
1115 | SolutionTerm = solution(Solution), | |
1116 | !, | |
1117 | find_identifier_uses(Substitution, [], TempUsedIDs), | |
1118 | % filter only machine variables, an operation may access machine constants that we need to discard here | |
1119 | b_get_machinevars_with_operation_params(MachineVars), | |
1120 | findall(UsedID, ( member(UsedID, TempUsedIDs), | |
1121 | member(b(identifier(UsedID),_,_), MachineVars) | |
1122 | ), UsedIDs), | |
1123 | get_machinevars_by_name(UsedIDs, UsedMachineVars), | |
1124 | get_states_aux(UsedMachineVars, C, Solution, PreconditionInvariant, b(truth,pred,[]), ValidStates), | |
1125 | !, | |
1126 | % restore previous enumeration order | |
1127 | ( RandEnumOrder == false -> | |
1128 | preferences:set_preference(randomise_enumeration_order, false) | |
1129 | ; true | |
1130 | ). | |
1131 | ||
1132 | get_precondition_from_operation_body(b(select([b(select_when(Condition,_),_,_)]),_,_), Condition). | |
1133 | get_precondition_from_operation_body(b(precondition(Precondition,_),_,_), Precondition). | |
1134 | get_precondition_from_operation_body(_, b(truth,pred,[])). | |
1135 | ||
1136 | get_valid_and_invalid_equality_predicates_for_invariants(ValidAmount, InvalidAmount, ValidPrettyEqualities, InvalidPrettyEqualities) :- | |
1137 | b_get_machinevars_with_operation_params(MachineVars), | |
1138 | maplist(get_texpr_id, MachineVars, MachineVarNames), | |
1139 | get_valid_states(MachineVarNames, ValidAmount, ValidStates), | |
1140 | maplist(create_equality_predicate_from_example, ValidStates, ValidEqualities), | |
1141 | maplist(translate_bexpression, ValidEqualities, ValidPrettyEqualities), | |
1142 | get_invalid_states(MachineVarNames, InvalidAmount, InvalidStates), | |
1143 | maplist(create_equality_predicate_from_example, InvalidStates, InvalidEqualities), | |
1144 | maplist(translate_bexpression, InvalidEqualities, InvalidPrettyEqualities). | |
1145 | ||
1146 | % Get invalid states for visualizing the invariants. | |
1147 | :- spec_pre(get_invalid_states/3, [list(atom),int,var]). | |
1148 | :- spec_invariant(get_invalid_states/3, [list(atom),int,list(ast)]). | |
1149 | :- spec_post(get_invalid_states/3, [list(atom),int,var], [list(atom),int,list(ast)]). | |
1150 | get_invalid_states(CurrentVarNames, C, ValidStates) :- | |
1151 | preferences:set_preference(randomise_enumeration_order, true), | |
1152 | b_get_invariant_from_machine(Invariant), | |
1153 | Invariant \= b(truth,pred,_), % only typing invariants are given, we then do not provide any invalid states | |
1154 | NegInvariant = b(negation(Invariant),pred,[]), | |
1155 | solve_predicate(NegInvariant, _, 1, [force_evaluation], SolutionTerm), | |
1156 | SolutionTerm = solution(Solution), | |
1157 | !, | |
1158 | get_machinevars_by_name(CurrentVarNames, CurrentVarNodes), | |
1159 | get_states_aux(CurrentVarNodes, C, Solution, NegInvariant, b(truth,pred,[]), ValidStates). | |
1160 | get_invalid_states(_, _, []). | |
1161 | ||
1162 | % Get valid states for synthesis of invariants. | |
1163 | :- spec_pre(get_valid_states/3, [list(atom),int,var]). | |
1164 | :- spec_invariant(get_valid_states/3, [list(atom),int,list(ast)]). | |
1165 | :- spec_post(get_valid_states/3, [list(atom),int,var], [list(atom),int,list(ast)]). | |
1166 | get_valid_states(CurrentVarNames, C, ValidStates) :- | |
1167 | preferences:set_preference(randomise_enumeration_order, true), | |
1168 | b_get_typed_invariant_from_machine(Invariant), | |
1169 | solve_predicate(Invariant, _, 1, [force_evaluation], SolutionTerm), | |
1170 | SolutionTerm = solution(Solution), | |
1171 | !, | |
1172 | get_machinevars_by_name(CurrentVarNames, CurrentVarNodes), | |
1173 | get_states_aux(CurrentVarNodes, C, Solution, Invariant, b(truth,pred,[]), ValidStates). | |
1174 | ||
1175 | :- spec_pre(get_states_aux/6, [list(atom),int,solution_bindings,ast,ast,var]). | |
1176 | :- spec_invariant(get_states_aux/6, [list(atom),int,solution_bindings,ast,ast,list(ast)]). | |
1177 | :- spec_post(get_states_aux/6, [list(atom),int,solution_bindings,ast,ast,var], [list(atom),int,solution_bindings,ast,ast,list(ast)]). | |
1178 | get_states_aux(CurrentVarNodes, C, Solution, Invariant, PriorExclusion, [ValidState|NT]) :- | |
1179 | C \= 1, | |
1180 | C1 is C-1, | |
1181 | findall(Exclusion, ( member(Var, CurrentVarNodes), | |
1182 | Var = b(identifier(VarName),Type,_), | |
1183 | member(binding(VarName,Value,_), Solution), | |
1184 | create_texpr(not_equal(Var,b(value(Value),Type,[])), pred, [], Exclusion) | |
1185 | ), ExclusionList), | |
1186 | disjunct_predicates(ExclusionList, Exclusion), | |
1187 | create_texpr(conjunct(PriorExclusion,Exclusion), pred, [], TExclusion), | |
1188 | create_texpr(conjunct(Invariant,TExclusion), pred, [], NewPred), | |
1189 | solve_predicate(NewPred, _, 1, [force_evaluation], SolutionTerm), | |
1190 | SolutionTerm = solution(NewSolution), | |
1191 | !, | |
1192 | example_bindings_to_ast_nodes(Solution, TempValidState), | |
1193 | reduce_state_to_current_vars(CurrentVarNodes, TempValidState, ValidState), | |
1194 | get_states_aux(CurrentVarNodes, C1, NewSolution, NewPred, TExclusion, NT). | |
1195 | get_states_aux(CurrentVarNodes, _, Solution, _, _, [ValidState]) :- | |
1196 | is_list(Solution), | |
1197 | example_bindings_to_ast_nodes(Solution, TempValidState), | |
1198 | reduce_state_to_current_vars(CurrentVarNodes, TempValidState, ValidState). | |
1199 | get_states_aux(_, _, _, _, _, []). | |
1200 | ||
1201 | % A state is a list of ast value nodes each having the term synthesis/2 with the corresponding machine var name in its info. | |
1202 | reduce_state_to_current_vars(CurrentVarNodes, State, ReducedState) :- | |
1203 | findall(ValueNode, ( member(ValueNode, State), | |
1204 | get_texpr_info(ValueNode, ValueNodeInfo), | |
1205 | member(synthesis(machinevar,MachineVar), ValueNodeInfo), | |
1206 | member(b(identifier(MachineVar),_,_), CurrentVarNodes) | |
1207 | ), ReducedState). | |
1208 | ||
1209 | %% Only for synthesis of guards or invariants: | |
1210 | % In case we synthesize guard/invariant it is possible that a state is graduated as positive and negative | |
1211 | % because we replace output states with TRUE/FALSE. Thus, the constraint would have a contradiction. | |
1212 | % E.g. synthesizing an invariant: If we graduate [0]-->[-1] as valid and [-1]-->[-2] as invalid, | |
1213 | % we don't want to assume [-1] to be a negative state from the second example. | |
1214 | % Synthesizing a guard it is the opposite, i.e., we don't want to assume [-1] to be a positive state. | |
1215 | get_distinct_states(invariant, Positive, TempNegative, Positive, Negative) :- | |
1216 | get_distinct_states(Positive, TempNegative, Negative). | |
1217 | get_distinct_states(guard, TempPositive, Negative, Positive, Negative) :- | |
1218 | get_distinct_states(Negative, TempPositive, Positive). | |
1219 | ||
1220 | % Duplicates are subtracted from the examples given in the second argument. | |
1221 | :- spec_pre(get_distinct_states/3, [ast_examples,ast_examples,var]). | |
1222 | :- spec_invariant(get_distinct_states/3, [ast_examples,ast_examples,ast_examples]). | |
1223 | :- spec_post(get_distinct_states/3, [ast_examples,ast_examples,var], [ast_examples,ast_examples,ast_examples]). | |
1224 | get_distinct_states(Examples1, TempExamples2, Examples2) :- | |
1225 | findall(E, ( member(E, TempExamples2), | |
1226 | \+ contains_equivalent_state(E, Examples1, _) | |
1227 | ), Examples2). | |
1228 | ||
1229 | % Mark initial examples to be able to restrict constant domains when defining the synthesis | |
1230 | % constraint. Otherwise we would expand the constant domains for each new example derived from | |
1231 | % distinguishing interactions. | |
1232 | mark_examples_as_initial(TempPositive, TempNegative, Positive, Negative) :- | |
1233 | maplist(mark_examples_as_initial_aux, TempPositive, Positive), | |
1234 | maplist(mark_examples_as_initial_aux, TempNegative, Negative). | |
1235 | mark_examples_as_initial_aux([], []). | |
1236 | mark_examples_as_initial_aux([ValueNode|T], [b(Node,Type,NewInfo)|NT]) :- | |
1237 | ValueNode = b(Node,Type,Info), | |
1238 | mark_examples_as_initial_aux2(Info, NewInfo), | |
1239 | mark_examples_as_initial_aux(T, NT). | |
1240 | mark_examples_as_initial_aux2(Info, Info) :- | |
1241 | member(synthesis(initial_example), Info). | |
1242 | mark_examples_as_initial_aux2(Info, [synthesis(initial_example)|Info]) :- | |
1243 | \+ member(synthesis(initial_example), Info). | |
1244 | ||
1245 | % InputList contains all machine variables, but for synthesizing an invariant we only need to | |
1246 | % observe variables from violated invariants. | |
1247 | % Furthermore we accept hints from the user on variables that are probably involved in an action's | |
1248 | % guard so we also need to reduce examples there. | |
1249 | reduce_input_to_violating_vars(ViolatingVars, InputExample, ReducedExample) :- | |
1250 | % find values of matching variable names located in ast nodes info term synthesis/2 | |
1251 | findall(Value, ( member(Value, InputExample), | |
1252 | get_varname_from_node_info(Value, VarName), | |
1253 | member(VarName, ViolatingVars) | |
1254 | ), ReducedExample). | |
1255 | ||
1256 | % Get the current variables from an example (info contains synthesis/2 with var name). | |
1257 | get_current_vars_from_example(Example, CurrentVars) :- | |
1258 | b_get_machinevars_with_operation_params(MachineVars), | |
1259 | findall(VarName, ( member(Node, Example), | |
1260 | get_varname_from_node_info(Node, VarName) | |
1261 | ), CurrentVarNames), | |
1262 | findall(VarNode, ( member(VarNode, MachineVars), | |
1263 | VarNode = b(identifier(VarName),_,_), | |
1264 | member(VarName, CurrentVarNames) | |
1265 | ), CurrentVars). | |
1266 | ||
1267 | value_to_ast(Value, b(value(Value),Type,[])) :- | |
1268 | kernel_objects:infer_value_type(Value, Type). | |
1269 | ||
1270 | get_varname_from_node_info(b(_,_,Info), VarName) :- | |
1271 | member(synthesis(machinevar,VarName), Info). | |
1272 | ||
1273 | get_varname_from_id_node(b(identifier(VarName),_,_), VarName). | |
1274 | ||
1275 | % translate prob values to asts node and add the machine variable name to each node's info | |
1276 | example_bindings_to_ast_nodes([], []). | |
1277 | example_bindings_to_ast_nodes([Binding|T], [Ast|NT]) :- | |
1278 | ( Binding = binding(TVarName,In,_) | |
1279 | ; Binding = bind(TVarName,In) | |
1280 | ), | |
1281 | value_to_ast(In, TAst), | |
1282 | remove_prime(TVarName, VarName), | |
1283 | add_texpr_infos(TAst, [synthesis(machinevar,VarName)], TTAst), | |
1284 | set_type_if_necessary(TTAst, Ast), | |
1285 | !, | |
1286 | example_bindings_to_ast_nodes(T, NT). | |
1287 | ||
1288 | remove_prime(b(identifier(VarName),Type,Info), b(identifier(UnPrimed),Type,Info)) :- | |
1289 | !, | |
1290 | remove_prime(VarName, UnPrimed). | |
1291 | remove_prime(VarName, UnPrimed) :- | |
1292 | atom_concat(UnPrimed, '\'', VarName), | |
1293 | !. | |
1294 | remove_prime(VarName, VarName). | |
1295 | ||
1296 | % we have to set the types of untyped ast nodes like empty sets | |
1297 | % TO DO: correctly set the type environment for the type checker in btype/7 and get rid of set_type_if_necessary/2 | |
1298 | set_type_if_necessary(b(Node,Type,Info), b(TypedNode,GroundType,Info)) :- | |
1299 | Node \= identifier(_), | |
1300 | Node \= couple(_,_), | |
1301 | untyped(Type), | |
1302 | b_get_machinevars_with_operation_params(MachineVars), | |
1303 | member(synthesis(machinevar,VarName), Info), | |
1304 | member(b(identifier(VarName),GroundType,_), MachineVars), | |
1305 | set_nested_types_if_necessary(Node, GroundType, TypedNode). | |
1306 | % or couples refering to enumerated machine sets | |
1307 | set_type_if_necessary(b(couple(Key,Value),Type,Info), b(couple(TypedKey,TypedValue),couple(KeyType,ValueType),Info)) :- | |
1308 | untyped(Type), | |
1309 | !, | |
1310 | set_type_if_necessary(Key, TypedKey), | |
1311 | set_type_if_necessary(Value, TypedValue), | |
1312 | get_texpr_type(TypedKey, KeyType), | |
1313 | get_texpr_type(TypedValue, ValueType). | |
1314 | % or elements of enumerated machine sets | |
1315 | set_type_if_necessary(b(identifier(IdName),Type,Info), b(identifier(IdName),GroundType,Info)) :- | |
1316 | untyped(Type), | |
1317 | !, | |
1318 | b_get_machine_enumerated_sets_with_names(SetNamesTuples), | |
1319 | % get the ast node of the enumerated set the identifier belongs to | |
1320 | findall(SetNode, ( member((SetNode,ElmNames), SetNamesTuples), | |
1321 | member(IdName, ElmNames) | |
1322 | ), [SetNode]), | |
1323 | SetNode = b(_,set(GroundType),_). | |
1324 | set_type_if_necessary(b(Node,Type,Info), b(Node,Type,Info)). | |
1325 | ||
1326 | % set the type of the elements of sets or sequences | |
1327 | set_nested_types_if_necessary(Node, set(InnerType), TypedNode) :- | |
1328 | Node =.. [SetOrSeq,Elements], | |
1329 | ( SetOrSeq = set_extension | |
1330 | ; SetOrSeq = sequence_extension | |
1331 | ), | |
1332 | maplist(set_explicit_type_if_necessary(InnerType), Elements, TypedElements), | |
1333 | TypedNode =.. [SetOrSeq,TypedElements]. | |
1334 | set_nested_types_if_necessary(Node, _, Node). | |
1335 | ||
1336 | set_explicit_type_if_necessary(InnerType, b(Node,Type,Info), TypedNode) :- | |
1337 | untyped(Type), | |
1338 | !, | |
1339 | TypedNode = b(Node,InnerType,Info). | |
1340 | set_explicit_type_if_necessary(_, Ast, Ast). | |
1341 | ||
1342 | b_get_machine_enumerated_sets_with_names(SetNamesTuples) :- | |
1343 | findall((SetName,SetNode), b_get_machine_set(SetName, SetNode), NameNodeTuples), | |
1344 | findall((SetName,ElmNames), b_get_named_machine_set(SetName, ElmNames), NameElmsTuples), | |
1345 | findall((SetNode,ElmNames), ( member((SetName,SetNode), NameNodeTuples), | |
1346 | member((SetName,ElmNames), NameElmsTuples) | |
1347 | ), SetNamesTuples). | |
1348 | ||
1349 | untyped(Type) :- | |
1350 | var(Type). | |
1351 | untyped(set(Type)) :- | |
1352 | untyped(Type). | |
1353 | untyped(set(couple(Key,Value))) :- | |
1354 | ( untyped(Key) | |
1355 | ; untyped(Value) | |
1356 | ). | |
1357 | untyped(seq(Type)) :- | |
1358 | untyped(Type). | |
1359 | untyped(seq(couple(Key,Value))) :- | |
1360 | ( untyped(Key) | |
1361 | ; untyped(Value) | |
1362 | ). | |
1363 | % ProB sometimes sets the type of empty sets to set(any) | |
1364 | % TO DO: check this | |
1365 | untyped(any). | |
1366 | ||
1367 | % Zip two lists of the same size, i.e., create tuples of elements having the same index respectively. | |
1368 | zip(L1, L2, Zipped) :- | |
1369 | zip_aux(L1, L2, [], Zipped). | |
1370 | zip_aux([], [], Acc, Acc). | |
1371 | zip_aux([Elm1|T1], [Elm2|T2], Acc, Zipped) :- | |
1372 | zip_aux(T1, T2, [(Elm1,Elm2)|Acc], Zipped). | |
1373 | ||
1374 | % Used in b_synthesis | |
1375 | % An operation satisfies the provided behavior, and thus, we do not have to synthesize anything. | |
1376 | prepare_solution(_AdaptMachineCode, _, _, operation_satisfied(OperationName), operation_satisfied(OperationName), none). | |
1377 | % Synthesis returned a distinguishing state or transition. The machine code is not changed. | |
1378 | prepare_solution(_AdaptMachineCode, _, _, state(State), none, state(TranslatedState)) :- | |
1379 | !, | |
1380 | maplist(translate_example_ast, State, TranslatedState). | |
1381 | prepare_solution(_AdaptMachineCode, _, _, transition(Input,Output), none, transition(TranslatedInput,TranslatedOutput)) :- | |
1382 | !, | |
1383 | maplist(translate_example_ast, Input, TranslatedInput), | |
1384 | maplist(translate_example_ast, Output, TranslatedOutput). | |
1385 | % Succeeded synthesizing a program fitting the desired behavior. Apply the solution to the machine code. | |
1386 | prepare_solution(yes, Operation, SynthesisType, Synthesized, NewMachineAtom, distinguishing(none)) :- | |
1387 | full_b_machine(Machine), | |
1388 | apply_solution_to_machine_and_type_invariant(Machine, Operation, SynthesisType, Synthesized, NewMachineAst), | |
1389 | translate_machine(NewMachineAst, Codes, _), | |
1390 | atom_codes(NewMachineAtom, Codes). | |
1391 | prepare_solution(no, _, _, Synthesized, Solution, Distinguishing) :- | |
1392 | !, | |
1393 | translate_subst_or_bexpr(Synthesized, PrettyPrint), | |
1394 | Solution = (Synthesized, PrettyPrint), | |
1395 | Distinguishing = distinguishing(none). | |
1396 | ||
1397 | % Return a tuple of machine variable name and value for a given state's value ast. | |
1398 | translate_example_ast(ExampleAst, (MachineVarName,TranslatedValue)) :- | |
1399 | translate_bexpression(ExampleAst, TranslatedValue), | |
1400 | get_varname_from_node_info(ExampleAst, MachineVarName). | |
1401 | ||
1402 | % We derive several operations when implicitly synthesizing if-statements which we here apply to the machine code. | |
1403 | adapt_machine_code_for_operations(Operations, NewMachineAtom) :- | |
1404 | full_b_machine(Machine), | |
1405 | adapt_machine_code_for_operations_aux(Operations, Machine, TempNewMachine), | |
1406 | % type invariants which may have been optimized by ProB | |
1407 | b_get_invariant_from_machine(Invariant), | |
1408 | apply_solution_to_machine_and_type_invariant(TempNewMachine, _, invariant, Invariant, NewMachineAst), | |
1409 | translate_machine(NewMachineAst, Codes, _), | |
1410 | atom_codes(NewMachineAtom, Codes). | |
1411 | ||
1412 | adapt_machine_code_for_operations_aux([], MachineCode, MachineCode). | |
1413 | adapt_machine_code_for_operations_aux([Operation|T], MachineCode, NewMachineCode) :- | |
1414 | apply_solution_to_machine(MachineCode, _, action, Operation, TempNewMachine), | |
1415 | adapt_machine_code_for_operations_aux(T, TempNewMachine, NewMachineCode). | |
1416 | ||
1417 | apply_solution_to_machine_and_type_invariant(Machine, Operation, Type, Synthesized, NewMachine) :- | |
1418 | Type \= invariant, | |
1419 | apply_solution_to_machine(Machine, Operation, Type, Synthesized, TempNewMachine), | |
1420 | % the invariant has not been changed but typing predicates may be removed by the solver optimization, | |
1421 | % therefore, we add typing predicates for each machine variable at the beginning of the invariant | |
1422 | b_get_invariant_from_machine(Invariant), | |
1423 | apply_solution_to_machine(TempNewMachine, _, invariant, Invariant, NewMachine). | |
1424 | apply_solution_to_machine_and_type_invariant(Machine, Operation, invariant, SynthesizedInvariant, NewMachine) :- | |
1425 | apply_solution_to_machine(Machine, Operation, invariant, SynthesizedInvariant, NewMachine). | |
1426 | ||
1427 | apply_solution_to_machine(Machine, _, _, none, Machine). | |
1428 | apply_solution_to_machine(machine(MachineName,Content), _, action, Synthesized, NewMachine) :- | |
1429 | member(operation_bodies/OperationBodies, Content), | |
1430 | subtract(Content, [operation_bodies/_], TempContent), | |
1431 | apply_solution_to_machine_aux(action, OperationBodies, Synthesized, NewOperationBodies), | |
1432 | append(TempContent, [operation_bodies/NewOperationBodies], NewContent), | |
1433 | NewMachine = machine(MachineName,NewContent). | |
1434 | apply_solution_to_machine(machine(MachineName,Content), Operation, guard, Synthesized, NewMachine) :- | |
1435 | member(operation_bodies/OperationBodies, Content), | |
1436 | subtract(Content, [operation_bodies/_], TempContent), | |
1437 | apply_solution_to_machine_aux(guard, Operation, Synthesized, OperationBodies, NewOperationBodies), | |
1438 | append(TempContent, [operation_bodies/NewOperationBodies], NewContent), | |
1439 | NewMachine = machine(MachineName,NewContent). | |
1440 | apply_solution_to_machine(machine(MachineName,Content), _, invariant, Synthesized, NewMachine) :- | |
1441 | subtract(Content, [invariant/_], TempContent), | |
1442 | conjunction_to_list(Synthesized, SynthesizedList), | |
1443 | % split synthesized code from previous invariants that have been valid beforehand | |
1444 | findall(SynthesizedCode, ( member(SynthesizedCode, SynthesizedList), | |
1445 | get_texpr_info(SynthesizedCode, Info), | |
1446 | member(synthesized_code, Info) | |
1447 | ), SynthesizedCodeList), | |
1448 | conjunct_predicates(SynthesizedCodeList, TempSynthesizedCode), | |
1449 | find_typed_identifier_uses(TempSynthesizedCode, UsedVars), | |
1450 | % create typing invariants only for variables that are involved in the synthesized code, the other typing invariants have already been conjuncted in ValidInvariants | |
1451 | % we hereby want to avoid redundancy and conflicts in typing invariants, like the valid invariant has been i : NATURAL but generate_typing_predicates creates i : INTEGER afterwards | |
1452 | % whilst i has not even been affected by the invariant violation | |
1453 | b_get_typing_predicate_for_vars(UsedVars, TypingPredicate), | |
1454 | conjunct_predicates([TypingPredicate,Synthesized], TypedSynthesizedInvariant), | |
1455 | add_invariant_to_machine_content(TempContent, TypedSynthesizedInvariant, NewContent), | |
1456 | NewMachine = machine(MachineName,NewContent). | |
1457 | ||
1458 | % Existing operations are replaced by the synthesized ones. | |
1459 | apply_solution_to_machine_aux(action, OperationBodies, Synthesized, NewOperationBodies) :- | |
1460 | is_list(Synthesized), | |
1461 | maplist(get_name_from_operation_ast, Synthesized, OperationNames), | |
1462 | remove_operations_from_list(OperationBodies, OperationNames, [], UninvoledOperations), | |
1463 | append(Synthesized, UninvoledOperations, NewOperationBodies). | |
1464 | apply_solution_to_machine_aux(action, OperationBodies, Synthesized, [Synthesized|UninvoledOperations]) :- | |
1465 | \+ is_list(Synthesized), | |
1466 | get_name_from_operation_ast(Synthesized, OperationName), | |
1467 | remove_operations_from_list(OperationBodies, [OperationName], [], UninvoledOperations). | |
1468 | ||
1469 | % Given a list of operation bodies, i.e. operation/4 ASTs, remove those whose name is in OperationNames. | |
1470 | remove_operations_from_list([], _, Acc, Acc). | |
1471 | remove_operations_from_list([OperationBody|T], OperationNames, Acc, NewOperationBodies) :- | |
1472 | get_name_from_operation_ast(OperationBody, OperationName), | |
1473 | \+ member(OperationName, OperationNames), | |
1474 | remove_operations_from_list(T, OperationNames, [OperationBody|Acc], NewOperationBodies). | |
1475 | remove_operations_from_list([_|T], OperationNames, Acc, NewOperationBodies) :- | |
1476 | remove_operations_from_list(T, OperationNames, Acc, NewOperationBodies). | |
1477 | ||
1478 | apply_solution_to_machine_aux(guard, OperationName, Synthesized, [Operation|T], [NewOperation|T]) :- | |
1479 | replace_precondition_if_operation_fits(OperationName, Operation, Synthesized, NewOperation), | |
1480 | !. | |
1481 | apply_solution_to_machine_aux(guard, OperationName, Synthesized, [Operation|T], [Operation|NT]) :- | |
1482 | apply_solution_to_machine_aux(guard, OperationName, Synthesized, T, NT). | |
1483 | ||
1484 | get_name_from_operation_ast(b(operation(ID,_,_,_),_,_), OperationName) :- | |
1485 | ID = b(identifier(op(OperationName)),_,_). | |
1486 | ||
1487 | % insert the synthesized invariant prior to the linking_invariant | |
1488 | add_invariant_to_machine_content([linking_invariant/OperationBodies|T], Invariant, [invariant/Invariant,linking_invariant/OperationBodies|T]). | |
1489 | add_invariant_to_machine_content([ContentType/Content|T], Invariant, [ContentType/Content|NewContent]) :- | |
1490 | ContentType \= operation_bodies, | |
1491 | add_invariant_to_machine_content(T, Invariant, NewContent). | |
1492 | ||
1493 | replace_precondition_if_operation_fits(OperationName, Operation, Synthesized, NewOperation) :- | |
1494 | Operation = b(Node,Type,Info), | |
1495 | Node = operation(OperationNameId,X,Y,Body), | |
1496 | get_varname_from_id_node(OperationNameId, IdName), | |
1497 | IdName = op(OperationName), % operation fits | |
1498 | replace_precondition_if_operation_fits_aux(Body, Synthesized, NewBody), | |
1499 | NewOperation = b(operation(OperationNameId,X,Y,NewBody),Type,Info). | |
1500 | % Replace or create a new precondition if the given operation name is equal to the one of the operation. | |
1501 | replace_precondition_if_operation_fits_aux(Body, Synthesized, NewBody) :- | |
1502 | Body = b(precondition(_,Substitutions),subst,Info), | |
1503 | !, | |
1504 | NewBody = b(precondition(Synthesized,Substitutions),subst,Info). | |
1505 | replace_precondition_if_operation_fits_aux(Body, Synthesized, NewBody) :- | |
1506 | Body = b(Substitutions,subst,Info), | |
1507 | NewBody = b(precondition(Synthesized,Substitutions),subst,Info). | |
1508 | ||
1509 | replace_precondition_of_operation(Operation, none, FinalOperation) :- | |
1510 | !, | |
1511 | FinalOperation = Operation. | |
1512 | replace_precondition_of_operation(Operation, Precondition, FinalOperation) :- | |
1513 | Operation = b(operation(OperationName,Res,Par,Body),subst,_), | |
1514 | Body = b(precondition(_,Subst),_,_), | |
1515 | !, | |
1516 | FinalOperation = b(operation(OperationName,Res,Par,b(precondition(Precondition,Subst),subst,[])),subst,[]). | |
1517 | replace_precondition_of_operation(Operation, Precondition, FinalOperation) :- | |
1518 | Operation = b(operation(OperationName,Res,Par,Subst),subst,_), | |
1519 | FinalOperation = b(operation(OperationName,Res,Par,b(precondition(Precondition,Subst),subst,[])),subst,[]). | |
1520 | ||
1521 | % The Java UI provides tuples of input and output examples. | |
1522 | split_input_output_tuples(InputOutputTupleList, InputList, OutputList) :- | |
1523 | split_input_output_tuples(InputOutputTupleList, [], [], InputList, OutputList). | |
1524 | split_input_output_tuples([], InputListAcc, OutputListAcc, InputListAcc, OutputListAcc). | |
1525 | split_input_output_tuples([(Input,Output)|T], InputListAcc, OutputListAcc, InputList, OutputList) :- | |
1526 | maplist(variable_parsedvalue_tuple_to_ast, Input, InputAsts), | |
1527 | maplist(variable_parsedvalue_tuple_to_ast, Output, OutputAsts), | |
1528 | accumulate_input_output_if_non_empty(InputAsts, InputListAcc, NewInputListAcc), | |
1529 | accumulate_input_output_if_non_empty(OutputAsts, OutputListAcc, NewOutputListAcc), | |
1530 | split_input_output_tuples(T, NewInputListAcc, NewOutputListAcc, InputList, OutputList). | |
1531 | ||
1532 | accumulate_input_output_if_non_empty([], Acc, Acc). | |
1533 | accumulate_input_output_if_non_empty(Asts, Acc, [Asts|Acc]) :- | |
1534 | Asts \= []. | |
1535 | ||
1536 | % Given a tuple of variable name and parsed term. Create the ast using the typechecker and | |
1537 | % add synthesis/2 term to the node's info describing the corresponding machine variable. | |
1538 | variable_parsedvalue_tuple_to_ast((VarName,ParsedValue), TypedAst) :- | |
1539 | %% TO DO: use type_check_in_machine_context/2 | |
1540 | btype(bsynthesis, ParsedValue, openenv(_,env_empty), _, TempAst, tr(_,_,_,_), tr(_,_,_,_)), | |
1541 | add_texpr_infos(TempAst, [synthesis(machinevar,VarName)], TempAst2), | |
1542 | remove_set_of_couple_type(TempAst2, TempAst3), | |
1543 | set_type_if_necessary(TempAst3, TempAst4), | |
1544 | !, | |
1545 | get_texpr_type(TempAst4, Type), | |
1546 | add_texpr_infos(TempAst4, [synthesis(type,Type)], TypedAst). | |
1547 | ||
1548 | % Remove set(couple(_,_)) type since a sequence with size < 3 may be interpreted as a set of couples although it should be a seq(_). | |
1549 | % The type is added afterwards according to the corresponding machine variable. | |
1550 | remove_set_of_couple_type(b(Node,Type,Info), b(Node,_,Info)) :- | |
1551 | Type == set(couple(_,_)), | |
1552 | !. | |
1553 | remove_set_of_couple_type(AST, AST). | |
1554 | ||
1555 | ||
1556 | keep_asserted_initial_examples(Input, Output, OldInput, OldOutput, NewInput, NewOutput) :- | |
1557 | keep_asserted_initial_states(Input, OldInput, NewInput), | |
1558 | keep_asserted_initial_states(Output, OldOutput, NewOutput). | |
1559 | keep_asserted_initial_states([], _, []). | |
1560 | keep_asserted_initial_states([State|T], OldStates, [NewState|NT]) :- | |
1561 | contains_equivalent_state(State, OldStates, EquivalentState), | |
1562 | !, | |
1563 | NewState = EquivalentState, | |
1564 | keep_asserted_initial_states(T, OldStates, NT). | |
1565 | keep_asserted_initial_states([State|T], OldStates, [State|NT]) :- | |
1566 | keep_asserted_initial_states(T, OldStates, NT). | |
1567 | ||
1568 | get_operation_params_from_ids(TypedIdentifiers, Params) :- | |
1569 | get_operation_params_from_ids(TypedIdentifiers, [], Params). | |
1570 | get_operation_params_from_ids([], Acc, Acc). | |
1571 | get_operation_params_from_ids([TypedIdentifier|T], Acc, Params) :- | |
1572 | get_texpr_info(TypedIdentifier, Info), | |
1573 | ( ( member(synthesis(ParamType,_), Info), | |
1574 | ( ParamType = param_single | |
1575 | ; ParamType = param_set | |
1576 | ) | |
1577 | ) | |
1578 | ; member(introduced_by(operation), Info) | |
1579 | ), | |
1580 | TypedIdentifier = b(Node,Type,_), | |
1581 | \+ member(b(Node,Type,_), Acc), | |
1582 | !, | |
1583 | get_operation_params_from_ids(T, [TypedIdentifier|Acc], Params). | |
1584 | get_operation_params_from_ids([_|T], Acc, Params) :- | |
1585 | get_operation_params_from_ids(T, Acc, Params). | |
1586 | ||
1587 | set_operation_params_domain(Params, Membership) :- | |
1588 | set_operation_params_domain(Params, [], MembershipList), | |
1589 | conjunct_predicates(MembershipList, Membership). | |
1590 | ||
1591 | set_operation_params_domain([], Acc, Acc). | |
1592 | set_operation_params_domain([Param|T], Acc, MembershipList) :- | |
1593 | get_texpr_info(Param, ParamInfo), | |
1594 | ( member(synthesis(type,set(global(IndependentType))), ParamInfo) | |
1595 | ; Param = b(_,global(IndependentType),_) | |
1596 | ), | |
1597 | b_get_named_machine_set(IndependentType, SetAst, _), | |
1598 | set_operation_params_domain(T, [b(member(Param,SetAst),pred,[])|Acc], MembershipList). | |
1599 | ||
1600 | finalize_synthesized_action(_, none, none). | |
1601 | finalize_synthesized_action(OperationName, b(precondition(PreCondition,Substitutions),subst,[]), | |
1602 | b(operation(OpNameId,_,Params,b(precondition(NewPreCondition,UnprimedSubstitutions),subst,[])),subst,[])) :- | |
1603 | OpNameId = b(identifier(op(OperationName)),op([],[]),[]), | |
1604 | find_typed_identifier_uses_with_info(Substitutions, TypedIdentifiers), | |
1605 | get_operation_params_from_ids(TypedIdentifiers, Params), | |
1606 | Params \= [], | |
1607 | !, | |
1608 | set_operation_params_domain(Params, ParamTypeDefsPred), | |
1609 | conjunct_predicates([ParamTypeDefsPred,PreCondition], NewPreCondition), | |
1610 | prime_or_unprime_variables_in_ast(unprime, [], Substitutions, UnprimedSubstitutions). | |
1611 | finalize_synthesized_action(OperationName, b(precondition(PreCondition,Substitutions),subst,[]), | |
1612 | b(operation(OpNameId,_,_,b(precondition(PreCondition,UnprimedSubstitutions),subst,[])),subst,[])) :- | |
1613 | OpNameId = b(identifier(op(OperationName)),op([],[]),[]), | |
1614 | prime_or_unprime_variables_in_ast(unprime, [], Substitutions, UnprimedSubstitutions). | |
1615 | finalize_synthesized_action(OperationName, Action, b(operation(OpNameId,_,Params,b(precondition(ParamTypeDefsPred,UnprimedAction),subst,[])),subst,[])) :- | |
1616 | OpNameId = b(identifier(op(OperationName)),op([],[]),[]), | |
1617 | find_typed_identifier_uses_with_info(Action, TypedIdentifiers), | |
1618 | get_operation_params_from_ids(TypedIdentifiers, Params), | |
1619 | Params \= [], | |
1620 | !, | |
1621 | set_operation_params_domain(Params, ParamTypeDefsPred), | |
1622 | prime_or_unprime_variables_in_ast(unprime, [], Action, UnprimedAction). | |
1623 | finalize_synthesized_action(OperationName, Action, b(operation(OpNameId,_,_,UnprimedAction),subst,[])) :- | |
1624 | OpNameId = b(identifier(op(OperationName)),op([],[]),[]), | |
1625 | prime_or_unprime_variables_in_ast(unprime, [], Action, UnprimedAction). | |
1626 | ||
1627 | % Synthesizing a guard for an action simultaneously can fail. We memorize distinguishing transitions | |
1628 | % so even without a guard we won't find a distinguishing transitions twice. However, | |
1629 | % finding a new distingushing transition that leads to synthesizing a guard simultaneously may | |
1630 | % succeed with the new example later on (previous distinguishing states are also memorized and will be reused). | |
1631 | prepare_synthesized_guards_for_action(Guards, Guards) :- | |
1632 | is_ast(Guards). | |
1633 | prepare_synthesized_guards_for_action(_, any). | |
1634 | ||
1635 | ||
1636 | % Get all the location vars from a list of operators. The result is a map with entries like | |
1637 | % Operator:ListOfLocationVars. Just one iteration of the list of location variables for all operators. | |
1638 | % get_all_location_vars_for_operators(Operators,LocationVars,OperatorLVarsMap) :- | |
1639 | % findall(Op:[],member(Op,Operators),EmptyAcc) , | |
1640 | % get_all_location_vars_for_operators_aux(LocationVars,EmptyAcc,OperatorLVarsMap). | |
1641 | % get_all_location_vars_for_operators_aux([],Acc,Acc). | |
1642 | % get_all_location_vars_for_operators_aux([LocationVar|T],Acc,OperatorLVarsMap) :- | |
1643 | % LocationVar = b(_,_,Info) , | |
1644 | % member(synthesis(ComponentName,LocationType),Info) , | |
1645 | % member(LocationType,[left_input,right_input,output]) , | |
1646 | % delete_numbers_from_atom(ComponentName,Operator) , | |
1647 | % accumulate_locationvar_for_operator(LocationVar,Operator,Acc,NewAcc) , | |
1648 | % get_all_location_vars_for_operators_aux(T,NewAcc,OperatorLVarsMap). | |
1649 | % get_all_location_vars_for_operators_aux([_|T],Acc,OperatorLVarsMap) :- | |
1650 | % get_all_location_vars_for_operators_aux(T,Acc,OperatorLVarsMap). | |
1651 | ||
1652 | % If the operator the location variable refers to is one that we search for, we accumulate the variable respectively. | |
1653 | % accumulate_locationvar_for_operator(LocationVar,Operator,Acc,NewAcc) :- | |
1654 | % member(Operator:OperatorLocationVars,Acc) , | |
1655 | % delete(Acc,Operator:_,TempAcc) , | |
1656 | % NewAcc = [Operator:[LocationVar|OperatorLocationVars]|TempAcc]. | |
1657 | % Otherwise skip the location variable. | |
1658 | % accumulate_locationvar_for_operator(_,Operator,Acc,Acc) :- | |
1659 | % \+member(Operator:_,Acc). | |
1660 | ||
1661 | % Adjust the distinguishing state to the current variables (synthesis type is guard or invariant). | |
1662 | % :- spec_pre(adjust_distinguishing_state_binding/3,[list(ast),solution_bindings,var]). | |
1663 | % :- spec_invariant(adjust_distinguishing_state_binding/3,[list(ast),solution_bindings,solution_bindings]). | |
1664 | % :- spec_post(adjust_distinguishing_state_binding/3,[list(ast),solution_bindings,var], | |
1665 | % [list(ast),solution_bindings,solution_bindings]). | |
1666 | % adjust_distinguishing_state_binding(CurrentVars,Distinguishing,Distinguishing) :- | |
1667 | % length(CurrentVars,VarAmount) , | |
1668 | % length(Distinguishing,VarAmount). | |
1669 | % Shrink distinguishing state. | |
1670 | % adjust_distinguishing_state_binding(CurrentVars,Distinguishing,ShrunkenInput) :- | |
1671 | % length(CurrentVars,VarAmount) , | |
1672 | % length(Distinguishing,DistAmount) , | |
1673 | % VarAmount < DistAmount , | |
1674 | % findall(Binding,(member(Binding,Distinguishing) , Binding = binding(VarName,_,_) , | |
1675 | % member(b(identifier(VarName),_,_),CurrentVars)),ShrunkenInput). | |
1676 | % Expand distinguishing state. | |
1677 | % adjust_distinguishing_state_binding(CurrentVars,Distinguishing,ShrunkenInput) :- | |
1678 | % length(CurrentVars,VarAmount) , | |
1679 | % length(Distinguishing,DistAmount) , | |
1680 | % VarAmount > DistAmount , | |
1681 | % findall(Binding,(member(b(identifier(VarName),_,_),CurrentVars) , | |
1682 | % \+member(Binding,Distinguishing) , Binding = binding(VarName,_,_)),ShrunkenInput). |