pyvcg 1.0.9__py3-none-any.whl

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.
pyvcg/__init__.py ADDED
File without changes
File without changes
@@ -0,0 +1,537 @@
1
+ #!/usr/bin/env python3
2
+ ##############################################################################
3
+ ## ##
4
+ ## Verification Condition Generator ##
5
+ ## ##
6
+ ## Copyright (C) 2023-2025, Florian Schanda ##
7
+ ## ##
8
+ ## This file is part of PyVCG. ##
9
+ ## ##
10
+ ## PyVCG is free software: you can redistribute it and/or modify it ##
11
+ ## under the terms of the GNU General Public License as published by ##
12
+ ## the Free Software Foundation, either version 3 of the License, or ##
13
+ ## (at your option) any later version. ##
14
+ ## ##
15
+ ## PyVCG is distributed in the hope that it will be useful, but ##
16
+ ## WITHOUT ANY WARRANTY; without even the implied warranty of ##
17
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ##
18
+ ## General Public License for more details. ##
19
+ ## ##
20
+ ## You should have received a copy of the GNU General Public License ##
21
+ ## along with PyVCG. If not, see <http://www.gnu.org/licenses/>. ##
22
+ ## ##
23
+ ##############################################################################
24
+
25
+ import cvc5
26
+
27
+ from pyvcg import smt
28
+
29
+
30
+ class CVC5_Solver(smt.VC_Solver):
31
+ def __init__(self):
32
+ self.solver = cvc5.Solver()
33
+ self.result = None
34
+ self.values = {}
35
+
36
+ self.const_mapping = {}
37
+ self.var_mapping = {}
38
+ self.enum_mapping = {}
39
+ self.literal_mapping = {}
40
+ self.function_mapping = {}
41
+ self.sort_mapping = {}
42
+ self.record_mapping = {}
43
+ self.user_functions = {}
44
+
45
+ self.relevant_values = []
46
+
47
+ def term_to_python(self, sort, term):
48
+ assert isinstance(sort, smt.Sort)
49
+ assert isinstance(term, cvc5.Term)
50
+
51
+ if isinstance(sort, smt.Enumeration):
52
+ rv = str(term)
53
+ if rv.startswith("|"):
54
+ rv = rv[1:-1]
55
+ return rv
56
+ elif isinstance(sort, smt.Record):
57
+ assert term.getKind() == cvc5.Kind.APPLY_CONSTRUCTOR
58
+ cons_name = term[0].getSymbol()
59
+ assert isinstance(cons_name, str)
60
+ assert cons_name.endswith("__cons") or cons_name.endswith("__null")
61
+ if cons_name.endswith("__null"):
62
+ assert sort.is_recursive
63
+ return None
64
+ else:
65
+ rv = {}
66
+ for idx, name in enumerate(sort.components, 1):
67
+ rv[name] = self.term_to_python(sort.components[name],
68
+ term[idx])
69
+ return rv
70
+ elif sort.name == "Bool":
71
+ return term.getBooleanValue()
72
+ elif sort.name == "Int":
73
+ return term.getIntegerValue()
74
+ elif sort.name == "Real":
75
+ if term.isRealValue():
76
+ return term.getRealValue()
77
+ else: # pragma: no cover
78
+ return None
79
+ elif sort.name == "String":
80
+ return term.getStringValue()
81
+ elif sort.name == "Seq":
82
+ return [self.term_to_python(sort.element_sort(), t)
83
+ for t in term.getSequenceValue()]
84
+ else:
85
+ assert False, (term.getKind(), term.__class__.__name__)
86
+
87
+ def set_solver_option(self, name, value):
88
+ assert isinstance(name, str)
89
+ assert isinstance(value, (bool, int, str))
90
+ if isinstance(value, bool):
91
+ self.solver.setOption(name, str(value).lower())
92
+ else:
93
+ self.solver.setOption(name, str(value))
94
+
95
+ def solve(self):
96
+ result = self.solver.checkSat()
97
+ if result.isSat():
98
+ self.result = "sat"
99
+ elif result.isUnsat():
100
+ self.result = "unsat"
101
+ return
102
+ else: # pragma: no cover
103
+ self.result = "unknown"
104
+
105
+ for constant in self.relevant_values:
106
+ value = self.solver.getValue(self.const_mapping[constant])
107
+ self.values[constant.name] = self.term_to_python(constant.sort,
108
+ value)
109
+
110
+ def get_status(self):
111
+ assert self.result is not None
112
+ return self.result
113
+
114
+ def get_values(self):
115
+ assert self.result is not None
116
+ return self.values
117
+
118
+ def visit_script(self, node, logic, functions):
119
+ assert isinstance(node, smt.Script)
120
+ assert isinstance(logic, str)
121
+ assert isinstance(functions, set)
122
+ assert all(isinstance(function, str)
123
+ for function in functions)
124
+
125
+ self.solver.setOption("produce-models", "true")
126
+ self.solver.setLogic(logic)
127
+
128
+ for function in functions:
129
+ if function == "floor_div":
130
+ lhs = self.solver.mkVar(self.solver.getIntegerSort(),
131
+ "lhs")
132
+ rhs = self.solver.mkVar(self.solver.getIntegerSort(),
133
+ "rhs")
134
+ fun = self.solver.defineFun(
135
+ function,
136
+ [lhs, rhs],
137
+ self.solver.getIntegerSort(),
138
+ self.solver.mkTerm(
139
+ cvc5.Kind.ITE,
140
+ self.solver.mkTerm(cvc5.Kind.LT,
141
+ rhs,
142
+ self.solver.mkInteger(0)),
143
+ self.solver.mkTerm(cvc5.Kind.INTS_DIVISION,
144
+ self.solver.mkTerm(cvc5.Kind.NEG,
145
+ lhs),
146
+ self.solver.mkTerm(cvc5.Kind.NEG,
147
+ rhs)),
148
+ self.solver.mkTerm(cvc5.Kind.INTS_DIVISION,
149
+ lhs,
150
+ rhs)))
151
+
152
+ elif function == "ada_remainder":
153
+ lhs = self.solver.mkVar(self.solver.getIntegerSort(),
154
+ "lhs")
155
+ rhs = self.solver.mkVar(self.solver.getIntegerSort(),
156
+ "rhs")
157
+ fun = self.solver.defineFun(
158
+ function,
159
+ [lhs, rhs],
160
+ self.solver.getIntegerSort(),
161
+ self.solver.mkTerm(
162
+ cvc5.Kind.ITE,
163
+ self.solver.mkTerm(cvc5.Kind.LT,
164
+ lhs,
165
+ self.solver.mkInteger(0)),
166
+ self.solver.mkTerm(
167
+ cvc5.Kind.NEG,
168
+ self.solver.mkTerm(
169
+ cvc5.Kind.INTS_MODULUS,
170
+ self.solver.mkTerm(cvc5.Kind.NEG, lhs),
171
+ rhs)),
172
+ self.solver.mkTerm(cvc5.Kind.INTS_MODULUS,
173
+ lhs,
174
+ rhs)))
175
+
176
+ elif function == "to_int_rna":
177
+ val = self.solver.mkVar(self.solver.getRealSort(),
178
+ "value")
179
+ fun = self.solver.defineFun(
180
+ function,
181
+ [val],
182
+ self.solver.getIntegerSort(),
183
+ self.solver.mkTerm(
184
+ cvc5.Kind.ITE,
185
+ self.solver.mkTerm(cvc5.Kind.GEQ,
186
+ val,
187
+ self.solver.mkReal(0, 1)),
188
+ self.solver.mkTerm(
189
+ cvc5.Kind.TO_INTEGER,
190
+ self.solver.mkTerm(
191
+ cvc5.Kind.ADD,
192
+ val,
193
+ self.solver.mkReal(1, 2))),
194
+ self.solver.mkTerm(
195
+ cvc5.Kind.NEG,
196
+ self.solver.mkTerm(
197
+ cvc5.Kind.TO_INTEGER,
198
+ self.solver.mkTerm(
199
+ cvc5.Kind.SUB,
200
+ self.solver.mkReal(1, 2),
201
+ val)))))
202
+
203
+ else:
204
+ assert False, function
205
+
206
+ self.function_mapping[function] = fun
207
+
208
+ for statement in node.statements:
209
+ statement.walk(self)
210
+
211
+ def visit_constant_declaration(self, node, tr_symbol, tr_value):
212
+ assert isinstance(node, smt.Constant_Declaration)
213
+ assert node.symbol in self.const_mapping
214
+
215
+ if tr_value is not None:
216
+ self.solver.assertFormula(
217
+ self.solver.mkTerm(cvc5.Kind.EQUAL, tr_symbol, tr_value))
218
+
219
+ if node.is_relevant:
220
+ self.relevant_values.append(node.symbol)
221
+
222
+ def visit_function_declaration(self, node, tr_sort, tr_body):
223
+ assert isinstance(node, smt.Function_Declaration)
224
+
225
+ if tr_body is None:
226
+ tr_params = [par.sort.walk(self)
227
+ for par in node.function.parameters]
228
+ fun = self.solver.declareFun(node.function.name,
229
+ tr_params,
230
+ tr_sort)
231
+
232
+ else:
233
+ tr_params = [par.walk(self)
234
+ for par in node.function.parameters]
235
+ fun = self.solver.defineFun(node.function.name,
236
+ tr_params,
237
+ tr_sort,
238
+ tr_body)
239
+
240
+ self.user_functions[node.function] = fun
241
+
242
+ def visit_assertion(self, node, tr_expression):
243
+ assert isinstance(node, smt.Assertion)
244
+
245
+ self.solver.assertFormula(tr_expression)
246
+
247
+ def visit_enumeration_declaration(self, node):
248
+ assert isinstance(node, smt.Enumeration_Declaration)
249
+
250
+ ctors = [self.solver.mkDatatypeConstructorDecl(literal)
251
+ for literal in node.sort.literals]
252
+
253
+ sort = self.solver.declareDatatype(node.sort.name, *ctors)
254
+ self.enum_mapping[node.sort] = sort
255
+
256
+ datatype = sort.getDatatype()
257
+ self.literal_mapping[node.sort] = {
258
+ literal: datatype.getConstructor(literal).getTerm()
259
+ for literal in node.sort.literals
260
+ }
261
+
262
+ def visit_record_declaration(self, node):
263
+ assert isinstance(node, smt.Record_Declaration)
264
+
265
+ ctor = self.solver.mkDatatypeConstructorDecl(node.sort.name +
266
+ "__cons")
267
+ for name, sort in node.sort.components.items():
268
+ if sort is node.sort:
269
+ ctor.addSelectorSelf(name)
270
+ else:
271
+ ctor.addSelector(name, sort.walk(self))
272
+
273
+ if node.sort.is_recursive:
274
+ null_ctor = self.solver.mkDatatypeConstructorDecl(node.sort.name +
275
+ "__null")
276
+ sort = self.solver.declareDatatype(node.sort.name,
277
+ null_ctor,
278
+ ctor)
279
+ else:
280
+ sort = self.solver.declareDatatype(node.sort.name, ctor)
281
+ self.record_mapping[node.sort] = sort
282
+
283
+ def visit_sort(self, node):
284
+ assert isinstance(node, smt.Sort)
285
+
286
+ if node.name == "Bool":
287
+ return self.solver.getBooleanSort()
288
+ elif node.name == "Int":
289
+ return self.solver.getIntegerSort()
290
+ elif node.name == "Real":
291
+ return self.solver.getRealSort()
292
+ elif node.name == "String":
293
+ return self.solver.getStringSort()
294
+ else:
295
+ assert False
296
+
297
+ def visit_parametric_sort(self, node, tr_parameters):
298
+ assert isinstance(node, smt.Parametric_Sort)
299
+ assert isinstance(tr_parameters, list)
300
+
301
+ if node not in self.sort_mapping:
302
+ if node.name == "Seq":
303
+ assert len(tr_parameters) == 1
304
+ self.sort_mapping[node] = \
305
+ self.solver.mkSequenceSort(tr_parameters[0])
306
+ else:
307
+ assert False
308
+
309
+ return self.sort_mapping[node]
310
+
311
+ def visit_function(self, node):
312
+ assert isinstance(node, smt.Function)
313
+ return self.user_functions[node]
314
+
315
+ def visit_enumeration(self, node):
316
+ assert isinstance(node, smt.Enumeration)
317
+ return self.enum_mapping[node]
318
+
319
+ def visit_record(self, node):
320
+ assert isinstance(node, smt.Record)
321
+ return self.record_mapping[node]
322
+
323
+ def visit_boolean_literal(self, node, tr_sort):
324
+ assert isinstance(node, smt.Boolean_Literal)
325
+ return self.solver.mkBoolean(node.value)
326
+
327
+ def visit_integer_literal(self, node, tr_sort):
328
+ assert isinstance(node, smt.Integer_Literal)
329
+ return self.solver.mkInteger(node.value)
330
+
331
+ def visit_real_literal(self, node, tr_sort):
332
+ assert isinstance(node, smt.Real_Literal)
333
+ num, den = node.value.as_integer_ratio()
334
+ return self.solver.mkReal(num, den)
335
+
336
+ def visit_enumeration_literal(self, node, tr_sort):
337
+ assert isinstance(node, smt.Enumeration_Literal)
338
+ cons = self.literal_mapping[node.sort][node.value]
339
+ return self.solver.mkTerm(cvc5.Kind.APPLY_CONSTRUCTOR, cons)
340
+
341
+ def visit_string_literal(self, node, tr_sort):
342
+ assert isinstance(node, smt.String_Literal)
343
+ return self.solver.mkString(node.value)
344
+
345
+ def visit_constant(self, node, tr_sort):
346
+ assert isinstance(node, smt.Constant)
347
+
348
+ if node not in self.const_mapping:
349
+ self.const_mapping[node] = self.solver.mkConst(tr_sort, node.name)
350
+
351
+ return self.const_mapping[node]
352
+
353
+ def visit_bound_variable(self, node, tr_sort):
354
+ assert isinstance(node, smt.Bound_Variable)
355
+
356
+ if node not in self.var_mapping:
357
+ self.var_mapping[node] = self.solver.mkVar(tr_sort, node.name)
358
+
359
+ return self.var_mapping[node]
360
+
361
+ def visit_boolean_negation(self, node, tr_sort, tr_expression):
362
+ assert isinstance(node, smt.Boolean_Negation)
363
+
364
+ return tr_expression.notTerm()
365
+
366
+ def visit_conjunction(self, node, tr_sort, tr_terms):
367
+ assert isinstance(node, smt.Conjunction)
368
+ assert isinstance(tr_terms, list)
369
+
370
+ return self.solver.mkTerm(cvc5.Kind.AND, *tr_terms)
371
+
372
+ def visit_disjunction(self, node, tr_sort, tr_terms):
373
+ assert isinstance(node, smt.Disjunction)
374
+ assert isinstance(tr_terms, list)
375
+
376
+ return self.solver.mkTerm(cvc5.Kind.OR, *tr_terms)
377
+
378
+ def visit_exclusive_disjunction(self, node, tr_sort, tr_lhs, tr_rhs):
379
+ assert isinstance(node, smt.Exclusive_Disjunction)
380
+
381
+ return self.solver.mkTerm(cvc5.Kind.XOR, tr_lhs, tr_rhs)
382
+
383
+ def visit_implication(self, node, tr_sort, tr_lhs, tr_rhs):
384
+ assert isinstance(node, smt.Implication)
385
+
386
+ return self.solver.mkTerm(cvc5.Kind.IMPLIES, tr_lhs, tr_rhs)
387
+
388
+ def visit_comparison(self, node, tr_lhs, tr_rhs):
389
+ assert isinstance(node, smt.Comparison)
390
+
391
+ kind = {"<" : cvc5.Kind.LT,
392
+ "<=" : cvc5.Kind.LEQ,
393
+ ">" : cvc5.Kind.GT,
394
+ ">=" : cvc5.Kind.GEQ,
395
+ "=" : cvc5.Kind.EQUAL}
396
+
397
+ return self.solver.mkTerm(kind[node.operator], tr_lhs, tr_rhs)
398
+
399
+ def visit_conversion_to_real(self, node, tr_value):
400
+ assert isinstance(node, smt.Conversion_To_Real)
401
+ return self.solver.mkTerm(cvc5.Kind.TO_REAL, tr_value)
402
+
403
+ def visit_conversion_to_integer(self, node, tr_value):
404
+ assert isinstance(node, smt.Conversion_To_Integer)
405
+ if node.rounding == "rtn":
406
+ return self.solver.mkTerm(cvc5.Kind.TO_INTEGER, tr_value)
407
+ else:
408
+ assert node.rounding == "rna"
409
+ return self.solver.mkTerm(cvc5.Kind.APPLY_UF,
410
+ self.function_mapping["to_int_rna"],
411
+ tr_value)
412
+
413
+ def visit_unary_int_arithmetic_op(self, node, tr_operand):
414
+ assert isinstance(node, smt.Unary_Int_Arithmetic_Op)
415
+
416
+ kind = {"-" : cvc5.Kind.NEG,
417
+ "abs" : cvc5.Kind.ABS}
418
+
419
+ return self.solver.mkTerm(kind[node.operator], tr_operand)
420
+
421
+ def visit_unary_real_arithmetic_op(self, node, tr_operand):
422
+ assert isinstance(node, smt.Unary_Real_Arithmetic_Op)
423
+
424
+ kind = {"-" : cvc5.Kind.NEG,
425
+ "abs" : cvc5.Kind.ABS}
426
+
427
+ return self.solver.mkTerm(kind[node.operator], tr_operand)
428
+
429
+ def visit_binary_int_arithmetic_op(self, node, tr_lhs, tr_rhs):
430
+ assert isinstance(node, smt.Binary_Int_Arithmetic_Op)
431
+
432
+ if node.operator in ("floor_div", "ada_remainder"):
433
+ return self.solver.mkTerm(cvc5.Kind.APPLY_UF,
434
+ self.function_mapping[node.operator],
435
+ tr_lhs,
436
+ tr_rhs)
437
+
438
+ kind = {"+" : cvc5.Kind.ADD,
439
+ "-" : cvc5.Kind.SUB,
440
+ "*" : cvc5.Kind.MULT,
441
+ "div" : cvc5.Kind.INTS_DIVISION,
442
+ "mod" : cvc5.Kind.INTS_MODULUS}
443
+
444
+ return self.solver.mkTerm(kind[node.operator], tr_lhs, tr_rhs)
445
+
446
+ def visit_binary_real_arithmetic_op(self, node, tr_lhs, tr_rhs):
447
+ assert isinstance(node, smt.Binary_Real_Arithmetic_Op)
448
+
449
+ kind = {"+" : cvc5.Kind.ADD,
450
+ "-" : cvc5.Kind.SUB,
451
+ "*" : cvc5.Kind.MULT,
452
+ "/" : cvc5.Kind.DIVISION}
453
+
454
+ return self.solver.mkTerm(kind[node.operator], tr_lhs, tr_rhs)
455
+
456
+ def visit_string_length(self, node, tr_string):
457
+ assert isinstance(node, smt.String_Length)
458
+ return self.solver.mkTerm(cvc5.Kind.STRING_LENGTH, tr_string)
459
+
460
+ def visit_string_predicate(self, node, tr_first, tr_second):
461
+ assert isinstance(node, smt.String_Predicate)
462
+
463
+ kind = {"contains" : cvc5.Kind.STRING_CONTAINS,
464
+ "prefixof" : cvc5.Kind.STRING_PREFIX,
465
+ "suffixof" : cvc5.Kind.STRING_SUFFIX}
466
+
467
+ return self.solver.mkTerm(kind[node.operation],
468
+ tr_first,
469
+ tr_second)
470
+
471
+ def visit_string_concatenation(self, node, tr_lhs, tr_rhs):
472
+ assert isinstance(node, smt.String_Concatenation)
473
+ return self.solver.mkTerm(cvc5.Kind.STRING_CONCAT, tr_lhs, tr_rhs)
474
+
475
+ def visit_sequence_length(self, node, tr_sequence):
476
+ assert isinstance(node, smt.Sequence_Length)
477
+ return self.solver.mkTerm(cvc5.Kind.SEQ_LENGTH, tr_sequence)
478
+
479
+ def visit_sequence_index(self, node, tr_sequence, tr_index):
480
+ assert isinstance(node, smt.Sequence_Index)
481
+ return self.solver.mkTerm(cvc5.Kind.SEQ_NTH, tr_sequence, tr_index)
482
+
483
+ def visit_sequence_contains(self, node, tr_sequence, tr_item):
484
+ assert isinstance(node, smt.Sequence_Contains)
485
+ return self.solver.mkTerm(cvc5.Kind.SEQ_CONTAINS,
486
+ tr_sequence,
487
+ self.solver.mkTerm(cvc5.Kind.SEQ_UNIT,
488
+ tr_item))
489
+
490
+ def visit_sequence_concatenation(self, node, tr_lhs, tr_rhs):
491
+ assert isinstance(node, smt.Sequence_Concatenation)
492
+ return self.solver.mkTerm(cvc5.Kind.SEQ_CONCAT, tr_lhs, tr_rhs)
493
+
494
+ def visit_record_access(self, node, tr_record):
495
+ assert isinstance(node, smt.Record_Access)
496
+ assert node.record.sort in self.record_mapping
497
+ s_record_sort = self.record_mapping[node.record.sort]
498
+ s_selector = s_record_sort.getDatatype().getSelector(node.component)
499
+ return self.solver.mkTerm(cvc5.Kind.APPLY_SELECTOR,
500
+ s_selector.getTerm(),
501
+ tr_record)
502
+
503
+ def visit_record_null_check(self, node, tr_record):
504
+ assert isinstance(node, smt.Record_Null_Check)
505
+ s_record_sort = self.record_mapping[node.record.sort]
506
+ s_dt = s_record_sort.getDatatype()
507
+ s_cons = s_dt.getConstructor(node.record.sort.name + "__null")
508
+ return self.solver.mkTerm(
509
+ cvc5.Kind.APPLY_TESTER,
510
+ s_cons.getTesterTerm(),
511
+ tr_record)
512
+
513
+ def visit_function_application(self, node, tr_function, tr_args):
514
+ assert isinstance(node, smt.Function_Application)
515
+ assert isinstance(tr_args, list)
516
+
517
+ return self.solver.mkTerm(cvc5.Kind.APPLY_UF,
518
+ tr_function,
519
+ *tr_args)
520
+
521
+ def visit_conditional(self, node, tr_condition, tr_true, tr_false):
522
+ assert isinstance(node, smt.Conditional)
523
+ return self.solver.mkTerm(cvc5.Kind.ITE,
524
+ tr_condition,
525
+ tr_true,
526
+ tr_false)
527
+
528
+ def visit_quantifier(self, node, tr_variables, tr_body):
529
+ assert isinstance(node, smt.Quantifier)
530
+
531
+ kind = {"forall" : cvc5.Kind.FORALL,
532
+ "exists" : cvc5.Kind.EXISTS}
533
+
534
+ return self.solver.mkTerm(kind[node.kind],
535
+ self.solver.mkTerm(cvc5.Kind.VARIABLE_LIST,
536
+ *tr_variables),
537
+ tr_body)