model-checker 0.1__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.
- model_checker-0.1.dist-info/LICENCE +19 -0
- model_checker-0.1.dist-info/METADATA +16 -0
- model_checker-0.1.dist-info/RECORD +11 -0
- model_checker-0.1.dist-info/WHEEL +5 -0
- model_checker-0.1.dist-info/top_level.txt +1 -0
- package/__init__.py +0 -0
- package/model_definitions.py +258 -0
- package/model_structure.py +402 -0
- package/semantics.py +490 -0
- package/syntax.py +162 -0
- package/test_complete.py +139 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2024 Benjamin Brast-McKie
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: model_checker
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: A hyperintensional model checker for counterfactual conditionals
|
|
5
|
+
Author-email: Benjamin Brast-McKie <benbrastmckie@gmail.com>, Miguel Buitrago <mfbuit46@mit.edu>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/benbrastmckie/ModelChecker
|
|
8
|
+
Project-URL: Issues, https://github.com/benbrastmckie/ModelChecker/issues
|
|
9
|
+
Keywords: semantics,Z3,counterfactuals,model checker
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENCE
|
|
15
|
+
Requires-Dist: z3-solver
|
|
16
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
package/model_definitions.py,sha256=HbsOCBxRRG86ylBEw2XK5t1hzYxlLrFmsZW3NYtBqw0,10290
|
|
3
|
+
package/model_structure.py,sha256=PCvxwji4_z8l_N2PBoKBYs6kxO8sXfvxvUKfXZAcRXU,19408
|
|
4
|
+
package/semantics.py,sha256=MJnufO4EivSECv5yUtVPz30UWcSw7Fqz23qKHHK8YaQ,19261
|
|
5
|
+
package/syntax.py,sha256=H2TyLhXz8WNXZfocqoWNNAr1eXk1YEaRQ2e23cqy-Y0,6776
|
|
6
|
+
package/test_complete.py,sha256=lu9HjS7ZHQBaPws0k8TX21nW2FKKfmzxcCODWCNL50Y,3932
|
|
7
|
+
model_checker-0.1.dist-info/LICENCE,sha256=1b7pP-AWYWwdmQvnAd2F4oWWP2zuFwBk4fFju8Jnm94,1064
|
|
8
|
+
model_checker-0.1.dist-info/METADATA,sha256=ZB0du4IZTSc9ZJkhPm8lOrLDYMympELrp0QxxS5fA_s,671
|
|
9
|
+
model_checker-0.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
10
|
+
model_checker-0.1.dist-info/top_level.txt,sha256=TpQW7893uEAT8mPh-QCMZWhhKYvlqryC9Fjm5Hxg2f8,8
|
|
11
|
+
model_checker-0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
package
|
package/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
'''
|
|
2
|
+
file contains all definitions for defining the model structure
|
|
3
|
+
'''
|
|
4
|
+
|
|
5
|
+
from z3 import (
|
|
6
|
+
BitVecVal, simplify,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from syntax import Infix
|
|
10
|
+
|
|
11
|
+
def summation(n, func, start = 0):
|
|
12
|
+
'''summation of i ranging from start to n of func(i)
|
|
13
|
+
used in find_all_bits'''
|
|
14
|
+
if start == n:
|
|
15
|
+
return func(start)
|
|
16
|
+
return func(start) + summation(n,func,start+1)
|
|
17
|
+
|
|
18
|
+
def find_all_bits(size):
|
|
19
|
+
'''extract all bitvectors from the input model
|
|
20
|
+
imported by model_structure'''
|
|
21
|
+
all_bits = []
|
|
22
|
+
max_bit_number = summation(
|
|
23
|
+
size + 1, lambda x: 2**x
|
|
24
|
+
) # this should hopefully be enough to cover all states
|
|
25
|
+
for val in range(max_bit_number):
|
|
26
|
+
test_bit = BitVecVal(val, size)
|
|
27
|
+
if test_bit in all_bits:
|
|
28
|
+
# break once bits start to repeat
|
|
29
|
+
continue
|
|
30
|
+
all_bits.append(test_bit)
|
|
31
|
+
return all_bits
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def find_poss_bits(model,all_bits, possible):
|
|
35
|
+
'''extract all possible bitvectors from all_bits given the model
|
|
36
|
+
imported by model_structure'''
|
|
37
|
+
poss_bits = []
|
|
38
|
+
for bit in all_bits:
|
|
39
|
+
if model.evaluate(possible(bit)): # of type/sort BoolRef
|
|
40
|
+
poss_bits.append(bit)
|
|
41
|
+
return poss_bits
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def find_world_bits(poss_bits):
|
|
45
|
+
'''finds the world bits from a list of possible bits.
|
|
46
|
+
used in print_states() and find_relations()'''
|
|
47
|
+
not_worlds = []
|
|
48
|
+
for potential_world in poss_bits:
|
|
49
|
+
if potential_world in not_worlds:
|
|
50
|
+
continue
|
|
51
|
+
for test in poss_bits:
|
|
52
|
+
if bit_part(test, potential_world):
|
|
53
|
+
continue
|
|
54
|
+
if bit_proper_part(potential_world, test):
|
|
55
|
+
not_worlds.append(potential_world)
|
|
56
|
+
break
|
|
57
|
+
world_bits = [world for world in poss_bits if world not in not_worlds]
|
|
58
|
+
# for world in world_bits:
|
|
59
|
+
# print(model.evaluate(is_world(world)))
|
|
60
|
+
# if not model.evaluate(is_world(world)):
|
|
61
|
+
# raise ValueError(f'{world} was in world_bits but is not a world per the model')
|
|
62
|
+
# for not_world in not_worlds:
|
|
63
|
+
# print(model.evaluate(is_world(not_world)))
|
|
64
|
+
# # if model.evaluate(is_world(not_world)):
|
|
65
|
+
# # raise ValueError(f'{not_world} was not in world_bits but is a world per the model')
|
|
66
|
+
return world_bits
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def find_compatible_parts(verifier_bit, poss_bits, eval_world):
|
|
70
|
+
"""
|
|
71
|
+
Finds the parts of the eval_world compatible with the verifier_bit.
|
|
72
|
+
Used in find_alt_bits()
|
|
73
|
+
"""
|
|
74
|
+
comp_parts = []
|
|
75
|
+
for part in poss_bits:
|
|
76
|
+
if bit_fusion(verifier_bit, part) in poss_bits and bit_part(part, eval_world):
|
|
77
|
+
comp_parts.append(part)
|
|
78
|
+
# ie, if fusion is possible and the bit part is in the eval_world
|
|
79
|
+
return comp_parts
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def find_max_comp_ver_parts(verifier_bit, comp_parts):
|
|
83
|
+
"""
|
|
84
|
+
Finds a list of fusions of the verifier_bit and a maximal compatible part.
|
|
85
|
+
Used in find_alt_bits(), immediately after find_compatible_parts() above.
|
|
86
|
+
"""
|
|
87
|
+
not_max_comp_part = []
|
|
88
|
+
for max_part in comp_parts:
|
|
89
|
+
for test in comp_parts:
|
|
90
|
+
if bit_proper_part(max_part, test):
|
|
91
|
+
not_max_comp_part.append(max_part)
|
|
92
|
+
break # continues with the first for loop
|
|
93
|
+
max_comp_parts = [part for part in comp_parts if part not in not_max_comp_part]
|
|
94
|
+
max_comp_ver_parts = [bit_fusion(verifier_bit, max) for max in max_comp_parts]
|
|
95
|
+
return max_comp_ver_parts
|
|
96
|
+
|
|
97
|
+
def relate_sents_and_states(all_bits, sentence, model, relation):
|
|
98
|
+
"""helper function for finding verifier and falsifier states to sentences in a model
|
|
99
|
+
Used in find_relations()
|
|
100
|
+
DOES NOT CHECK IF THESE ARE POSSIBLE. """
|
|
101
|
+
relation_set = set()
|
|
102
|
+
for bit in all_bits:
|
|
103
|
+
if model.evaluate(relation(bit, model[sentence])):
|
|
104
|
+
relation_set.add(bit)
|
|
105
|
+
return relation_set
|
|
106
|
+
|
|
107
|
+
def find_true_and_false_in_alt(alt_bit, parent_model_structure):
|
|
108
|
+
"""returns two sets as a tuple, one being the set of sentences true in the alt world and the other the set being false.
|
|
109
|
+
Used in Proposition class print_alt_worlds"""
|
|
110
|
+
extensional_sentences = parent_model_structure.extensional_subsentences
|
|
111
|
+
all_bits = parent_model_structure.all_bits
|
|
112
|
+
true_in_alt = []
|
|
113
|
+
for R in extensional_sentences:
|
|
114
|
+
for bit in all_bits:
|
|
115
|
+
# print(model.evaluate(extended_verify(bit, R, evaluate=True), model_completion=True))
|
|
116
|
+
# print(type(model.evaluate(extended_verify(bit, R, evaluate=True), model_completion=True)))
|
|
117
|
+
if bit in parent_model_structure.find_complex_proposition(R)[0] and bit_part(bit, alt_bit):
|
|
118
|
+
true_in_alt.append(R)
|
|
119
|
+
break # returns to the for loop over sentence_letters
|
|
120
|
+
false_in_alt = [R for R in extensional_sentences if not R in true_in_alt] # replace with
|
|
121
|
+
return (true_in_alt, false_in_alt)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def make_set_pretty_for_print(set_with_strings):
|
|
125
|
+
"""input a set with strings
|
|
126
|
+
print that same set but with no quotation marks around each individual string, and also with the set in order
|
|
127
|
+
returns the set as a string
|
|
128
|
+
Used in print_vers_and_fals() and print_alt_worlds()"""
|
|
129
|
+
sorted_set = sorted(list(set_with_strings)) # actually type list, not set
|
|
130
|
+
print_str = "{"
|
|
131
|
+
for i, elem in enumerate(sorted_set):
|
|
132
|
+
print_str += elem
|
|
133
|
+
if i != len(sorted_set) - 1:
|
|
134
|
+
print_str += ", "
|
|
135
|
+
print_str += "}"
|
|
136
|
+
return print_str
|
|
137
|
+
|
|
138
|
+
def product(set_A, set_B):
|
|
139
|
+
product_set = set()
|
|
140
|
+
for a in set_A:
|
|
141
|
+
for b in set_B:
|
|
142
|
+
product_set.add(bit_fusion(a,b))
|
|
143
|
+
return product_set
|
|
144
|
+
|
|
145
|
+
def coproduct(set_A, set_B):
|
|
146
|
+
A_U_B = set_A.union(set_B)
|
|
147
|
+
return A_U_B.union(product(set_A, set_B))
|
|
148
|
+
|
|
149
|
+
def atomic_propositions_dict(all_bits, sentence_letters, model, verify, falsify):
|
|
150
|
+
atomic_VFs_dict = {}
|
|
151
|
+
for letter in sentence_letters:
|
|
152
|
+
ver_bits = relate_sents_and_states(all_bits, letter, model, verify)
|
|
153
|
+
fal_bits = relate_sents_and_states(all_bits, letter, model, falsify)
|
|
154
|
+
atomic_VFs_dict[letter] = (ver_bits, fal_bits)
|
|
155
|
+
return atomic_VFs_dict
|
|
156
|
+
|
|
157
|
+
def print_alt_relation(alt_relation_set, alt_bit, relation_truth_value, N):
|
|
158
|
+
"""true is a string representing the relation ("true" for true_in_alt; m.m. for false) that is being used for
|
|
159
|
+
alt_relation_set is the set of prefix sentences that have truth value relation_truth_value in a
|
|
160
|
+
given alternative world alt_bit
|
|
161
|
+
returns None, only prints
|
|
162
|
+
Used in print_alt_worlds()"""
|
|
163
|
+
if not alt_relation_set:
|
|
164
|
+
return
|
|
165
|
+
alt_relation_list = sorted([Infix(sent) for sent in alt_relation_set])
|
|
166
|
+
alt_relation_string = ", ".join(alt_relation_list)
|
|
167
|
+
if len(alt_relation_set) == 1:
|
|
168
|
+
print(f" {alt_relation_string} is {relation_truth_value} in {bitvec_to_substates(alt_bit, N)}")
|
|
169
|
+
else:
|
|
170
|
+
print(f" {alt_relation_string} are {relation_truth_value} in {bitvec_to_substates(alt_bit, N)}")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
#############################################
|
|
174
|
+
######### MOVED FROM DEFINITIONS.PY #########
|
|
175
|
+
#############################################
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def bit_fusion(bit_s, bit_t):
|
|
180
|
+
"""the result of taking the maximum for each index in _s and _t"""
|
|
181
|
+
return simplify(bit_s | bit_t)
|
|
182
|
+
# NOTE: this does seem to make a difference, otherwise no comp_parts
|
|
183
|
+
|
|
184
|
+
def bit_part(bit_s, bit_t):
|
|
185
|
+
"""the fusion of _s and _t is identical to bit_t"""
|
|
186
|
+
return bool(simplify(bit_fusion(bit_s, bit_t) == bit_t))
|
|
187
|
+
# NOTE: this does seem to make a difference, otherwise no comp_parts
|
|
188
|
+
|
|
189
|
+
def bit_proper_part(bit_s, bit_t):
|
|
190
|
+
"""bit_s is a part of bit_t and bit_t is not a part of bit_s"""
|
|
191
|
+
return bool(bit_part(bit_s, bit_t)) and not bit_s == bit_t
|
|
192
|
+
# NOTE: this does not seem to make a difference and so has been turned off
|
|
193
|
+
# in the interest of discovering if it is required or not
|
|
194
|
+
# return bool(bit_part(bit_s, bit_t)) and not bit_s == bit_t
|
|
195
|
+
|
|
196
|
+
def index_to_substate(index):
|
|
197
|
+
'''
|
|
198
|
+
test cases should make evident what's going on
|
|
199
|
+
>>> index_to_substate(0)
|
|
200
|
+
'a'
|
|
201
|
+
>>> index_to_substate(26)
|
|
202
|
+
'aa'
|
|
203
|
+
>>> index_to_substate(27)
|
|
204
|
+
'bb'
|
|
205
|
+
>>> index_to_substate(194)
|
|
206
|
+
'mmmmmmmm'
|
|
207
|
+
used in bitvec_to_substates
|
|
208
|
+
'''
|
|
209
|
+
number = index + 1 # because python indices start at 0
|
|
210
|
+
letter_dict = {1:'a', 2:'b', 3:'c', 4:'d', 5:'e', 6:'f', 7:'g', 8:'h', 9:'i', 10:'j',
|
|
211
|
+
11:'k', 12:'l', 13:'m', 14:'n', 15:'o', 16:'p', 17:'q', 18:'r', 19:'s', 20:'t',
|
|
212
|
+
21:'u', 22:'v', 23:'w', 24:'x', 25:'y', 26:'z'} # could be make less hard-code-y
|
|
213
|
+
# but this makes it clearer and more intuitive what we want to happen
|
|
214
|
+
letter = letter_dict[number%26]
|
|
215
|
+
return ((number//26) + 1) * letter
|
|
216
|
+
|
|
217
|
+
def int_to_binary(integer, number, backwards_binary_str = ''):
|
|
218
|
+
'''converts a #x string to a #b string. follows the first algorithm that shows up on google
|
|
219
|
+
when you google how to do this
|
|
220
|
+
used in bitvec_to_substates'''
|
|
221
|
+
rem = integer%2
|
|
222
|
+
new_backwards_str = backwards_binary_str + str(rem)
|
|
223
|
+
if integer//2 == 0: # base case: we've reached the end
|
|
224
|
+
remaining_0s_to_tack_on = number - len(new_backwards_str) # to fill in the zeroes
|
|
225
|
+
return '#b' + remaining_0s_to_tack_on * '0' + new_backwards_str[::-1]
|
|
226
|
+
new_int = integer//2
|
|
227
|
+
return int_to_binary(new_int, number, new_backwards_str)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# TODO: linter says all or none of the returns should be an expression
|
|
231
|
+
def bitvec_to_substates(bit_vec, N):
|
|
232
|
+
'''converts bitvectors to fusions of atomic states.'''
|
|
233
|
+
bit_vec_as_string = bit_vec.sexpr()
|
|
234
|
+
if 'x' in bit_vec_as_string: # if we have a hexadecimal, ie N=4m
|
|
235
|
+
integer = int(bit_vec_as_string[2:],16)
|
|
236
|
+
bit_vec_as_string = int_to_binary(integer, N)
|
|
237
|
+
bit_vec_backwards = bit_vec_as_string[::-1]
|
|
238
|
+
state_repr = ""
|
|
239
|
+
for i, char in enumerate(bit_vec_backwards):
|
|
240
|
+
if char == "b":
|
|
241
|
+
if not state_repr:
|
|
242
|
+
return "□" # null state
|
|
243
|
+
return state_repr[0 : len(state_repr) - 1]
|
|
244
|
+
if char == "1":
|
|
245
|
+
state_repr += index_to_substate(i)
|
|
246
|
+
state_repr += "."
|
|
247
|
+
|
|
248
|
+
def infix_combine(prem,con):
|
|
249
|
+
'''combines the premises with the negation of the conclusion(s).
|
|
250
|
+
premises are infix sentences, and so are the conclusions
|
|
251
|
+
imported by model_structure'''
|
|
252
|
+
# if prem is None:
|
|
253
|
+
# prem = []
|
|
254
|
+
input_sent = prem
|
|
255
|
+
for sent in con:
|
|
256
|
+
neg_sent = '\\neg ' + sent
|
|
257
|
+
input_sent.append(neg_sent)
|
|
258
|
+
return input_sent
|