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).