mirror of
https://github.com/Brandon-Rozek/matmod.git
synced 2026-02-11 08:01:21 +00:00
Compare commits
13 commits
4150ac2a7a
...
51bf1a44d9
| Author | SHA1 | Date | |
|---|---|---|---|
| 51bf1a44d9 | |||
| 06cca7d32e | |||
| 85ef364a57 | |||
| 51f8adcd44 | |||
| bf86bfd83e | |||
| 6d87793803 | |||
| f8eca388d4 | |||
| 07d7b2fdb1 | |||
| 51c26dd9fc | |||
| bf2735023f | |||
| f799a1ff5d | |||
| ea0b487528 | |||
| 18c2bcb673 |
8 changed files with 238 additions and 166 deletions
81
R.py
81
R.py
|
|
@ -12,7 +12,8 @@ from logic import (
|
||||||
)
|
)
|
||||||
from model import Model, ModelFunction, ModelValue, satisfiable
|
from model import Model, ModelFunction, ModelValue, satisfiable
|
||||||
from generate_model import generate_model
|
from generate_model import generate_model
|
||||||
# from vsp import has_vsp
|
from vsp import has_vsp
|
||||||
|
from smt import smt_is_loaded
|
||||||
|
|
||||||
|
|
||||||
# ===================================================
|
# ===================================================
|
||||||
|
|
@ -56,12 +57,17 @@ disjunction_rules = {
|
||||||
Rule({Conjunction(x, Disjunction(y, z)),}, Disjunction(Conjunction(x, y), Conjunction(x, z)))
|
Rule({Conjunction(x, Disjunction(y, z)),}, Disjunction(Conjunction(x, y), Conjunction(x, z)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
falsification_rules = {
|
||||||
|
# At least one value is non-designated
|
||||||
|
Rule(set(), x)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
logic_rules = implication_rules | negation_rules | conjunction_rules | disjunction_rules
|
logic_rules = implication_rules | negation_rules | conjunction_rules | disjunction_rules
|
||||||
|
|
||||||
operations = {Negation, Conjunction, Disjunction, Implication}
|
operations = {Negation, Conjunction, Disjunction, Implication}
|
||||||
|
|
||||||
R_logic = Logic(operations, logic_rules, "R")
|
R_logic = Logic(operations, logic_rules, falsification_rules, "R")
|
||||||
|
|
||||||
# ===============================
|
# ===============================
|
||||||
|
|
||||||
|
|
@ -69,36 +75,36 @@ R_logic = Logic(operations, logic_rules, "R")
|
||||||
Example 2-Element Model of R
|
Example 2-Element Model of R
|
||||||
"""
|
"""
|
||||||
|
|
||||||
a0 = ModelValue("a0")
|
a0 = ModelValue("0")
|
||||||
a1 = ModelValue("a1")
|
a1 = ModelValue("1")
|
||||||
|
|
||||||
carrier_set = {a0, a1}
|
carrier_set = {a0, a1}
|
||||||
|
|
||||||
mnegation = ModelFunction(1, {
|
mnegation = ModelFunction(1, {
|
||||||
a0: a1,
|
a0: a1,
|
||||||
a1: a0
|
a1: a0
|
||||||
})
|
}, "¬")
|
||||||
|
|
||||||
mimplication = ModelFunction(2, {
|
mimplication = ModelFunction(2, {
|
||||||
(a0, a0): a1,
|
(a0, a0): a1,
|
||||||
(a0, a1): a1,
|
(a0, a1): a1,
|
||||||
(a1, a0): a0,
|
(a1, a0): a0,
|
||||||
(a1, a1): a1
|
(a1, a1): a1
|
||||||
})
|
}, "→")
|
||||||
|
|
||||||
mconjunction = ModelFunction(2, {
|
mconjunction = ModelFunction(2, {
|
||||||
(a0, a0): a0,
|
(a0, a0): a0,
|
||||||
(a0, a1): a0,
|
(a0, a1): a0,
|
||||||
(a1, a0): a0,
|
(a1, a0): a0,
|
||||||
(a1, a1): a1
|
(a1, a1): a1
|
||||||
})
|
}, "∧")
|
||||||
|
|
||||||
mdisjunction = ModelFunction(2, {
|
mdisjunction = ModelFunction(2, {
|
||||||
(a0, a0): a0,
|
(a0, a0): a0,
|
||||||
(a0, a1): a1,
|
(a0, a1): a1,
|
||||||
(a1, a0): a1,
|
(a1, a0): a1,
|
||||||
(a1, a1): a1
|
(a1, a1): a1
|
||||||
})
|
}, "∨")
|
||||||
|
|
||||||
|
|
||||||
designated_values = {a1}
|
designated_values = {a1}
|
||||||
|
|
@ -117,11 +123,18 @@ interpretation = {
|
||||||
|
|
||||||
print(R_model_2)
|
print(R_model_2)
|
||||||
|
|
||||||
|
print(f"Does {R_model_2.name} satisfy the logic R?", satisfiable(R_logic, R_model_2, interpretation))
|
||||||
|
|
||||||
|
if smt_is_loaded():
|
||||||
|
print(has_vsp(R_model_2, mimplication, True, True))
|
||||||
|
else:
|
||||||
|
print("Z3 not setup, skipping VSP check...")
|
||||||
|
|
||||||
|
|
||||||
# =================================
|
# =================================
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Generate models of R of a specified size
|
Generate models of R of a specified size using the slow approach
|
||||||
"""
|
"""
|
||||||
|
|
||||||
print("*" * 30)
|
print("*" * 30)
|
||||||
|
|
@ -130,14 +143,20 @@ model_size = 2
|
||||||
print("Generating models of Logic", R_logic.name, "of size", model_size)
|
print("Generating models of Logic", R_logic.name, "of size", model_size)
|
||||||
solutions = generate_model(R_logic, model_size, print_model=False)
|
solutions = generate_model(R_logic, model_size, print_model=False)
|
||||||
|
|
||||||
print(f"Found {len(solutions)} satisfiable models")
|
if smt_is_loaded():
|
||||||
|
num_satisfies_vsp = 0
|
||||||
|
for model, interpretation in solutions:
|
||||||
|
negation_defined = Negation in interpretation
|
||||||
|
conj_disj_defined = Conjunction in interpretation and Disjunction in interpretation
|
||||||
|
if has_vsp(model, interpretation[Implication], negation_defined, conj_disj_defined).has_vsp:
|
||||||
|
num_satisfies_vsp += 1
|
||||||
|
|
||||||
|
print(f"Found {len(solutions)} satisfiable models of size {model_size}, {num_satisfies_vsp} of which satisfy VSP")
|
||||||
|
|
||||||
# for model, interpretation in solutions:
|
|
||||||
# print(has_vsp(model, interpretation))
|
|
||||||
|
|
||||||
print("*" * 30)
|
print("*" * 30)
|
||||||
|
|
||||||
######
|
# =================================
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Showing the smallest model for R that has the
|
Showing the smallest model for R that has the
|
||||||
|
|
@ -146,12 +165,12 @@ variable sharing property.
|
||||||
This model has 6 elements.
|
This model has 6 elements.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
a0 = ModelValue("a0")
|
a0 = ModelValue("0")
|
||||||
a1 = ModelValue("a1")
|
a1 = ModelValue("1")
|
||||||
a2 = ModelValue("a2")
|
a2 = ModelValue("2")
|
||||||
a3 = ModelValue("a3")
|
a3 = ModelValue("3")
|
||||||
a4 = ModelValue("a4")
|
a4 = ModelValue("4")
|
||||||
a5 = ModelValue("a5")
|
a5 = ModelValue("5")
|
||||||
|
|
||||||
carrier_set = { a0, a1, a2, a3, a4, a5 }
|
carrier_set = { a0, a1, a2, a3, a4, a5 }
|
||||||
designated_values = {a1, a2, a3, a4, a5 }
|
designated_values = {a1, a2, a3, a4, a5 }
|
||||||
|
|
@ -312,4 +331,26 @@ interpretation = {
|
||||||
|
|
||||||
print(R_model_6)
|
print(R_model_6)
|
||||||
print(f"Model {R_model_6.name} satisfies logic {R_logic.name}?", satisfiable(R_logic, R_model_6, interpretation))
|
print(f"Model {R_model_6.name} satisfies logic {R_logic.name}?", satisfiable(R_logic, R_model_6, interpretation))
|
||||||
# print(has_vsp(R_model_6, interpretation))
|
if smt_is_loaded():
|
||||||
|
print(has_vsp(R_model_6, mimplication, True, True))
|
||||||
|
else:
|
||||||
|
print("Z3 not loaded, skipping VSP check...")
|
||||||
|
|
||||||
|
"""
|
||||||
|
Generate models of R of a specified size using the SMT approach
|
||||||
|
"""
|
||||||
|
|
||||||
|
from vsp import logic_has_vsp
|
||||||
|
|
||||||
|
size = 7
|
||||||
|
print(f"Searching for a model of size {size} which witness VSP...")
|
||||||
|
if smt_is_loaded():
|
||||||
|
solution = logic_has_vsp(R_logic, size)
|
||||||
|
if solution is None:
|
||||||
|
print(f"No models found of size {size} which witness VSP")
|
||||||
|
else:
|
||||||
|
model, vsp_result = solution
|
||||||
|
print(vsp_result)
|
||||||
|
print(model)
|
||||||
|
else:
|
||||||
|
print("Z3 not setup, skipping...")
|
||||||
|
|
@ -67,7 +67,7 @@ def only_rules_with(rules: Set[Rule], operation: Operation) -> List[Rule]:
|
||||||
|
|
||||||
def possible_interpretations(
|
def possible_interpretations(
|
||||||
logic: Logic, carrier_set: Set[ModelValue],
|
logic: Logic, carrier_set: Set[ModelValue],
|
||||||
designated_values: Set[ModelValue]):
|
designated_values: Set[ModelValue], debug: bool):
|
||||||
"""
|
"""
|
||||||
Consider every possible interpretation of operations
|
Consider every possible interpretation of operations
|
||||||
within the specified logic given the carrier set of
|
within the specified logic given the carrier set of
|
||||||
|
|
@ -100,7 +100,7 @@ def possible_interpretations(
|
||||||
passed_functions = candidate_functions
|
passed_functions = candidate_functions
|
||||||
if len(passed_functions) == 0:
|
if len(passed_functions) == 0:
|
||||||
raise Exception("No interpretation satisfies the axioms for the operation " + str(operation))
|
raise Exception("No interpretation satisfies the axioms for the operation " + str(operation))
|
||||||
else:
|
elif debug:
|
||||||
print(
|
print(
|
||||||
f"Operation {operation.symbol} has {len(passed_functions)} candidate functions"
|
f"Operation {operation.symbol} has {len(passed_functions)} candidate functions"
|
||||||
)
|
)
|
||||||
|
|
@ -120,7 +120,7 @@ def possible_interpretations(
|
||||||
|
|
||||||
def generate_model(
|
def generate_model(
|
||||||
logic: Logic, number_elements: int, num_solutions: int = -1,
|
logic: Logic, number_elements: int, num_solutions: int = -1,
|
||||||
print_model=False) -> List[Tuple[Model, Interpretation]]:
|
print_model=False, debug=False) -> List[Tuple[Model, Interpretation]]:
|
||||||
"""
|
"""
|
||||||
Generate the specified number of models that
|
Generate the specified number of models that
|
||||||
satisfy a logic of a certain size.
|
satisfy a logic of a certain size.
|
||||||
|
|
@ -136,9 +136,10 @@ def generate_model(
|
||||||
|
|
||||||
for designated_values in possible_designated_values:
|
for designated_values in possible_designated_values:
|
||||||
designated_values = set(designated_values)
|
designated_values = set(designated_values)
|
||||||
|
if debug:
|
||||||
print("Considering models for designated values", set_to_str(designated_values))
|
print("Considering models for designated values", set_to_str(designated_values))
|
||||||
|
|
||||||
possible_interps = possible_interpretations(logic, carrier_set, designated_values)
|
possible_interps = possible_interpretations(logic, carrier_set, designated_values, debug)
|
||||||
for interpretation in possible_interps:
|
for interpretation in possible_interps:
|
||||||
is_valid = True
|
is_valid = True
|
||||||
model = Model(carrier_set, set(interpretation.values()), designated_values)
|
model = Model(carrier_set, set(interpretation.values()), designated_values)
|
||||||
|
|
|
||||||
2
logic.py
2
logic.py
|
|
@ -85,7 +85,7 @@ class Logic:
|
||||||
name: Optional[str] = None):
|
name: Optional[str] = None):
|
||||||
self.operations = operations
|
self.operations = operations
|
||||||
self.rules = rules
|
self.rules = rules
|
||||||
self.falsifies = falsifies
|
self.falsifies = falsifies if falsifies is not None else set()
|
||||||
self.name = str(abs(hash((
|
self.name = str(abs(hash((
|
||||||
frozenset(operations),
|
frozenset(operations),
|
||||||
frozenset(rules)
|
frozenset(rules)
|
||||||
|
|
|
||||||
103
model.py
103
model.py
|
|
@ -5,7 +5,7 @@ a given logic.
|
||||||
from common import set_to_str, immutable
|
from common import set_to_str, immutable
|
||||||
from logic import (
|
from logic import (
|
||||||
get_propostional_variables, Logic,
|
get_propostional_variables, Logic,
|
||||||
Operation, PropositionalVariable, Term
|
Operation, PropositionalVariable, Rule, Term
|
||||||
)
|
)
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import cached_property, lru_cache, reduce
|
from functools import cached_property, lru_cache, reduce
|
||||||
|
|
@ -13,7 +13,7 @@ from itertools import (
|
||||||
chain, combinations_with_replacement,
|
chain, combinations_with_replacement,
|
||||||
permutations, product
|
permutations, product
|
||||||
)
|
)
|
||||||
from typing import Dict, List, Optional, Set, Tuple
|
from typing import Any, Dict, Generator, List, Optional, Set, Tuple
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['ModelValue', 'ModelFunction', 'Model', 'Interpretation']
|
__all__ = ['ModelValue', 'ModelFunction', 'Model', 'Interpretation']
|
||||||
|
|
@ -129,6 +129,12 @@ class OrderTable:
|
||||||
|
|
||||||
candidates = X.intersection(Y)
|
candidates = X.intersection(Y)
|
||||||
|
|
||||||
|
if not candidates:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if len(candidates) == 1:
|
||||||
|
return next(iter(candidates))
|
||||||
|
|
||||||
# Grab all elements greater than each of the candidates
|
# Grab all elements greater than each of the candidates
|
||||||
candidate_ge_maps = (self.ge_map[candidate] for candidate in candidates)
|
candidate_ge_maps = (self.ge_map[candidate] for candidate in candidates)
|
||||||
common_ge_values = reduce(set.intersection, candidate_ge_maps)
|
common_ge_values = reduce(set.intersection, candidate_ge_maps)
|
||||||
|
|
@ -147,6 +153,12 @@ class OrderTable:
|
||||||
|
|
||||||
candidates = X.intersection(Y)
|
candidates = X.intersection(Y)
|
||||||
|
|
||||||
|
if not candidates:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if len(candidates) == 1:
|
||||||
|
return next(iter(candidates))
|
||||||
|
|
||||||
# Grab all elements smaller than each of the candidates
|
# Grab all elements smaller than each of the candidates
|
||||||
candidate_le_maps = (self.le_map[candidate] for candidate in candidates)
|
candidate_le_maps = (self.le_map[candidate] for candidate in candidates)
|
||||||
common_le_values = reduce(set.intersection, candidate_le_maps)
|
common_le_values = reduce(set.intersection, candidate_le_maps)
|
||||||
|
|
@ -209,7 +221,7 @@ class Model:
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
result = ("=" * 25) + f"""
|
result = ("=" * 25) + f"""
|
||||||
Model Name: {self.name}
|
Matrix Name: {self.name}
|
||||||
Carrier Set: {set_to_str(self.carrier_set)}
|
Carrier Set: {set_to_str(self.carrier_set)}
|
||||||
Designated Values: {set_to_str(self.designated_values)}
|
Designated Values: {set_to_str(self.designated_values)}
|
||||||
"""
|
"""
|
||||||
|
|
@ -243,7 +255,7 @@ def evaluate_term(
|
||||||
|
|
||||||
def all_model_valuations(
|
def all_model_valuations(
|
||||||
pvars: Tuple[PropositionalVariable],
|
pvars: Tuple[PropositionalVariable],
|
||||||
mvalues: Tuple[ModelValue]):
|
mvalues: Tuple[ModelValue]) -> Generator[Dict[PropositionalVariable, ModelValue], Any, None]:
|
||||||
"""
|
"""
|
||||||
Given propositional variables and model values,
|
Given propositional variables and model values,
|
||||||
produce every possible mapping between the two.
|
produce every possible mapping between the two.
|
||||||
|
|
@ -265,67 +277,50 @@ def all_model_valuations_cached(
|
||||||
return list(all_model_valuations(pvars, mvalues))
|
return list(all_model_valuations(pvars, mvalues))
|
||||||
|
|
||||||
|
|
||||||
|
def rule_satisfied(
|
||||||
|
rule: Rule, valuations: List[Dict[PropositionalVariable, ModelValue]],
|
||||||
|
interpretation: Dict[Operation, ModelFunction], designated_values: Set[ModelValue]) -> bool:
|
||||||
|
"""
|
||||||
|
Checks whether a rule holds under all valuations.
|
||||||
|
|
||||||
|
If there is a mapping where the premise holds but the consequent does
|
||||||
|
not then this returns False.
|
||||||
|
"""
|
||||||
|
for valuation in valuations:
|
||||||
|
premise_met = True
|
||||||
|
for premise in rule.premises:
|
||||||
|
premise_t = evaluate_term(premise, valuation, interpretation)
|
||||||
|
if premise_t not in designated_values:
|
||||||
|
premise_met = False
|
||||||
|
break
|
||||||
|
|
||||||
|
# If any of the premises doesn't hold, then this won't serve as a counterexample
|
||||||
|
if not premise_met:
|
||||||
|
continue
|
||||||
|
|
||||||
|
consequent_t = evaluate_term(rule.conclusion, valuation, interpretation)
|
||||||
|
if consequent_t not in designated_values:
|
||||||
|
# Counterexample found, return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
# No valuation found which contradicts our rule
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def satisfiable(logic: Logic, model: Model, interpretation: Dict[Operation, ModelFunction]) -> bool:
|
def satisfiable(logic: Logic, model: Model, interpretation: Dict[Operation, ModelFunction]) -> bool:
|
||||||
"""
|
"""
|
||||||
Determine whether a model satisfies a logic
|
Determine whether a model satisfies a logic
|
||||||
given an interpretation.
|
given an interpretation.
|
||||||
"""
|
"""
|
||||||
pvars = tuple(get_propostional_variables(tuple(logic.rules)))
|
pvars = tuple(get_propostional_variables(tuple(logic.rules)))
|
||||||
mappings = all_model_valuations_cached(pvars, tuple(model.carrier_set))
|
valuations = all_model_valuations_cached(pvars, tuple(model.carrier_set))
|
||||||
|
|
||||||
for rule in logic.rules:
|
for rule in logic.rules:
|
||||||
# The rule most hold for all valuations
|
if not rule_satisfied(rule, valuations, interpretation, model.designated_values):
|
||||||
for mapping in mappings:
|
|
||||||
# The check only applies if the premises are designated
|
|
||||||
premise_met = True
|
|
||||||
premise_ts: Set[ModelValue] = set()
|
|
||||||
|
|
||||||
for premise in rule.premises:
|
|
||||||
premise_t = evaluate_term(premise, mapping, interpretation)
|
|
||||||
# As soon as one premise is not designated,
|
|
||||||
# move to the next rule.
|
|
||||||
if premise_t not in model.designated_values:
|
|
||||||
premise_met = False
|
|
||||||
break
|
|
||||||
# If designated, keep track of the evaluated term
|
|
||||||
premise_ts.add(premise_t)
|
|
||||||
|
|
||||||
if not premise_met:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# With the premises designated, make sure the consequent is designated
|
|
||||||
consequent_t = evaluate_term(rule.conclusion, mapping, interpretation)
|
|
||||||
if consequent_t not in model.designated_values:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for rule in logic.falsifies:
|
for rule in logic.falsifies:
|
||||||
# We must find one mapping where this does not hold
|
if rule_satisfied(rule, valuations, interpretation, model.designated_values):
|
||||||
counterexample_found = False
|
|
||||||
for mapping in mappings:
|
|
||||||
# The check only applies if the premises are designated
|
|
||||||
premise_met = True
|
|
||||||
premise_ts: Set[ModelValue] = set()
|
|
||||||
|
|
||||||
for premise in rule.premises:
|
|
||||||
premise_t = evaluate_term(premise, mapping, interpretation)
|
|
||||||
# As soon as one premise is not designated,
|
|
||||||
# move to the next rule.
|
|
||||||
if premise_t not in model.designated_values:
|
|
||||||
premise_met = False
|
|
||||||
break
|
|
||||||
# If designated, keep track of the evaluated term
|
|
||||||
premise_ts.add(premise_t)
|
|
||||||
|
|
||||||
if not premise_met:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# With the premises designated, make sure the consequent is not designated
|
|
||||||
consequent_t = evaluate_term(rule.conclusion, mapping, interpretation)
|
|
||||||
if consequent_t not in model.designated_values:
|
|
||||||
counterexample_found = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if not counterexample_found:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
140
smt.py
140
smt.py
|
|
@ -1,96 +1,122 @@
|
||||||
|
from functools import lru_cache
|
||||||
from itertools import product
|
from itertools import product
|
||||||
from typing import Dict, Generator, Optional, Tuple
|
from typing import Dict, Generator, Optional, Set, Tuple
|
||||||
|
|
||||||
from logic import Logic, Operation, Rule, PropositionalVariable, Term, OpTerm, get_prop_vars_from_rule
|
from logic import Logic, Operation, Rule, PropositionalVariable, Term, OpTerm, get_prop_vars_from_rule
|
||||||
from model import Model, ModelValue, ModelFunction
|
from model import Model, ModelValue, ModelFunction
|
||||||
|
|
||||||
from z3 import (
|
SMT_LOADED = True
|
||||||
|
try:
|
||||||
|
from z3 import (
|
||||||
And, BoolSort, Context, EnumSort, Function, Implies, Or, sat, Solver, z3
|
And, BoolSort, Context, EnumSort, Function, Implies, Or, sat, Solver, z3
|
||||||
)
|
)
|
||||||
|
except ImportError:
|
||||||
|
SMT_LOADED = False
|
||||||
|
|
||||||
|
def smt_is_loaded() -> bool:
|
||||||
|
global SMT_LOADED
|
||||||
|
return SMT_LOADED
|
||||||
|
|
||||||
def term_to_smt(
|
def term_to_smt(
|
||||||
t: Term,
|
t: Term,
|
||||||
op_mapping: Dict[Operation, z3.FuncDeclRef],
|
op_mapping: Dict[Operation, "z3.FuncDeclRef"],
|
||||||
var_mapping: Dict[PropositionalVariable, z3.DatatypeRef]
|
var_mapping: Dict[PropositionalVariable, "z3.DatatypeRef"]
|
||||||
) -> z3.DatatypeRef:
|
) -> "z3.DatatypeRef":
|
||||||
"""Convert a logic term to its SMT representation."""
|
"""Convert a logic term to its SMT representation."""
|
||||||
if isinstance(t, PropositionalVariable):
|
if isinstance(t, PropositionalVariable):
|
||||||
return var_mapping[t]
|
return var_mapping[t]
|
||||||
|
|
||||||
assert isinstance(t, OpTerm)
|
assert isinstance(t, OpTerm)
|
||||||
|
|
||||||
|
# Recursively convert all arguments to SMT
|
||||||
arguments = [term_to_smt(arg, op_mapping, var_mapping) for arg in t.arguments]
|
arguments = [term_to_smt(arg, op_mapping, var_mapping) for arg in t.arguments]
|
||||||
fn = op_mapping[t.operation]
|
fn = op_mapping[t.operation]
|
||||||
|
|
||||||
return fn(*arguments)
|
return fn(*arguments)
|
||||||
|
|
||||||
|
def all_smt_valuations(pvars: Tuple[PropositionalVariable], smtvalues):
|
||||||
|
"""
|
||||||
|
Generator which maps all the propositional variable to
|
||||||
|
smt variables representing the carrier set.
|
||||||
|
|
||||||
|
Exhaust the generator to get all such mappings.
|
||||||
|
"""
|
||||||
|
all_possible_values = product(smtvalues, repeat=len(pvars))
|
||||||
|
for valuation in all_possible_values:
|
||||||
|
mapping = dict()
|
||||||
|
assert len(pvars) == len(valuation)
|
||||||
|
for pvar, value in zip(pvars, valuation):
|
||||||
|
mapping[pvar] = value
|
||||||
|
yield mapping
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache
|
||||||
|
def all_smt_valuations_cached(pvars: Tuple[PropositionalVariable], smtvalues):
|
||||||
|
return list(all_smt_valuations(pvars, smtvalues))
|
||||||
|
|
||||||
def logic_rule_to_smt_constraints(
|
def logic_rule_to_smt_constraints(
|
||||||
rule: Rule,
|
rule: Rule,
|
||||||
IsDesignated: z3.FuncDeclRef,
|
IsDesignated: "z3.FuncDeclRef",
|
||||||
smt_carrier_set,
|
smt_carrier_set,
|
||||||
op_mapping: Dict[Operation, z3.FuncDeclRef]
|
op_mapping: Dict[Operation, "z3.FuncDeclRef"]
|
||||||
) -> Generator[z3.BoolRef, None, None]:
|
) -> Generator["z3.BoolRef", None, None]:
|
||||||
"""
|
"""
|
||||||
Encode a logic rule as SMT constraints.
|
Encode a logic rule as SMT constraints.
|
||||||
|
|
||||||
For all valuations: if premises are designated, then conclusion is designated.
|
For all valuations: if premises are designated, then conclusion is designated.
|
||||||
"""
|
"""
|
||||||
prop_vars = get_prop_vars_from_rule(rule)
|
prop_vars = tuple(get_prop_vars_from_rule(rule))
|
||||||
|
valuations = all_smt_valuations_cached(prop_vars, tuple(smt_carrier_set))
|
||||||
# Requires that the rule holds under all valuations
|
|
||||||
for smt_vars in product(smt_carrier_set, repeat=len(prop_vars)):
|
|
||||||
assert len(prop_vars) == len(smt_vars)
|
|
||||||
|
|
||||||
var_mapping = {
|
|
||||||
prop_var: smt_var
|
|
||||||
for (prop_var, smt_var) in zip(prop_vars, smt_vars)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for valuation in valuations:
|
||||||
premises = [
|
premises = [
|
||||||
IsDesignated(term_to_smt(premise, op_mapping, var_mapping)) == True
|
IsDesignated(term_to_smt(premise, op_mapping, valuation)) == True
|
||||||
for premise in rule.premises
|
for premise in rule.premises
|
||||||
]
|
]
|
||||||
conclusion = IsDesignated(term_to_smt(rule.conclusion, op_mapping, var_mapping)) == True
|
conclusion = IsDesignated(term_to_smt(rule.conclusion, op_mapping, valuation)) == True
|
||||||
|
|
||||||
if len(premises) == 0:
|
if len(premises) == 0:
|
||||||
|
# If there are no premises, then the conclusion must always be designated
|
||||||
yield conclusion
|
yield conclusion
|
||||||
else:
|
else:
|
||||||
|
# Otherwise, combine all the premises with and
|
||||||
|
# and have that if the premises are designated
|
||||||
|
# then the conclusion is designated
|
||||||
premise = premises[0]
|
premise = premises[0]
|
||||||
for p in premises[1:]:
|
for p in premises[1:]:
|
||||||
premise = And(premise, p)
|
premise = And(premise, p)
|
||||||
|
|
||||||
yield Implies(premise, conclusion)
|
yield Implies(premise, conclusion)
|
||||||
|
|
||||||
|
|
||||||
def logic_falsification_rule_to_smt_constraints(
|
def logic_falsification_rule_to_smt_constraints(
|
||||||
rule: Rule,
|
rule: Rule,
|
||||||
IsDesignated: z3.FuncDeclRef,
|
IsDesignated: "z3.FuncDeclRef",
|
||||||
smt_carrier_set,
|
smt_carrier_set,
|
||||||
op_mapping: Dict[Operation, z3.FuncDeclRef]
|
op_mapping: Dict[Operation, "z3.FuncDeclRef"]
|
||||||
) -> z3.BoolRef:
|
) -> "z3.BoolRef":
|
||||||
"""
|
"""
|
||||||
Encode a falsification rule as an SMT constraint.
|
Encode a falsification rule as an SMT constraint.
|
||||||
|
|
||||||
There exists at least one valuation where premises are designated
|
There exists at least one valuation where premises are designated
|
||||||
but conclusion is not designated.
|
but conclusion is not designated.
|
||||||
"""
|
"""
|
||||||
prop_vars = get_prop_vars_from_rule(rule)
|
prop_vars = tuple(get_prop_vars_from_rule(rule))
|
||||||
|
valuations = all_smt_valuations_cached(prop_vars, tuple(smt_carrier_set))
|
||||||
|
|
||||||
# Collect all possible counter-examples (valuations that falsify the rule)
|
# Collect all possible counter-examples (valuations that falsify the rule)
|
||||||
counter_examples = []
|
counter_examples = []
|
||||||
|
|
||||||
for smt_vars in product(smt_carrier_set, repeat=len(prop_vars)):
|
for valuation in valuations:
|
||||||
assert len(prop_vars) == len(smt_vars)
|
# The rule is falsified when all of our premises
|
||||||
|
# are designated but our conclusion is not designated
|
||||||
var_mapping = {
|
|
||||||
prop_var: smt_var
|
|
||||||
for (prop_var, smt_var) in zip(prop_vars, smt_vars)
|
|
||||||
}
|
|
||||||
|
|
||||||
premises = [
|
premises = [
|
||||||
IsDesignated(term_to_smt(premise, op_mapping, var_mapping)) == True
|
IsDesignated(term_to_smt(premise, op_mapping, valuation)) == True
|
||||||
for premise in rule.premises
|
for premise in rule.premises
|
||||||
]
|
]
|
||||||
conclusion = IsDesignated(term_to_smt(rule.conclusion, op_mapping, var_mapping)) == False
|
|
||||||
|
conclusion = IsDesignated(term_to_smt(rule.conclusion, op_mapping, valuation)) == False
|
||||||
|
|
||||||
if len(premises) == 0:
|
if len(premises) == 0:
|
||||||
counter_examples.append(conclusion)
|
counter_examples.append(conclusion)
|
||||||
|
|
@ -132,7 +158,7 @@ class SMTLogicEncoder:
|
||||||
self.carrier_sort, self.smt_carrier_set = EnumSort("C", element_names, ctx=self.ctx)
|
self.carrier_sort, self.smt_carrier_set = EnumSort("C", element_names, ctx=self.ctx)
|
||||||
|
|
||||||
# Create operation functions
|
# Create operation functions
|
||||||
self.operation_function_map: Dict[Operation, z3.FuncDeclRef] = {}
|
self.operation_function_map: Dict[Operation, "z3.FuncDeclRef"] = {}
|
||||||
for operation in logic.operations:
|
for operation in logic.operations:
|
||||||
self.operation_function_map[operation] = self.create_function(operation.symbol, operation.arity)
|
self.operation_function_map[operation] = self.create_function(operation.symbol, operation.arity)
|
||||||
|
|
||||||
|
|
@ -143,10 +169,10 @@ class SMTLogicEncoder:
|
||||||
self._add_logic_constraints()
|
self._add_logic_constraints()
|
||||||
self._add_designation_symmetry_constraints()
|
self._add_designation_symmetry_constraints()
|
||||||
|
|
||||||
def create_predicate(self, name: str, arity: int) -> z3.FuncDeclRef:
|
def create_predicate(self, name: str, arity: int) -> "z3.FuncDeclRef":
|
||||||
return Function(name, *(self.carrier_sort for _ in range(arity)), BoolSort(ctx=self.ctx))
|
return Function(name, *(self.carrier_sort for _ in range(arity)), BoolSort(ctx=self.ctx))
|
||||||
|
|
||||||
def create_function(self, name: str, arity: int) -> z3.FuncDeclRef:
|
def create_function(self, name: str, arity: int) -> "z3.FuncDeclRef":
|
||||||
return Function(name, *(self.carrier_sort for _ in range(arity + 1)))
|
return Function(name, *(self.carrier_sort for _ in range(arity + 1)))
|
||||||
|
|
||||||
def _add_logic_constraints(self):
|
def _add_logic_constraints(self):
|
||||||
|
|
@ -171,9 +197,9 @@ class SMTLogicEncoder:
|
||||||
)
|
)
|
||||||
self.solver.add(constraint)
|
self.solver.add(constraint)
|
||||||
|
|
||||||
def extract_model(self, smt_model) -> Model:
|
def extract_model(self, smt_model) -> Tuple[Model, Dict[Operation, ModelFunction]]:
|
||||||
"""
|
"""
|
||||||
Extract a Model object from an SMT model.
|
Extract a Model object and interpretation from an SMT model.
|
||||||
"""
|
"""
|
||||||
carrier_set = {ModelValue(f"{i}") for i in range(self.size)}
|
carrier_set = {ModelValue(f"{i}") for i in range(self.size)}
|
||||||
|
|
||||||
|
|
@ -185,7 +211,8 @@ class SMTLogicEncoder:
|
||||||
designated_values = {ModelValue(str(x)) for x in smt_designated}
|
designated_values = {ModelValue(str(x)) for x in smt_designated}
|
||||||
|
|
||||||
# Extract operation functions
|
# Extract operation functions
|
||||||
model_functions = set()
|
model_functions: Set[ModelFunction] = set()
|
||||||
|
interpretation: Dict[Operation, ModelFunction] = dict()
|
||||||
for (operation, smt_function) in self.operation_function_map.items():
|
for (operation, smt_function) in self.operation_function_map.items():
|
||||||
mapping: Dict[Tuple[ModelValue], ModelValue] = {}
|
mapping: Dict[Tuple[ModelValue], ModelValue] = {}
|
||||||
for smt_inputs in product(self.smt_carrier_set, repeat=operation.arity):
|
for smt_inputs in product(self.smt_carrier_set, repeat=operation.arity):
|
||||||
|
|
@ -193,9 +220,12 @@ class SMTLogicEncoder:
|
||||||
smt_output = smt_model.evaluate(smt_function(*smt_inputs))
|
smt_output = smt_model.evaluate(smt_function(*smt_inputs))
|
||||||
model_output = ModelValue(str(smt_output))
|
model_output = ModelValue(str(smt_output))
|
||||||
mapping[model_inputs] = model_output
|
mapping[model_inputs] = model_output
|
||||||
model_functions.add(ModelFunction(operation.arity, mapping, operation.symbol))
|
model_function = ModelFunction(operation.arity, mapping, operation.symbol)
|
||||||
|
model_functions.add(model_function)
|
||||||
|
interpretation[operation] = model_function
|
||||||
|
|
||||||
return Model(carrier_set, model_functions, designated_values)
|
|
||||||
|
return Model(carrier_set, model_functions, designated_values), interpretation
|
||||||
|
|
||||||
|
|
||||||
def _add_designation_symmetry_constraints(self):
|
def _add_designation_symmetry_constraints(self):
|
||||||
|
|
@ -218,7 +248,7 @@ class SMTLogicEncoder:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_exclusion_constraint(self, model: Model) -> z3.BoolRef:
|
def create_exclusion_constraint(self, model: Model) -> "z3.BoolRef":
|
||||||
"""
|
"""
|
||||||
Create a constraint that excludes the given model from future solutions.
|
Create a constraint that excludes the given model from future solutions.
|
||||||
"""
|
"""
|
||||||
|
|
@ -230,7 +260,7 @@ class SMTLogicEncoder:
|
||||||
for smt_elem in self.smt_carrier_set
|
for smt_elem in self.smt_carrier_set
|
||||||
}
|
}
|
||||||
|
|
||||||
# Exclude operation function mappings
|
# Iterate over all logical operations
|
||||||
for model_func in model.logical_operations:
|
for model_func in model.logical_operations:
|
||||||
operation = Operation(model_func.operation_name, model_func.arity)
|
operation = Operation(model_func.operation_name, model_func.arity)
|
||||||
smt_func = self.operation_function_map[operation]
|
smt_func = self.operation_function_map[operation]
|
||||||
|
|
@ -239,10 +269,9 @@ class SMTLogicEncoder:
|
||||||
smt_inputs = tuple(model_value_to_smt[inp] for inp in inputs)
|
smt_inputs = tuple(model_value_to_smt[inp] for inp in inputs)
|
||||||
smt_output = model_value_to_smt[output]
|
smt_output = model_value_to_smt[output]
|
||||||
|
|
||||||
# For future models the input->output mapping may differ
|
# It may be the case that the output of f(input) differs
|
||||||
constraints.append(smt_func(*smt_inputs) != smt_output)
|
constraints.append(smt_func(*smt_inputs) != smt_output)
|
||||||
|
|
||||||
# Exclude designated value set
|
|
||||||
for smt_elem in self.smt_carrier_set:
|
for smt_elem in self.smt_carrier_set:
|
||||||
model_val = ModelValue(str(smt_elem))
|
model_val = ModelValue(str(smt_elem))
|
||||||
is_designated_in_model = model_val in model.designated_values
|
is_designated_in_model = model_val in model.designated_values
|
||||||
|
|
@ -255,7 +284,7 @@ class SMTLogicEncoder:
|
||||||
|
|
||||||
return Or(constraints)
|
return Or(constraints)
|
||||||
|
|
||||||
def find_model(self) -> Optional[Model]:
|
def find_model(self) -> Optional[Tuple[Model, Dict[Operation, ModelFunction]]]:
|
||||||
"""
|
"""
|
||||||
Find a single model satisfying the logic constraints.
|
Find a single model satisfying the logic constraints.
|
||||||
|
|
||||||
|
|
@ -275,12 +304,12 @@ class SMTLogicEncoder:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def find_model(logic: Logic, size: int) -> Optional[Model]:
|
def find_model(logic: Logic, size: int) -> Optional[Tuple[Model, Dict[Operation, ModelFunction]]]:
|
||||||
"""Find a single model for the given logic and size."""
|
"""Find a single model for the given logic and size."""
|
||||||
encoder = SMTLogicEncoder(logic, size)
|
encoder = SMTLogicEncoder(logic, size)
|
||||||
return encoder.find_model()
|
return encoder.find_model()
|
||||||
|
|
||||||
def find_all_models(logic: Logic, size: int) -> Generator[Model, None, None]:
|
def find_all_models(logic: Logic, size: int) -> Generator[Tuple[Model, Dict[Operation, ModelFunction]], None, None]:
|
||||||
"""
|
"""
|
||||||
Find all models for the given logic and size.
|
Find all models for the given logic and size.
|
||||||
|
|
||||||
|
|
@ -295,13 +324,14 @@ def find_all_models(logic: Logic, size: int) -> Generator[Model, None, None]:
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
# Try to find a model
|
# Try to find a model
|
||||||
model = encoder.find_model()
|
solution = encoder.find_model()
|
||||||
if model is None:
|
if solution is None:
|
||||||
break
|
break
|
||||||
|
|
||||||
yield model
|
yield solution
|
||||||
|
|
||||||
# Add constraint to exclude this model from future solutions
|
# Add constraint to exclude this model from future solutions
|
||||||
|
model, _ = solution
|
||||||
exclusion_constraint = encoder.create_exclusion_constraint(model)
|
exclusion_constraint = encoder.create_exclusion_constraint(model)
|
||||||
encoder.solver.add(exclusion_constraint)
|
encoder.solver.add(exclusion_constraint)
|
||||||
|
|
||||||
|
|
@ -347,13 +377,13 @@ class SMTModelEncoder:
|
||||||
is_designated = model_value in model.designated_values
|
is_designated = model_value in model.designated_values
|
||||||
self.solver.add(self.is_designated(self.model_value_to_smt[model_value]) == is_designated)
|
self.solver.add(self.is_designated(self.model_value_to_smt[model_value]) == is_designated)
|
||||||
|
|
||||||
def create_predicate(self, name: str, arity: int) -> z3.FuncDeclRef:
|
def create_predicate(self, name: str, arity: int) -> "z3.FuncDeclRef":
|
||||||
return Function(name, *(self.carrier_sort for _ in range(arity)), BoolSort(ctx=self.ctx))
|
return Function(name, *(self.carrier_sort for _ in range(arity)), BoolSort(ctx=self.ctx))
|
||||||
|
|
||||||
def create_function(self, name: str, arity: int) -> z3.FuncDeclRef:
|
def create_function(self, name: str, arity: int) -> "z3.FuncDeclRef":
|
||||||
return Function(name, *(self.carrier_sort for _ in range(arity + 1)))
|
return Function(name, *(self.carrier_sort for _ in range(arity + 1)))
|
||||||
|
|
||||||
def add_function_constraints_from_table(self, smt_fn: z3.FuncDeclRef, model_fn: ModelFunction):
|
def add_function_constraints_from_table(self, smt_fn: "z3.FuncDeclRef", model_fn: ModelFunction):
|
||||||
for inputs, output in model_fn.mapping.items():
|
for inputs, output in model_fn.mapping.items():
|
||||||
smt_inputs = tuple(self.model_value_to_smt[inp] for inp in inputs)
|
smt_inputs = tuple(self.model_value_to_smt[inp] for inp in inputs)
|
||||||
smt_output = self.model_value_to_smt[output]
|
smt_output = self.model_value_to_smt[output]
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,9 @@ if __name__ == "__main__":
|
||||||
extra_models1 = 0
|
extra_models1 = 0
|
||||||
extra_models2 = 0
|
extra_models2 = 0
|
||||||
|
|
||||||
for model, impfunction, negation_defined in solutions1:
|
for model, impfunction, negation_defined, conjunction_disjunction_defined in solutions1:
|
||||||
total_models1 += 1
|
total_models1 += 1
|
||||||
vsp_result = has_vsp(model, impfunction, negation_defined)
|
vsp_result = has_vsp(model, impfunction, negation_defined, conjunction_disjunction_defined)
|
||||||
|
|
||||||
if vsp_result.has_vsp:
|
if vsp_result.has_vsp:
|
||||||
good_models1 += 1
|
good_models1 += 1
|
||||||
|
|
@ -83,9 +83,9 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
|
|
||||||
# Check through the remaining models in the second set
|
# Check through the remaining models in the second set
|
||||||
for model, impfunction, negation_defined in solutions2:
|
for model, impfunction, negation_defined, conjunction_disjunction_defined in solutions2:
|
||||||
total_models2 += 1
|
total_models2 += 1
|
||||||
vsp_result = has_vsp(model, impfunction, negation_defined)
|
vsp_result = has_vsp(model, impfunction, negation_defined, conjunction_disjunction_defined)
|
||||||
|
|
||||||
if not vsp_result.has_vsp:
|
if not vsp_result.has_vsp:
|
||||||
bad_models2 += 1
|
bad_models2 += 1
|
||||||
|
|
|
||||||
34
vsp.py
34
vsp.py
|
|
@ -10,12 +10,12 @@ from model import (
|
||||||
Model, model_closure, ModelFunction, ModelValue
|
Model, model_closure, ModelFunction, ModelValue
|
||||||
)
|
)
|
||||||
|
|
||||||
SMT_LOADED = True
|
from smt import SMTModelEncoder, SMTLogicEncoder, smt_is_loaded
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from z3 import And, Or, Implies, sat
|
from z3 import And, Or, Implies, sat
|
||||||
from smt import SMTModelEncoder, SMTLogicEncoder
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
SMT_LOADED = False
|
pass
|
||||||
|
|
||||||
class VSP_Result:
|
class VSP_Result:
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
@ -29,14 +29,14 @@ class VSP_Result:
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if not self.has_vsp:
|
if not self.has_vsp:
|
||||||
return f"Model {self.model_name} does not have the variable sharing property."
|
return f"Matrix {self.model_name} does not have the variable sharing property."
|
||||||
return f"""Model {self.model_name} has the variable sharing property.
|
return f"""Matrix {self.model_name} has the variable sharing property.
|
||||||
Subalgebra 1: {set_to_str(self.subalgebra1)}
|
Subalgebra 1: {set_to_str(self.subalgebra1)}
|
||||||
Subalgebra 2: {set_to_str(self.subalgebra2)}
|
Subalgebra 2: {set_to_str(self.subalgebra2)}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def has_vsp_magical(model: Model, impfunction: ModelFunction,
|
def has_vsp_magical(model: Model, impfunction: ModelFunction,
|
||||||
negation_defined: bool) -> VSP_Result:
|
negation_defined: bool, conjunction_disjunction_defined: bool) -> VSP_Result:
|
||||||
"""
|
"""
|
||||||
Checks whether a MaGIC model has the variable
|
Checks whether a MaGIC model has the variable
|
||||||
sharing property.
|
sharing property.
|
||||||
|
|
@ -46,6 +46,10 @@ def has_vsp_magical(model: Model, impfunction: ModelFunction,
|
||||||
if len(model.designated_values) == 1:
|
if len(model.designated_values) == 1:
|
||||||
return VSP_Result(False, model.name)
|
return VSP_Result(False, model.name)
|
||||||
|
|
||||||
|
if len(model.carrier_set) in [2,3,4,5,7] \
|
||||||
|
and conjunction_disjunction_defined and negation_defined:
|
||||||
|
return VSP_Result(False, model.name)
|
||||||
|
|
||||||
assert model.ordering is not None, "Expected ordering table in model"
|
assert model.ordering is not None, "Expected ordering table in model"
|
||||||
|
|
||||||
top = model.ordering.top()
|
top = model.ordering.top()
|
||||||
|
|
@ -135,7 +139,7 @@ def has_vsp_smt(model: Model, impfn: ModelFunction) -> VSP_Result:
|
||||||
Checks whether a given model satisfies the variable
|
Checks whether a given model satisfies the variable
|
||||||
sharing property via SMT
|
sharing property via SMT
|
||||||
"""
|
"""
|
||||||
if not SMT_LOADED:
|
if not smt_is_loaded():
|
||||||
raise Exception("Z3 is not property installed, cannot check via SMT")
|
raise Exception("Z3 is not property installed, cannot check via SMT")
|
||||||
|
|
||||||
encoder = SMTModelEncoder(model)
|
encoder = SMTModelEncoder(model)
|
||||||
|
|
@ -164,7 +168,7 @@ def has_vsp_smt(model: Model, impfn: ModelFunction) -> VSP_Result:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# x -> y is non-designated
|
# x -> y is non-designated for any x in K1 and y in K2
|
||||||
smt_imp = encoder.model_function_map[impfn]
|
smt_imp = encoder.model_function_map[impfn]
|
||||||
for (x, y) in product(encoder.smt_carrier_set, encoder.smt_carrier_set):
|
for (x, y) in product(encoder.smt_carrier_set, encoder.smt_carrier_set):
|
||||||
encoder.solver.add(
|
encoder.solver.add(
|
||||||
|
|
@ -190,11 +194,11 @@ def has_vsp_smt(model: Model, impfn: ModelFunction) -> VSP_Result:
|
||||||
|
|
||||||
|
|
||||||
def has_vsp(model: Model, impfunction: ModelFunction,
|
def has_vsp(model: Model, impfunction: ModelFunction,
|
||||||
negation_defined: bool) -> VSP_Result:
|
negation_defined: bool, conjunction_disjunction_defined: bool) -> VSP_Result:
|
||||||
if model.is_magical:
|
if model.is_magical:
|
||||||
return has_vsp_magical(model, impfunction, negation_defined)
|
return has_vsp_magical(model, impfunction, negation_defined, conjunction_disjunction_defined)
|
||||||
|
|
||||||
return has_vsp_smt(model)
|
return has_vsp_smt(model, impfunction)
|
||||||
|
|
||||||
|
|
||||||
def logic_has_vsp(logic: Logic, size: int) -> Optional[Tuple[Model, VSP_Result]]:
|
def logic_has_vsp(logic: Logic, size: int) -> Optional[Tuple[Model, VSP_Result]]:
|
||||||
|
|
@ -240,7 +244,7 @@ def logic_has_vsp(logic: Logic, size: int) -> Optional[Tuple[Model, VSP_Result]]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# x -> y is non-designated
|
# x -> y is non-designated for any x in k1 and y in k2
|
||||||
Impfn = encoder.operation_function_map[Implication]
|
Impfn = encoder.operation_function_map[Implication]
|
||||||
for (x, y) in product(encoder.smt_carrier_set, encoder.smt_carrier_set):
|
for (x, y) in product(encoder.smt_carrier_set, encoder.smt_carrier_set):
|
||||||
encoder.solver.add(
|
encoder.solver.add(
|
||||||
|
|
@ -250,15 +254,15 @@ def logic_has_vsp(logic: Logic, size: int) -> Optional[Tuple[Model, VSP_Result]]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
model = encoder.find_model()
|
solution = encoder.find_model()
|
||||||
|
|
||||||
# We failed to find a VSP witness
|
# We failed to find a VSP witness
|
||||||
if model is None:
|
if solution is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Otherwise, a matrix model and correspoding
|
# Otherwise, a matrix model and correspoding
|
||||||
# subalgebras exist.
|
# subalgebras exist.
|
||||||
|
model, _ = solution
|
||||||
smt_model = encoder.solver.model()
|
smt_model = encoder.solver.model()
|
||||||
K1_smt = [x for x in encoder.smt_carrier_set if smt_model.evaluate(IsInK1(x))]
|
K1_smt = [x for x in encoder.smt_carrier_set if smt_model.evaluate(IsInK1(x))]
|
||||||
K1 = {ModelValue(str(x)) for x in K1_smt}
|
K1 = {ModelValue(str(x)) for x in K1_smt}
|
||||||
|
|
|
||||||
21
vspursuer.py
21
vspursuer.py
|
|
@ -6,7 +6,7 @@ from queue import Empty as QueueEmpty
|
||||||
import argparse
|
import argparse
|
||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
|
|
||||||
from logic import Negation, Implication, Operation
|
from logic import Conjunction, Disjunction, Implication, Negation, Operation
|
||||||
from model import Model, ModelFunction
|
from model import Model, ModelFunction
|
||||||
from parse_magic import SourceFile, parse_matrices
|
from parse_magic import SourceFile, parse_matrices
|
||||||
from vsp import has_vsp, VSP_Result
|
from vsp import has_vsp, VSP_Result
|
||||||
|
|
@ -36,13 +36,14 @@ def restructure_solutions(solutions: Iterator[Tuple[Model, Dict[Operation, Model
|
||||||
# NOTE: Implication must be defined, the rest may not
|
# NOTE: Implication must be defined, the rest may not
|
||||||
impfunction = interpretation[Implication]
|
impfunction = interpretation[Implication]
|
||||||
negation_defined = Negation in interpretation
|
negation_defined = Negation in interpretation
|
||||||
yield (model, impfunction, negation_defined)
|
conjunction_disjunction_defined = (Conjunction in interpretation) and (Disjunction in interpretation)
|
||||||
|
yield (model, impfunction, negation_defined, conjunction_disjunction_defined)
|
||||||
|
|
||||||
def has_vsp_plus_model(model, impfunction, negation_defined) -> Tuple[Optional[Model], VSP_Result]:
|
def has_vsp_plus_model(model, impfunction, negation_defined, conjunction_disjunction_defined) -> Tuple[Optional[Model], VSP_Result]:
|
||||||
"""
|
"""
|
||||||
Wrapper which also stores the models along with its vsp result
|
Wrapper which also stores the models along with its vsp result
|
||||||
"""
|
"""
|
||||||
vsp_result = has_vsp(model, impfunction, negation_defined)
|
vsp_result = has_vsp(model, impfunction, negation_defined, conjunction_disjunction_defined)
|
||||||
# NOTE: Memory optimization - Don't return model if it doesn't have VSP
|
# NOTE: Memory optimization - Don't return model if it doesn't have VSP
|
||||||
model = model if vsp_result.has_vsp else None
|
model = model if vsp_result.has_vsp else None
|
||||||
return (model, vsp_result)
|
return (model, vsp_result)
|
||||||
|
|
@ -61,8 +62,8 @@ def worker_vsp(task_queue: mp.Queue, result_queue: mp.Queue):
|
||||||
# If sentinal value, break
|
# If sentinal value, break
|
||||||
if task is None:
|
if task is None:
|
||||||
break
|
break
|
||||||
(model, impfunction, negation_defined) = task
|
(model, impfunction, negation_defined, conjunction_disjunction_defined) = task
|
||||||
result = has_vsp_plus_model(model, impfunction, negation_defined)
|
result = has_vsp_plus_model(model, impfunction, negation_defined, conjunction_disjunction_defined)
|
||||||
result_queue.put(result)
|
result_queue.put(result)
|
||||||
finally:
|
finally:
|
||||||
# Either an exception occured or the worker finished
|
# Either an exception occured or the worker finished
|
||||||
|
|
@ -168,7 +169,7 @@ def multi_process_runner(num_cpu: int, data_file_path: str, skip_to: Optional[st
|
||||||
if vsp_result.has_vsp:
|
if vsp_result.has_vsp:
|
||||||
num_has_vsp += 1
|
num_has_vsp += 1
|
||||||
|
|
||||||
print_with_timestamp(f"Tested {num_tested} models, {num_has_vsp} of which satisfy VSP")
|
print_with_timestamp(f"Tested {num_tested} matrices, {num_has_vsp} of which satisfy VSP")
|
||||||
|
|
||||||
def single_process_runner(data_file_path: str, skip_to: Optional[str]):
|
def single_process_runner(data_file_path: str, skip_to: Optional[str]):
|
||||||
num_tested = 0
|
num_tested = 0
|
||||||
|
|
@ -178,8 +179,8 @@ def single_process_runner(data_file_path: str, skip_to: Optional[str]):
|
||||||
solutions = parse_matrices(SourceFile(data_file))
|
solutions = parse_matrices(SourceFile(data_file))
|
||||||
solutions = restructure_solutions(solutions, skip_to)
|
solutions = restructure_solutions(solutions, skip_to)
|
||||||
|
|
||||||
for model, impfunction, negation_defined in solutions:
|
for model, impfunction, negation_defined, conjunction_disjunction_defined in solutions:
|
||||||
model, vsp_result = has_vsp_plus_model(model, impfunction, negation_defined)
|
model, vsp_result = has_vsp_plus_model(model, impfunction, negation_defined, conjunction_disjunction_defined)
|
||||||
print_with_timestamp(vsp_result)
|
print_with_timestamp(vsp_result)
|
||||||
num_tested += 1
|
num_tested += 1
|
||||||
|
|
||||||
|
|
@ -189,7 +190,7 @@ def single_process_runner(data_file_path: str, skip_to: Optional[str]):
|
||||||
if vsp_result.has_vsp:
|
if vsp_result.has_vsp:
|
||||||
num_has_vsp += 1
|
num_has_vsp += 1
|
||||||
|
|
||||||
print_with_timestamp(f"Tested {num_tested} models, {num_has_vsp} of which satisfy VSP")
|
print_with_timestamp(f"Tested {num_tested} matrices, {num_has_vsp} of which satisfy VSP")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue