adsl 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/spass/bin.rb ADDED
@@ -0,0 +1,164 @@
1
+ require 'tempfile'
2
+ require 'parser/adsl_parser.tab'
3
+ require 'spass/spass_ds_extensions'
4
+ require 'spass/util'
5
+ require 'colorize'
6
+ require 'util/util'
7
+ require 'util/csv_hash_formatter'
8
+
9
+ module Spass
10
+ module Bin
11
+ include Spass::Util
12
+
13
+ def output(term_msg, csv)
14
+ if @verification_output == :terminal
15
+ if term_msg
16
+ print term_msg
17
+ STDOUT.flush
18
+ end
19
+ elsif @verification_output == :csv
20
+ @csv_output << csv if csv
21
+ else
22
+ raise "Unknown verification output #{@verification_output}"
23
+ end
24
+ end
25
+
26
+ def filter_by_name(elems, names)
27
+ return elems if names.nil?
28
+ filtered = elems.select{ |elem| names.map{ |name| elem.name.include? name}.include? true }
29
+ end
30
+
31
+ def verify(input, options={})
32
+ parser = ADSL::ADSLParser.new
33
+ ds_spec = parser.parse input
34
+
35
+ stop_on_incorrect = options[:halt_on_error]
36
+ check_satisfiability = options[:check_satisfiability]
37
+ timeout = options[:timeout]
38
+ actions = filter_by_name ds_spec.actions, options[:actions]
39
+ invariants = filter_by_name ds_spec.invariants, options[:invariants]
40
+
41
+ @csv_output = ::Util::CSVHashFormatter.new
42
+
43
+ @verification_output = options[:csv_output] ? :csv : :terminal
44
+ do_stats = @verification_output == :csv
45
+
46
+ if check_satisfiability
47
+ begin
48
+ output "Checking for satisfiability...", nil
49
+
50
+ translation = nil
51
+ translation_time = Time.time_execution do
52
+ translation = ds_spec.translate_action nil
53
+ end
54
+
55
+ result, stats = exec_spass translation, timeout, true
56
+ if do_stats
57
+ stats[:translation_time] = translation_time
58
+ stats[:action] = '<unsatisfiability>'
59
+ stats[:result] = result.to_s
60
+ end
61
+
62
+ if result == :correct
63
+ output "\rSatisfiability check #{ 'failed!'.red }", stats
64
+ return
65
+ elsif result == :inconclusive
66
+ output "\rSatisfiability check #{ 'unconclusive'.yellow } ", stats
67
+ else
68
+ output "\rSatisfiability check #{ 'passed'.green }. ", stats
69
+ end
70
+ ensure
71
+ output "\n", nil
72
+ end
73
+ end
74
+
75
+ actions.each do |action|
76
+ invariants.each do |invariant|
77
+ output "Verifying action '#{action.name}' with invariant '#{invariant.name}'...", nil
78
+ begin
79
+ translation = nil
80
+ translation_time = Time.time_execution do
81
+ translation = ds_spec.translate_action action.name, invariant
82
+ end
83
+ result, stats = exec_spass translation, timeout, true
84
+ if do_stats
85
+ stats[:translation_time] = translation_time
86
+ stats[:action] = action.name
87
+ stats[:invariant] = invariant.name
88
+ stats[:result] = result.to_s
89
+ end
90
+
91
+ case result
92
+ when :correct
93
+ output "\rAction '#{action.name}' with invariant '#{invariant.name}': #{ 'correct'.green } ", stats
94
+ when :incorrect
95
+ output "\rAction '#{action.name}' with invariant '#{invariant.name}': #{ 'incorrect'.red } ", stats
96
+ return if stop_on_incorrect
97
+ when :inconclusive
98
+ output "\rAction '#{action.name}' with invariant '#{invariant.name}': #{ 'inconclusive'.yellow } ", stats
99
+ else
100
+ raise "Unknown exec_spass result: #{result}"
101
+ end
102
+ rescue => e
103
+ # puts translation
104
+ raise e
105
+ ensure
106
+ output "\n", nil
107
+ end
108
+ end
109
+ end
110
+ ensure
111
+ puts @csv_output.to_s if @verification_output == :csv
112
+ @csv_output = nil
113
+ end
114
+
115
+ def exec_spass(spass_code, timeout=-1, include_stats = false)
116
+ tmp_file = Tempfile.new "spass_temp"
117
+ tmp_file.write spass_code
118
+ tmp_file.close
119
+ arg_combos = ["", "-Sorts=0"]
120
+ output = process_race(*arg_combos.map{ |a| "SPASS #{a} -TimeLimit=#{timeout} #{tmp_file.path}" })
121
+ result = /^SPASS beiseite: (.+)\.$/.match(output)[1]
122
+
123
+ stats = include_stats ? pack_stats(spass_code, output) : nil
124
+ verdict = nil
125
+
126
+ case result
127
+ when 'Proof found'
128
+ verdict = :correct
129
+ when 'Completion found'
130
+ verdict = :incorrect
131
+ else
132
+ verdict = :inconclusive
133
+ end
134
+ return stats.nil? ? verdict : [verdict, stats]
135
+ ensure
136
+ tmp_file.delete unless tmp_file.nil?
137
+ end
138
+
139
+ def pack_stats(spass_code, spass_output)
140
+ spass_output = spass_output.split("\n").last(10).join("\n")
141
+ stats = {}
142
+ stats[:translation_time] = nil # should be set externally
143
+
144
+ identifiers = /predicates\s*\[([^\]]*)\]/.match(spass_code)[1].scan(/\w+/)
145
+ stats[:spass_predicate_count] = identifiers.length
146
+
147
+ formulae = spass_code.scan(/formula\s*\([^\.]+\)\./)
148
+ stats[:spass_formula_count] = formulae.length
149
+ stats[:average_formula_length] = formulae.inject(0){ |total, formula| total += formula.length} / formulae.length
150
+
151
+ times = spass_output.scan(/(\d):(\d\d):(\d\d)\.(\d\d)/)
152
+ raise if times.length != 6
153
+ times = times.map{ |time| time[3].to_i*10 + time[2].to_i*1000 + time[1].to_i*60*1000 + time[0].to_i*60*60*1000 }
154
+ stats[:spass_preparation_time] = times[1..2].sum
155
+ stats[:spass_proof_lookup_time] = times[3..5].sum
156
+
157
+ stats[:proof_clause_count] = /^SPASS derived (\d+) clauses.*$/.match(spass_output)[1].to_i
158
+
159
+ stats[:memory] = /^\s*SPASS allocated (\d+) KBytes.*$/.match(spass_output)[1].to_i
160
+
161
+ stats
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,25 @@
1
+ class Symbol
2
+ def to_spass_string
3
+ to_s
4
+ end
5
+ end
6
+
7
+ class String
8
+ def to_spass_string
9
+ self
10
+ end
11
+
12
+ def resolve_params(*args)
13
+ args = args.flatten
14
+ max_arg_index = self.scan(/\$\{(\d+)\}/).map{ |a| a.first.to_i }.max || 0
15
+ if args.length < max_arg_index
16
+ raise ArgumentError, "Invalid argument number: #{args.length} instead of #{max_arg_index}"
17
+ end
18
+ result = self
19
+ args.length.times do |i|
20
+ result = result.gsub "${#{i + 1}}", args[i].to_s
21
+ end
22
+ result
23
+ end
24
+ end
25
+
@@ -0,0 +1,870 @@
1
+ require 'fol/first_order_logic'
2
+ require 'spass/spass_translator'
3
+
4
+ module DS
5
+ class DSNode
6
+ def replace_var(from, to); end
7
+ end
8
+
9
+ class DSSpec < DSNode
10
+ def translate_action(action_name, *listed_invariants)
11
+ translation = SpassTranslator::Translation.new
12
+
13
+ if action_name
14
+ action = @actions.select{ |a| a.name == action_name }
15
+ raise ArgumentError, "Action '#{action_name}' not found" if action.empty?
16
+ action = action.first
17
+ end
18
+
19
+ translation.classes.concat @classes
20
+ @classes.each do |klass|
21
+ klass.translate(translation)
22
+ end
23
+
24
+ relations = @classes.map{ |c| c.relations }.flatten
25
+ relations.select{ |r| r.inverse_of.nil? }.each do |relation|
26
+ relation.translate(translation)
27
+ end
28
+ relations.select{ |r| r.inverse_of }.each do |relation|
29
+ relation.translate(translation)
30
+ end
31
+
32
+ action.prepare(translation) if action_name
33
+
34
+ translation.create_formula FOL::ForAll.new(:o, FOL::Equiv.new(
35
+ translation.is_object[:o],
36
+ FOL::Or.new(@classes.map{ |c| c[:o] })
37
+ ))
38
+ @classes.group_by{ |klass| klass.parent }.each do |common_parent, children|
39
+ unless common_parent.nil?
40
+ translation.create_formula FOL::ForAll.new(:o, FOL::Implies.new(
41
+ FOL::Or.new(children.map{ |c| c[:o] }),
42
+ common_parent[:o]
43
+ ))
44
+ end
45
+ if children.length > 1
46
+ children.each do |child|
47
+ translation.create_formula FOL::ForAll.new(:o, FOL::Implies.new(
48
+ child[:o],
49
+ FOL::Not.new(children.select{ |c| c != child}.map{ |c| c[:o] })
50
+ ))
51
+ end
52
+ end
53
+ end
54
+
55
+ relations = @classes.map{ |c| c.relations }.flatten.map{ |rel| rel.type_pred }.uniq
56
+ translation.create_formula FOL::ForAll.new(:o, FOL::Equiv.new(
57
+ translation.is_tuple[:o],
58
+ FOL::Or.new(relations.map{ |r| r[:o] })
59
+ ))
60
+ if relations.length > 1
61
+ relations.each do |relation|
62
+ translation.create_formula FOL::ForAll.new(:o, FOL::Implies.new(
63
+ relation[:o],
64
+ FOL::Not.new(relations.select{ |c| c != relation}.map{ |c| c[:o] })
65
+ ))
66
+ end
67
+ end
68
+
69
+ translation.create_formula FOL::ForAll.new(:o, FOL::OneOf.new(
70
+ translation.is_object[:o],
71
+ translation.is_tuple[:o],
72
+ translation.is_either_resolution[:o]
73
+ ))
74
+ translation.create_formula FOL::ForAll.new(:o, FOL::Implies.new(
75
+ translation.existed_initially[:o],
76
+ FOL::Or.new(translation.is_object[:o], translation.is_tuple[:o])
77
+ ))
78
+ translation.create_formula FOL::ForAll.new(:o, FOL::Equiv.new(
79
+ translation.existed_initially[:o],
80
+ translation.prev_state[:o]
81
+ ))
82
+
83
+ action.translate(translation) if action_name
84
+
85
+ if action_name
86
+ translation.invariant_state = translation.existed_initially
87
+ pre_invariants = @invariants.map{ |invariant| invariant.formula.resolve_invariant_formula(translation) }
88
+
89
+ listed_invariants = @invariants if listed_invariants.empty?
90
+ translation.invariant_state = translation.exists_finally
91
+ post_invariants = listed_invariants.map{ |invariant| invariant.formula.resolve_invariant_formula(translation) }
92
+
93
+ translation.create_conjecture FOL::Implies.new(
94
+ FOL::And.new(pre_invariants),
95
+ FOL::And.new(post_invariants)
96
+ ).resolve_spass
97
+ else
98
+ # used to check for contradictions in the model
99
+ invariants_formulae = invariants.map do |invariant|
100
+ formula = invariant.formula
101
+ dummy_state = translation.create_predicate 'always_true', 1
102
+ translation.create_formula FOL::ForAll.new(:o, dummy_state[:o])
103
+ translation.invariant_state = dummy_state
104
+ formula.resolve_invariant_formula(translation)
105
+ end
106
+ translation.create_conjecture FOL::Not.new(FOL::And.new(invariants_formulae))
107
+ end
108
+
109
+ return translation.to_spass_string
110
+ end
111
+ end
112
+
113
+ class DSAction < DSNode
114
+ include FOL
115
+
116
+ def prepare(translation)
117
+ @args.each do |arg|
118
+ arg.define_predicate translation
119
+ end
120
+ @block.prepare translation
121
+ end
122
+
123
+ def translate(translation)
124
+ translation.context = translation.root_context
125
+
126
+ @args.length.times do |i|
127
+ cardinality = @cardinalities[i]
128
+ arg = @args[i]
129
+
130
+ translation.create_formula _for_all(:o, _implies(
131
+ arg[:o],
132
+ _and(translation.existed_initially[:o], arg.type[:o])
133
+ ))
134
+
135
+ translation.create_formula _exists(:o, arg[:o]) if cardinality[0] > 0
136
+ if cardinality[1] == 1
137
+ translation.create_formula _for_all(:o1, :o2, _implies(
138
+ _and(arg[:o1], arg[:o2]),
139
+ _equal(:o1, :o2)
140
+ ))
141
+ end
142
+ end
143
+
144
+ @block.migrate_state_spass translation
145
+
146
+ translation.create_formula _for_all(:o, _equiv(
147
+ translation.exists_finally[:o],
148
+ translation.prev_state[:o]
149
+ ))
150
+ end
151
+ end
152
+
153
+ class DSClass < DSNode
154
+ def [](variable)
155
+ @type_pred[variable]
156
+ end
157
+
158
+ def translate(translation)
159
+ @type_pred = translation.create_predicate "of_#{@name}_type", 1
160
+ end
161
+ end
162
+
163
+ class DSRelation < DSNode
164
+ include FOL
165
+ attr_reader :type_pred, :left_link, :right_link
166
+
167
+ def [](variable)
168
+ @type_pred[variable]
169
+ end
170
+
171
+ def translate(translation)
172
+ if @inverse_of
173
+ @type_pred = @inverse_of.type_pred
174
+ @left_link = @inverse_of.right_link
175
+ @right_link = @inverse_of.left_link
176
+ else
177
+ @type_pred = translation.create_predicate "of_#{@from_class.name}_#{@name}_type", 1
178
+ @left_link = translation.create_predicate "left_link_#{@from_class.name}_#{@name}", 2
179
+ @right_link = translation.create_predicate "right_link_#{@from_class.name}_#{@name}", 2
180
+ translation.create_formula _for_all(:t, :o, _implies(@left_link[:t, :o], _and(@from_class[:o], @type_pred[:t])))
181
+ translation.create_formula _for_all(:t, :o, _implies(@right_link[:t, :o], _and(@to_class[:o], @type_pred[:t])))
182
+ translation.create_formula _for_all(:t, :o1, :o2, _and(
183
+ _implies(_and(@left_link[:t, :o1], @left_link[:t, :o2]), _equal(:o1, :o2)),
184
+ _implies(_and(@right_link[:t, :o1], @right_link[:t, :o2]), _equal(:o1, :o2))
185
+ ))
186
+ translation.create_formula _for_all(:t, _implies(
187
+ @type_pred[:t],
188
+ _exists(:o1, :o2, _and(@left_link[:t, :o1], @right_link[:t, :o2]))
189
+ ))
190
+ end
191
+
192
+ if @cardinality[0] > 0
193
+ translation.create_formula _for_all(:o, _implies(@from_class[:o], _exists(:t, @left_link[:t, :o])))
194
+ end
195
+ if @cardinality[1] == 1
196
+ translation.create_formula _for_all(:o, :t1, :t2, _implies(
197
+ _and(@left_link[:t1, :o], @left_link[:t2, :o]),
198
+ _equal(:t1, :t2)
199
+ ))
200
+ end
201
+ end
202
+ end
203
+
204
+ class DSCreateObj < DSNode
205
+ include FOL
206
+ attr_reader :context_creation_link, :context
207
+
208
+ def prepare(translation)
209
+ @context = translation.context
210
+ translation.create_obj_stmts[@klass] << self
211
+ @context_creation_link = translation.create_predicate "created_#{@klass.name}_in_context", context.level + 1
212
+ end
213
+
214
+ def migrate_state_spass(translation)
215
+ state = translation.create_state "post_create_#{@klass.name}"
216
+ prev_state = translation.prev_state
217
+ translation.gen_formula_for_unique_arg(@context_creation_link, (0..@context.level-1), @context.level)
218
+ translation.reserve_names @context.p_names, :o do |ps, o|
219
+ created_by_other_create_stmts = translation.create_obj_stmts[@klass].select{|s| s != self}.map do |stmt|
220
+ formula = nil
221
+ translation.reserve_names stmt.context.p_names do |other_ps|
222
+ formula = _exists(other_ps, stmt.context_creation_link[other_ps, o])
223
+ end
224
+ formula
225
+ end
226
+ created_by_other_create_stmts << translation.existed_initially[o]
227
+ child_classes = translation.classes.select{ |c| c.parent == @klass }
228
+ not_of_child_types = child_classes.empty? ? true : _and(child_classes.map{ |c| _not(c[o]) })
229
+ translation.create_formula _for_all(ps, o, _implies(
230
+ @context_creation_link[ps, o], _and(
231
+ context.type_pred(ps),
232
+ _not(created_by_other_create_stmts),
233
+ @klass[o],
234
+ not_of_child_types
235
+ )
236
+ ))
237
+ translation.create_formula _for_all(ps, _implies(@context.type_pred(ps), _exists(o,
238
+ @context_creation_link[ps, o]
239
+ )))
240
+ translation.create_formula _for_all(ps, o,
241
+ _if_then_else_eq(
242
+ @context_creation_link[ps, o],
243
+ _and(_not(prev_state[ps, o]), state[ps, o]),
244
+ _equiv(prev_state[ps, o], state[ps, o])
245
+ )
246
+ )
247
+
248
+ relevant_from_relations = translation.classes.map{ |c| c.relations }.flatten.select{ |r| r.from_class == @klass }
249
+ relevant_to_relations = translation.classes.map{ |c| c.relations }.flatten.select{ |r| r.to_class == @klass }
250
+ translation.reserve_names :r do |r|
251
+ translation.create_formula _for_all(ps, o, _implies(
252
+ @context_creation_link[ps, o],
253
+ _for_all(r, _not(_and(
254
+ state[ps, r],
255
+ _or(
256
+ relevant_from_relations.map{ |rel| rel.left_link[r, o] },
257
+ relevant_to_relations.map{ |rel| rel.right_link[r, o] }
258
+ )
259
+ )))
260
+ ))
261
+ end
262
+ end
263
+
264
+ translation.prev_state = state
265
+ end
266
+ end
267
+
268
+ class DSCreateObjset < DSNode
269
+ include FOL
270
+
271
+ def prepare_action(translation); end
272
+
273
+ def resolve_action_objset(translation, ps, var)
274
+ return @createobj.context_creation_link[ps, var]
275
+ end
276
+ end
277
+
278
+ class DSDeleteObj < DSNode
279
+ include FOL
280
+ attr_accessor :context_deletion_link
281
+
282
+ def prepare(translation)
283
+ @objset.prepare_action translation
284
+ end
285
+
286
+ def migrate_state_spass(translation)
287
+ state = translation.create_state "post_delete_#{@objset.type.name}"
288
+ prev_state = translation.prev_state
289
+ context = translation.context
290
+
291
+ translation.reserve_names context.p_names, :o do |ps, o|
292
+ translation.create_formula _for_all(ps, o,
293
+ _if_then_else_eq(_and(@objset.resolve_action_objset(translation, ps, o), prev_state[ps, o]),
294
+ _and(prev_state[ps, o], _not(state[ps, o])),
295
+ _equiv(prev_state[ps, o], state[ps, o])
296
+ )
297
+ )
298
+ end
299
+
300
+ translation.prev_state = state
301
+ end
302
+ end
303
+
304
+ class DSCreateTup < DSNode
305
+ include FOL
306
+
307
+ def prepare(translation)
308
+ @objset1.prepare_action translation
309
+ @objset2.prepare_action translation
310
+ end
311
+
312
+ def migrate_state_spass(translation)
313
+ state = translation.create_state "post_create_#{@relation.from_class.name}_#{@relation.name}"
314
+ prev_state = translation.prev_state
315
+ context = translation.context
316
+
317
+ translation.reserve_names context.p_names, :r, :o1, :o2 do |ps, r, o1, o2|
318
+ objset1 = @objset1.resolve_action_objset(translation, ps, o1)
319
+ objset2 = @objset2.resolve_action_objset(translation, ps, o2)
320
+ translation.create_formula FOL::ForAll.new(ps, r, FOL::Implies.new(
321
+ context.type_pred(ps),
322
+ FOL::Equiv.new(
323
+ state[ps, r],
324
+ FOL::Or.new(
325
+ prev_state[ps, r],
326
+ FOL::Exists.new(o1, o2, FOL::And.new(
327
+ prev_state[ps, o1], prev_state[ps, o2],
328
+ @relation.left_link[r, o1], @relation.right_link[r, o2],
329
+ objset1, objset2
330
+ ))
331
+ )
332
+ )
333
+ ))
334
+ translation.create_formula FOL::ForAll.new(ps, o1, o2, FOL::Implies.new(
335
+ FOL::And.new(prev_state[ps, o1], prev_state[ps, o2], objset1, objset2),
336
+ FOL::Exists.new(r, FOL::And.new(state[ps, r], @relation.left_link[r, o1], @relation.right_link[r, o2]))
337
+ ))
338
+ end
339
+ translation.prev_state = state
340
+ end
341
+ end
342
+
343
+ class DSDeleteTup < DSNode
344
+ include FOL
345
+
346
+ def prepare(translation)
347
+ @objset1.prepare_action translation
348
+ @objset2.prepare_action translation
349
+ end
350
+
351
+ def migrate_state_spass(translation)
352
+ state = translation.create_state "post_deleteref_#{@relation.from_class.name}_#{@relation.name}"
353
+ prev_state = translation.prev_state
354
+ context = translation.context
355
+
356
+ translation.reserve_names context.p_names, :r, :o1, :o2 do |ps, r, o1, o2|
357
+ objset1 = @objset1.resolve_action_objset(translation, ps, o1)
358
+ objset2 = @objset2.resolve_action_objset(translation, ps, o2)
359
+ translation.create_formula FOL::ForAll.new(ps, r, FOL::Equiv.new(
360
+ state[ps, r],
361
+ FOL::And.new(
362
+ prev_state[ps, r],
363
+ FOL::ForAll.new(o1, o2, FOL::Not.new(FOL::And.new(
364
+ objset1, objset2,
365
+ prev_state[ps, o1], prev_state[ps, o2],
366
+ @relation.left_link[r, o1], @relation.right_link[r, o2]
367
+ )))
368
+ )
369
+ ))
370
+ end
371
+
372
+ translation.prev_state = state
373
+ end
374
+ end
375
+
376
+ class DSEither < DSNode
377
+ include FOL
378
+ attr_reader :resolution_link, :is_trues
379
+
380
+ def prepare(translation)
381
+ context = translation.context
382
+ @resolution_link = translation.create_predicate :resolution_link, context.level+1
383
+ translation.reserve_names context.p_names, :r do |ps, r|
384
+ translation.gen_formula_for_unique_arg(@resolution_link, (0..ps.length-1), ps.length)
385
+ translation.create_formula _for_all(ps, r, _implies(@resolution_link[ps, r], _and(
386
+ translation.context.type_pred(ps),
387
+ translation.is_either_resolution[r]
388
+ )))
389
+ end
390
+ @is_trues = []
391
+ @blocks.length.times do |i|
392
+ is_trues << translation.create_predicate("either_resolution_#{i}_is_true", 1)
393
+ end
394
+ @blocks.each do |block|
395
+ block.prepare(translation)
396
+ end
397
+ end
398
+
399
+ def migrate_state_spass(translation)
400
+ post_state = translation.create_state :post_either
401
+ prev_state = translation.prev_state
402
+ context = translation.context
403
+
404
+ pre_states = []
405
+ post_states = []
406
+ @blocks.length.times do |i|
407
+ pre_states << translation.create_state(:pre_of_either)
408
+ end
409
+ translation.create_formula FOL::ForAll.new(:r, FOL::Implies.new(
410
+ translation.is_either_resolution[:r],
411
+ FOL::OneOf.new(is_trues.map{ |pred| pred[:r] })
412
+ ))
413
+
414
+ translation.reserve_names context.p_names, :resolution, :o do |ps, resolution, o|
415
+ translation.create_formula FOL::ForAll.new(ps, FOL::Implies.new(
416
+ translation.context.type_pred(ps),
417
+ FOL::Exists.new(resolution, FOL::And.new(
418
+ @resolution_link[ps, resolution],
419
+ FOL::And.new((0..@blocks.length-1).map { |i|
420
+ FOL::Equiv.new(is_trues[i][resolution], FOL::ForAll.new(o, FOL::Equiv.new(prev_state[ps, o], pre_states[i][ps, o])))
421
+ })
422
+ ))
423
+ ))
424
+ end
425
+
426
+ @blocks.length.times do |i|
427
+ translation.prev_state = pre_states[i]
428
+ @blocks[i].migrate_state_spass translation
429
+ post_states << translation.prev_state
430
+ end
431
+
432
+ translation.reserve_names context.p_names, :resolution, :o do |ps, resolution, o|
433
+ translation.create_formula FOL::ForAll.new(ps, FOL::Implies.new(
434
+ translation.context.type_pred(ps),
435
+ FOL::Exists.new(resolution, FOL::And.new(
436
+ @resolution_link[ps, resolution],
437
+ FOL::And.new((0..@blocks.length-1).map { |i|
438
+ FOL::Equiv.new(is_trues[i][resolution], FOL::ForAll.new(o, FOL::Equiv.new(post_state[ps, o], post_states[i][ps, o])))
439
+ })
440
+ ))
441
+ ))
442
+ end
443
+
444
+ translation.prev_state = post_state
445
+ end
446
+ end
447
+
448
+ class DSEitherLambdaObjset < DSNode
449
+ def prepare_action(translation); end
450
+
451
+ def resolve_action_objset(translation, ps, o)
452
+ translation.reserve_names :r do |r|
453
+ implications = []
454
+ @either.blocks.length.times do |i|
455
+ implications << FOL::Implies.new(@either.is_trues[i][r], @vars[i].resolve_action_objset(translation, ps, o))
456
+ end
457
+
458
+ return FOL::ForAll.new(:r, FOL::Implies.new(
459
+ @either.resolution_link[ps, r],
460
+ FOL::And.new(implications)
461
+ ))
462
+ end
463
+ end
464
+ end
465
+
466
+ class DSForEachCommon < DSNode
467
+ include FOL
468
+
469
+ attr_reader :context, :pre_iteration_state, :post_iteration_state, :pre_state, :post_state
470
+
471
+ def prepare_with_context(translation, flat_context)
472
+ @context = translation.create_context "for_each_context", flat_context, translation.context
473
+ @objset.prepare_action translation
474
+ translation.context = @context
475
+ @block.prepare translation
476
+ translation.context = @context.parent
477
+ end
478
+
479
+ def migrate_state_spass(translation)
480
+ @pre_state = translation.prev_state
481
+ @post_state = translation.create_state :post_for_each
482
+
483
+ translation.reserve_names @context.parent.p_names, :o do |ps, o|
484
+ translation.create_formula _for_all(ps, o, _equiv(
485
+ _and(@objset.resolve_action_objset(translation, ps, o), @pre_state[ps, o]),
486
+ @context.type_pred(ps, o)
487
+ ))
488
+ end
489
+
490
+ translation.context = @context
491
+
492
+ @pre_iteration_state = translation.create_state :pre_iteration
493
+ @post_iteration_state = translation.create_state :post_iteration
494
+
495
+ translation.prev_state = @pre_iteration_state
496
+ @block.migrate_state_spass translation
497
+
498
+ translation.reserve_names @context.p_names, :o do |ps, o|
499
+ translation.create_formula _for_all(ps, o, _equiv(
500
+ translation.prev_state[ps, o],
501
+ @post_iteration_state[ps, o]
502
+ ))
503
+ end
504
+
505
+ create_iteration_formulae translation
506
+ translation.context = @context.parent
507
+ translation.prev_state = post_state
508
+ end
509
+ end
510
+
511
+ class DSForEach < DSForEachCommon
512
+ def prepare(translation)
513
+ prepare_with_context(translation, false)
514
+ end
515
+
516
+ def create_iteration_formulae(translation)
517
+ raise "Not implemented for flexible arities"
518
+ translation.create_formula _if_then_else_eq(
519
+ _exists(:c, :o, _and(old_state[:c, :o], @objset.resolve_action_objset(translation, :c, :o))),
520
+ _for_all(:parent, :c, :o, _implies(@context.parent_of_link[:parent, :c], _and(
521
+ _if_then_else(
522
+ @context.first[:parent, :c],
523
+ _equiv(old_state[:parent, :o], @pre_iteration_state[:c, :o]),
524
+ _for_all(:prev, _implies(
525
+ @context.just_before[:prev, :c],
526
+ _equiv(@post_iteration_state[:prev, :o], @pre_iteration_state[:c, :o])
527
+ ))
528
+ ),
529
+ _implies(
530
+ @context.last[:parent, :c],
531
+ _equiv(@post_state[:parent, :o], @post_iteration_state[:c, :o])
532
+ )
533
+ ))),
534
+ _for_all(:c, :o, _equiv(@pre_state[:c, :o], @post_state[:c, :o]))
535
+ )
536
+ end
537
+ end
538
+
539
+ class DSForEachIteratorObjset < DSNode
540
+ def prepare_action(translation); end
541
+
542
+ def resolve_action_objset(translation, ps, o)
543
+ return FOL::Equal.new(o, ps[@for_each.context.level-1])
544
+ end
545
+ end
546
+
547
+ class DSForEachPreLambdaObjset < DSNode
548
+ def prepare_action(translation); end
549
+
550
+ def resolve_action_objset(translation, ps, o)
551
+ raise "Not implemented for flexible arities"
552
+ translation.reserve_names :parent, :prev_context do |parent, prev_context|
553
+ return FOL::ForAll.new(parent, FOL::Implies.new(@context.parent_of_pred[parent, :c],
554
+ FOL::IfThenElseEq.new(
555
+ @context.first[parent, c],
556
+ @before_var[c, o],
557
+ FOL::Exists.new( prev_context, FOL::And.new(
558
+ @context.just_before[prev_context, c],
559
+ @inside_var[prev_context, o]
560
+ ))
561
+ )
562
+ ))
563
+ end
564
+ end
565
+ end
566
+
567
+ class DSFlatForEach < DSForEachCommon
568
+ def prepare(translation)
569
+ prepare_with_context(translation, true)
570
+ end
571
+
572
+ def create_iteration_formulae(translation)
573
+ context = translation.context
574
+ translation.reserve_names context.p_names, :o, :c1, :c2 do |ps, o, c1, c2|
575
+ ps_without_last = ps.first(ps.length - 1)
576
+ translation.create_formula _for_all(ps, o, _implies(
577
+ @context.type_pred(ps),
578
+ _equiv(@pre_state[ps_without_last, o], @pre_iteration_state[ps, o])
579
+ ))
580
+ translation.create_formula _for_all(ps_without_last, o, _if_then_else(
581
+ _not(_exists(ps.last, @context.type_pred(ps))),
582
+ _equiv(@pre_state[ps_without_last, o], @post_state[ps_without_last, o]),
583
+ _implies(
584
+ @context.parent.type_pred(ps_without_last),
585
+ _equiv(
586
+ @post_state[ps_without_last, o],
587
+ _or(
588
+ _and(
589
+ @pre_state[ps_without_last, o],
590
+ _for_all(ps.last, _implies(
591
+ @context.type_pred(ps),
592
+ @post_iteration_state[ps, o]
593
+ ))
594
+ ),
595
+ _and(
596
+ _not(@pre_state[ps_without_last, o]),
597
+ _exists(ps.last, @post_iteration_state[ps, o])
598
+ )
599
+ )
600
+ )
601
+ )
602
+ ))
603
+ end
604
+ end
605
+ end
606
+
607
+ class DSBlock < DSNode
608
+ def prepare(translation)
609
+ @statements.each do |stat|
610
+ stat.prepare translation
611
+ end
612
+ end
613
+
614
+ def migrate_state_spass(translation)
615
+ @statements.each do |stat|
616
+ stat.migrate_state_spass translation
617
+ end
618
+ end
619
+ end
620
+
621
+ class DSAssignment < DSNode
622
+ def prepare(translation)
623
+ @var.define_predicate translation
624
+ @objset.prepare_action translation
625
+ end
626
+
627
+ def migrate_state_spass(translation)
628
+ context = translation.context
629
+ translation.reserve_names context.p_names, :o do |ps, o|
630
+ translation.create_formula FOL::ForAll.new(ps, o, FOL::Equiv.new(
631
+ var.resolve_action_objset(translation, ps, o),
632
+ FOL::And.new(
633
+ translation.prev_state[ps, o],
634
+ objset.resolve_action_objset(translation, ps, o)
635
+ )
636
+ ))
637
+ end
638
+ end
639
+ end
640
+
641
+ class DSVariable < DSNode
642
+ attr_accessor :context, :pred
643
+
644
+ def action_name
645
+ "var_#{@name}"
646
+ end
647
+
648
+ # The predicate is not defined in prepare_action
649
+ # as we want the predicate to be defined only when assigning to the variable
650
+ # not when using it
651
+ # a nil check does not work because it makes the translation non-reusable
652
+ def prepare_action(translation); end
653
+ def define_predicate(translation)
654
+ @context = translation.context
655
+ @pred = translation.create_predicate action_name, context.level + 1
656
+ end
657
+
658
+ def resolve_action_objset(translation, ps, var)
659
+ return @pred[ps.first(@context.level), var]
660
+ end
661
+
662
+ def invariant_name
663
+ "invariant_var_#{@name}"
664
+ end
665
+
666
+ def resolve_invariant_objset(translation, var)
667
+ FOL::Equal.new(invariant_name, var)
668
+ end
669
+
670
+ def [](*args)
671
+ @pred[args]
672
+ end
673
+ end
674
+
675
+ class DSAllOf < DSNode
676
+ def prepare_action(translation); end
677
+
678
+ def resolve_action_objset(translation, ps, var)
679
+ return @klass[var]
680
+ end
681
+
682
+ def resolve_invariant_objset(translation, var)
683
+ return @klass[var]
684
+ end
685
+ end
686
+
687
+ class DSDereference < DSNode
688
+ def prepare_action(translation)
689
+ @objset.prepare_action translation
690
+ end
691
+
692
+ def resolve_action_objset(translation, ps, var)
693
+ translation.reserve_names :temp, :r do |temp, r|
694
+ return FOL::Exists.new(temp, r, FOL::And.new(
695
+ translation.prev_state[ps, r],
696
+ translation.prev_state[ps, temp],
697
+ @objset.resolve_action_objset(translation, ps, temp),
698
+ @relation.left_link[r, temp],
699
+ @relation.right_link[r, var]
700
+ ))
701
+ end
702
+ end
703
+
704
+ def resolve_invariant_objset(translation, var)
705
+ translation.reserve_names :temp, :r do |temp, r|
706
+ return FOL::Exists.new(temp, r, FOL::And.new(
707
+ translation.invariant_state[temp],
708
+ translation.invariant_state[r],
709
+ @objset.resolve_invariant_objset(translation, temp),
710
+ @relation.left_link[r, temp],
711
+ @relation.right_link[r, var]
712
+ ))
713
+ end
714
+ end
715
+ end
716
+
717
+ class DSSubset < DSNode
718
+ def prepare_action(translation)
719
+ @objset.prepare_action translation
720
+ end
721
+
722
+ def resolve_action_objset(translation, ps, var)
723
+ context = translation.context
724
+ pred = translation.create_predicate :subset, context.level + 1
725
+ translation.reserve_names context.p_names do |ps|
726
+ translation.create_formula FOL::ForAll.new(ps, :o,
727
+ FOL::Implies.new(pred[ps, :o], @objset.resolve_action_objset(translation, ps, :o))
728
+ )
729
+ end
730
+ return pred[ps, var]
731
+ end
732
+
733
+ def resolve_invariant_objset(translation, var)
734
+ pred = translation.create_predicate :subset, 1
735
+ translation.create_formula FOL::ForAll.new(:o,
736
+ FOL::Implies.new(pred[:o], @objset.resolve_invariant_objset(translation, :o))
737
+ )
738
+ return pred[var]
739
+ end
740
+ end
741
+
742
+ class DSOr < DSNode
743
+ def resolve_invariant_formula(translation)
744
+ FOL::Or.new(@subformulae.map{ |sub| sub.resolve_invariant_formula translation })
745
+ end
746
+ end
747
+
748
+ class DSAnd < DSNode
749
+ def resolve_invariant_formula(translation)
750
+ FOL::And.new(@subformulae.map{ |sub| sub.resolve_invariant_formula translation })
751
+ end
752
+ end
753
+
754
+ class DSEquiv < DSNode
755
+ def resolve_invariant_formula(translation)
756
+ FOL::Equiv.new(@subformulae.map{ |sub| sub.resolve_invariant_formula translation })
757
+ end
758
+ end
759
+
760
+ class DSImplies < DSNode
761
+ def resolve_invariant_formula(translation)
762
+ subformula1 = @subformula1.resolve_invariant_formula translation
763
+ subformula2 = @subformula2.resolve_invariant_formula translation
764
+ FOL::Implies.new(subformula1, subformula2)
765
+ end
766
+ end
767
+
768
+ class DSOneOf < DSNode
769
+ def prepare_action(translation)
770
+ @objset.prepare_action translation
771
+ end
772
+
773
+ def resolve_action_objset(translation, ps, var)
774
+ context = translation.context
775
+ pred = translation.create_predicate :one_of, context.level + 1
776
+ translation.gen_formula_for_unique_arg(pred, context.level)
777
+ translation.reserve_names context.p_names, :o do |subps, o|
778
+ co_in_objset = @objset.resolve_action_objset(translation, subps, o)
779
+ translation.create_formula FOL::ForAll.new(subps, FOL::Equiv.new(
780
+ FOL::Exists.new(o, FOL::And.new(translation.prev_state[subps, o], pred[subps, o])),
781
+ FOL::Exists.new(o, FOL::And.new(translation.prev_state[subps, o], co_in_objset))
782
+ ))
783
+ translation.create_formula FOL::ForAll.new(subps, o,
784
+ FOL::Implies.new(pred[subps, o], FOL::And.new(translation.prev_state[subps, o], co_in_objset))
785
+ )
786
+ end
787
+ return pred[ps, var]
788
+ end
789
+ end
790
+
791
+ class DSNot < DSNode
792
+ def resolve_invariant_formula(translation)
793
+ subformula = @subformula.resolve_invariant_formula translation
794
+ return FOL::Not.new(subformula)
795
+ end
796
+ end
797
+
798
+ class DSBoolean < DSNode
799
+ def resolve_invariant_formula(translation)
800
+ return @bool_value.resolve_spass
801
+ end
802
+ end
803
+
804
+ class DSForAll < DSNode
805
+ def resolve_invariant_formula(translation)
806
+ subformula = @subformula.resolve_invariant_formula translation
807
+ var_constraints = []
808
+ @vars.length.times do |index|
809
+ var_constraints << @objsets[index].resolve_invariant_objset(translation, @vars[index].invariant_name)
810
+ end
811
+ return FOL::ForAll.new(@vars.map{ |v| v.invariant_name }, FOL::Implies.new(
812
+ FOL::And.new(
813
+ @vars.map{ |v| translation.invariant_state[v.invariant_name] },
814
+ var_constraints
815
+ ),
816
+ subformula
817
+ )).resolve_spass
818
+ end
819
+ end
820
+
821
+ class DSExists < DSNode
822
+ def resolve_invariant_formula(translation)
823
+ subformula = @subformula.nil? ? true : @subformula.resolve_invariant_formula(translation)
824
+ subformula ||= true
825
+ return FOL::Exists.new(@vars.map{ |v| v.invariant_name }, FOL::And.new(
826
+ @vars.map{ |v| translation.invariant_state[v.invariant_name] },
827
+ @vars.map{ |v| v.type[v.invariant_name] },
828
+ @vars.map{ |v| v.resolve_invariant_objset(translation, v.invariant_name) },
829
+ subformula
830
+ )).resolve_spass
831
+ end
832
+ end
833
+
834
+ class DSEqual < DSNode
835
+ def resolve_invariant_formula(translation)
836
+ translation.reserve_names :temp do |temp|
837
+ objsets = @objsets.map{ |o| o.resolve_invariant_objset translation, temp }
838
+ return FOL::ForAll.new(temp, FOL::Implies.new(
839
+ translation.invariant_state[temp],
840
+ FOL::Equiv.new(objsets)
841
+ )).resolve_spass
842
+ end
843
+ end
844
+ end
845
+
846
+ class DSIn < DSNode
847
+ def resolve_invariant_formula(translation)
848
+ translation.reserve_names :temp do |temp|
849
+ return FOL::ForAll.new(temp, FOL::Implies.new(
850
+ translation.invariant_state[temp],
851
+ FOL::Implies.new(
852
+ @objset1.resolve_invariant_objset(translation, temp),
853
+ @objset2.resolve_invariant_objset(translation, temp)
854
+ )
855
+ ))
856
+ end
857
+ end
858
+ end
859
+
860
+ class DSEmpty < DSNode
861
+ def resolve_invariant_formula(translation)
862
+ translation.reserve_names :temp do |temp|
863
+ return FOL::ForAll.new(temp, FOL::Implies.new(
864
+ translation.invariant_state[temp],
865
+ FOL::Not.new(@objset.resolve_invariant_objset(translation, temp))
866
+ ))
867
+ end
868
+ end
869
+ end
870
+ end