mirror of
https://github.com/Brandon-Rozek/matmod.git
synced 2024-11-08 21:20:33 -05:00
Compare commits
6 commits
2fa8aa9c15
...
43bb036008
Author | SHA1 | Date | |
---|---|---|---|
43bb036008 | |||
667eea0c70 | |||
df5b0f5161 | |||
6bb863da97 | |||
6b4d5828c8 | |||
81a2d17965 |
7 changed files with 346 additions and 194 deletions
67
R.py
67
R.py
|
@ -2,22 +2,24 @@
|
||||||
Modeling the logic R
|
Modeling the logic R
|
||||||
"""
|
"""
|
||||||
from logic import (
|
from logic import (
|
||||||
|
Conjunction,
|
||||||
|
Disjunction,
|
||||||
|
Implication,
|
||||||
|
Logic,
|
||||||
|
Negation,
|
||||||
PropositionalVariable,
|
PropositionalVariable,
|
||||||
Rule,
|
Rule,
|
||||||
Logic,
|
|
||||||
Implication,
|
|
||||||
Conjunction,
|
|
||||||
Negation,
|
|
||||||
Disjunction,
|
|
||||||
Rule,
|
|
||||||
)
|
)
|
||||||
from model import Model, ModelFunction, ModelValue, has_vsp, 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
|
||||||
|
|
||||||
|
|
||||||
# ===================================================
|
# ===================================================
|
||||||
|
|
||||||
# Defining the logic of R
|
"""
|
||||||
|
Defining the Logic of R
|
||||||
|
"""
|
||||||
|
|
||||||
x = PropositionalVariable("x")
|
x = PropositionalVariable("x")
|
||||||
y = PropositionalVariable("y")
|
y = PropositionalVariable("y")
|
||||||
|
@ -59,12 +61,13 @@ logic_rules = implication_rules | negation_rules | conjunction_rules | disjuncti
|
||||||
|
|
||||||
operations = {Negation, Conjunction, Disjunction, Implication}
|
operations = {Negation, Conjunction, Disjunction, Implication}
|
||||||
|
|
||||||
R_logic = Logic(operations, logic_rules)
|
R_logic = Logic(operations, logic_rules, "R")
|
||||||
|
|
||||||
# ===============================
|
# ===============================
|
||||||
|
|
||||||
# Example Model of R
|
"""
|
||||||
|
Example 2-Element Model of R
|
||||||
|
"""
|
||||||
|
|
||||||
a0 = ModelValue("a0")
|
a0 = ModelValue("a0")
|
||||||
a1 = ModelValue("a1")
|
a1 = ModelValue("a1")
|
||||||
|
@ -103,7 +106,7 @@ designated_values = {a1}
|
||||||
logical_operations = {
|
logical_operations = {
|
||||||
mnegation, mimplication, mconjunction, mdisjunction
|
mnegation, mimplication, mconjunction, mdisjunction
|
||||||
}
|
}
|
||||||
R_model_2 = Model(carrier_set, logical_operations, designated_values)
|
R_model_2 = Model(carrier_set, logical_operations, designated_values, "R2")
|
||||||
|
|
||||||
interpretation = {
|
interpretation = {
|
||||||
Negation: mnegation,
|
Negation: mnegation,
|
||||||
|
@ -112,24 +115,36 @@ interpretation = {
|
||||||
Implication: mimplication
|
Implication: mimplication
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print(R_model_2)
|
||||||
|
|
||||||
|
|
||||||
# =================================
|
# =================================
|
||||||
|
|
||||||
# Generate models of R of a given size
|
"""
|
||||||
|
Generate models of R of a specified size
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("*" * 30)
|
||||||
|
|
||||||
model_size = 2
|
model_size = 2
|
||||||
solutions = generate_model(R_logic, model_size, print_model=True)
|
print("Generating models of Logic", R_logic.name, "of size", model_size)
|
||||||
|
solutions = generate_model(R_logic, model_size, print_model=False)
|
||||||
|
|
||||||
print(f"There are {len(solutions)} satisfiable models of element length {model_size}")
|
print(f"Found {len(solutions)} satisfiable models")
|
||||||
|
|
||||||
for model, interpretation in solutions:
|
for model, interpretation in solutions:
|
||||||
print("Has VSP?", has_vsp(model, interpretation))
|
print(has_vsp(model, interpretation))
|
||||||
|
|
||||||
print("-" * 5)
|
print("*" * 30)
|
||||||
|
|
||||||
######
|
######
|
||||||
|
|
||||||
# Smallest model for R that has the variable sharing property
|
"""
|
||||||
|
Showing the smallest model for R that has the
|
||||||
|
variable sharing property.
|
||||||
|
|
||||||
|
This model has 6 elements.
|
||||||
|
"""
|
||||||
|
|
||||||
a0 = ModelValue("a0")
|
a0 = ModelValue("a0")
|
||||||
a1 = ModelValue("a1")
|
a1 = ModelValue("a1")
|
||||||
|
@ -148,7 +163,7 @@ mnegation = ModelFunction(1, {
|
||||||
a2: a2,
|
a2: a2,
|
||||||
a1: a4,
|
a1: a4,
|
||||||
a0: a5
|
a0: a5
|
||||||
})
|
}, "¬")
|
||||||
|
|
||||||
mimplication = ModelFunction(2, {
|
mimplication = ModelFunction(2, {
|
||||||
(a5, a5): a5,
|
(a5, a5): a5,
|
||||||
|
@ -192,7 +207,7 @@ mimplication = ModelFunction(2, {
|
||||||
(a0, a2): a5,
|
(a0, a2): a5,
|
||||||
(a0, a1): a5,
|
(a0, a1): a5,
|
||||||
(a0, a0): a5
|
(a0, a0): a5
|
||||||
})
|
}, "→")
|
||||||
|
|
||||||
|
|
||||||
mconjunction = ModelFunction(2, {
|
mconjunction = ModelFunction(2, {
|
||||||
|
@ -237,7 +252,7 @@ mconjunction = ModelFunction(2, {
|
||||||
(a0, a2): a0,
|
(a0, a2): a0,
|
||||||
(a0, a1): a0,
|
(a0, a1): a0,
|
||||||
(a0, a0): a0
|
(a0, a0): a0
|
||||||
})
|
}, "∧")
|
||||||
|
|
||||||
mdisjunction = ModelFunction(2, {
|
mdisjunction = ModelFunction(2, {
|
||||||
(a5, a5): a5,
|
(a5, a5): a5,
|
||||||
|
@ -281,12 +296,12 @@ mdisjunction = ModelFunction(2, {
|
||||||
(a0, a2): a2,
|
(a0, a2): a2,
|
||||||
(a0, a1): a1,
|
(a0, a1): a1,
|
||||||
(a0, a0): a0
|
(a0, a0): a0
|
||||||
})
|
}, "∨")
|
||||||
|
|
||||||
logical_operations = {
|
logical_operations = {
|
||||||
mnegation, mimplication, mconjunction, mdisjunction
|
mnegation, mimplication, mconjunction, mdisjunction
|
||||||
}
|
}
|
||||||
R_model_6 = Model(carrier_set, logical_operations, designated_values)
|
R_model_6 = Model(carrier_set, logical_operations, designated_values, "R6")
|
||||||
|
|
||||||
interpretation = {
|
interpretation = {
|
||||||
Negation: mnegation,
|
Negation: mnegation,
|
||||||
|
@ -296,7 +311,5 @@ 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("Satisfiable", satisfiable(R_logic, R_model_6, interpretation))
|
print(has_vsp(R_model_6, interpretation))
|
||||||
|
|
||||||
print("Has VSP?", has_vsp(R_model_6, interpretation))
|
|
||||||
|
|
|
@ -4,3 +4,9 @@ This repository is mostly an experiment to help
|
||||||
me better understand matrix models.
|
me better understand matrix models.
|
||||||
|
|
||||||
You're likely better off using [arranstewart/magic](https://github.com/arranstewart/magic).
|
You're likely better off using [arranstewart/magic](https://github.com/arranstewart/magic).
|
||||||
|
|
||||||
|
We support output from magic using the ugly data format.
|
||||||
|
|
||||||
|
```
|
||||||
|
python3 parse_magic.py < UGLY_FILE_FROM_MAGIC
|
||||||
|
```
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
"""
|
"""
|
||||||
File which generates all the models
|
Generate all the models for a given logic
|
||||||
|
with a specified number of elements.
|
||||||
"""
|
"""
|
||||||
from common import set_to_str
|
from common import set_to_str
|
||||||
from logic import Logic, Operation, Rule, get_operations_from_term
|
from logic import Logic, Operation, Rule, get_operations_from_term
|
||||||
from model import ModelValue, Model, satisfiable, ModelFunction, ModelOrderConstraint
|
from model import (
|
||||||
|
Interpretation, ModelValue, Model,
|
||||||
|
ModelFunction, satisfiable
|
||||||
|
)
|
||||||
from itertools import combinations, chain, product
|
from itertools import combinations, chain, product
|
||||||
from typing import Set, List, Dict, Tuple
|
from typing import Set, List, Tuple
|
||||||
|
|
||||||
def possible_designations(iterable):
|
def possible_designations(iterable):
|
||||||
"""Powerset without the empty and complete set"""
|
"""Powerset without the empty and complete set"""
|
||||||
|
@ -13,6 +17,11 @@ def possible_designations(iterable):
|
||||||
return chain.from_iterable(combinations(s, r) for r in range(1, len(s)))
|
return chain.from_iterable(combinations(s, r) for r in range(1, len(s)))
|
||||||
|
|
||||||
def possible_functions(operation, carrier_set):
|
def possible_functions(operation, carrier_set):
|
||||||
|
"""
|
||||||
|
Create every possible input, output pair
|
||||||
|
for a given model function based on an
|
||||||
|
operation and a carrier set.
|
||||||
|
"""
|
||||||
arity = operation.arity
|
arity = operation.arity
|
||||||
|
|
||||||
inputs = list(product(carrier_set, repeat=arity))
|
inputs = list(product(carrier_set, repeat=arity))
|
||||||
|
@ -26,12 +35,19 @@ def possible_functions(operation, carrier_set):
|
||||||
yield ModelFunction(arity, new_function, operation.symbol)
|
yield ModelFunction(arity, new_function, operation.symbol)
|
||||||
|
|
||||||
|
|
||||||
def only_rules_with(rules: Set[Rule], operation: Operation) -> Set[Rule]:
|
def only_rules_with(rules: Set[Rule], operation: Operation) -> List[Rule]:
|
||||||
result_rules = []
|
"""
|
||||||
|
Filter the list of rules in a logic to those
|
||||||
|
that only contain the logical operation specified.
|
||||||
|
"""
|
||||||
|
result_rules: List[Rule] = []
|
||||||
for rule in rules:
|
for rule in rules:
|
||||||
is_valid = True
|
is_valid = True
|
||||||
|
# Go through every term in the premises and conclusion
|
||||||
for t in (rule.premises | {rule.conclusion,}):
|
for t in (rule.premises | {rule.conclusion,}):
|
||||||
t_operations = get_operations_from_term(t)
|
t_operations = get_operations_from_term(t)
|
||||||
|
# Make sure there's only one operation
|
||||||
|
# and that it matches the operation specified
|
||||||
if len(t_operations) > 1:
|
if len(t_operations) > 1:
|
||||||
is_valid = False
|
is_valid = False
|
||||||
break
|
break
|
||||||
|
@ -48,23 +64,32 @@ def only_rules_with(rules: Set[Rule], operation: Operation) -> Set[Rule]:
|
||||||
|
|
||||||
def possible_interpretations(
|
def possible_interpretations(
|
||||||
logic: Logic, carrier_set: Set[ModelValue],
|
logic: Logic, carrier_set: Set[ModelValue],
|
||||||
designated_values: Set[ModelValue], ordering: Set[ModelOrderConstraint]):
|
designated_values: Set[ModelValue]):
|
||||||
operations = []
|
"""
|
||||||
model_functions = []
|
Consider every possible interpretation of operations
|
||||||
|
within the specified logic given the carrier set of
|
||||||
|
model values, and the set of designated values.
|
||||||
|
"""
|
||||||
|
operations: List[Operation] = []
|
||||||
|
model_functions: List[List[ModelFunction]] = []
|
||||||
|
|
||||||
for operation in logic.operations:
|
for operation in logic.operations:
|
||||||
operations.append(operation)
|
operations.append(operation)
|
||||||
candidate_functions = list(possible_functions(operation, carrier_set))
|
candidate_functions = list(possible_functions(operation, carrier_set))
|
||||||
passed_functions = []
|
passed_functions: List[ModelFunction] = []
|
||||||
"""
|
"""
|
||||||
Only consider functions that at least pass
|
Discard candidate functions that don't pass
|
||||||
in the rules with the operation by itself.
|
the rules that only contain the given
|
||||||
|
logical operation.
|
||||||
"""
|
"""
|
||||||
restricted_rules = only_rules_with(logic.rules, operation)
|
restricted_rules = only_rules_with(logic.rules, operation)
|
||||||
if len(restricted_rules) > 0:
|
if len(restricted_rules) > 0:
|
||||||
small_logic = Logic({operation,}, restricted_rules)
|
small_logic = Logic({operation,}, restricted_rules)
|
||||||
|
# Add candidate functions whose small model
|
||||||
|
# and logic are satisfied given the restricted
|
||||||
|
# rule set.
|
||||||
for f in candidate_functions:
|
for f in candidate_functions:
|
||||||
small_model = Model(carrier_set, {f,}, designated_values, ordering)
|
small_model = Model(carrier_set, {f,}, designated_values)
|
||||||
interp = {operation: f}
|
interp = {operation: f}
|
||||||
if satisfiable(small_logic, small_model, interp):
|
if satisfiable(small_logic, small_model, interp):
|
||||||
passed_functions.append(f)
|
passed_functions.append(f)
|
||||||
|
@ -78,45 +103,42 @@ def possible_interpretations(
|
||||||
)
|
)
|
||||||
model_functions.append(passed_functions)
|
model_functions.append(passed_functions)
|
||||||
|
|
||||||
|
# The model_functions variables contains
|
||||||
|
# the candidate functions for each operation.
|
||||||
|
|
||||||
functions_choice = product(*model_functions)
|
functions_choice = product(*model_functions)
|
||||||
|
# Assign a function to each operation
|
||||||
for functions in functions_choice:
|
for functions in functions_choice:
|
||||||
assert len(operations) == len(functions)
|
assert len(operations) == len(functions)
|
||||||
interpretation = dict()
|
interpretation: Interpretation = dict()
|
||||||
for operation, function in zip(operations, functions):
|
for operation, function in zip(operations, functions):
|
||||||
interpretation[operation] = function
|
interpretation[operation] = function
|
||||||
yield interpretation
|
yield interpretation
|
||||||
|
|
||||||
def generate_model(logic: Logic, number_elements: int, num_solutions: int = -1, print_model=False) -> List[Tuple[Model, Dict[Operation, ModelFunction]]]:
|
def generate_model(
|
||||||
|
logic: Logic, number_elements: int, num_solutions: int = -1,
|
||||||
|
print_model=False) -> List[Tuple[Model, Interpretation]]:
|
||||||
|
"""
|
||||||
|
Generate the specified number of models that
|
||||||
|
satisfy a logic of a certain size.
|
||||||
|
"""
|
||||||
assert number_elements > 0
|
assert number_elements > 0
|
||||||
carrier_set = {
|
carrier_set = {
|
||||||
ModelValue("a" + str(i)) for i in range(number_elements)
|
ModelValue("a" + str(i)) for i in range(number_elements)
|
||||||
}
|
}
|
||||||
|
|
||||||
ordering = set()
|
|
||||||
|
|
||||||
# a(0) is less than all other elements
|
|
||||||
a0 = ModelValue("a0")
|
|
||||||
for v in carrier_set:
|
|
||||||
if v != a0:
|
|
||||||
ordering.add(a0 < v)
|
|
||||||
|
|
||||||
# Every other element is less than a(n - 1)
|
|
||||||
an = ModelValue(f"a{number_elements-1}")
|
|
||||||
for v in carrier_set:
|
|
||||||
if an != v:
|
|
||||||
ordering.add(v < an)
|
|
||||||
|
|
||||||
possible_designated_values = possible_designations(carrier_set)
|
possible_designated_values = possible_designations(carrier_set)
|
||||||
|
|
||||||
solutions: List[Tuple[Model, Dict[Operation, ModelFunction]]] = []
|
solutions: List[Tuple[Model, Interpretation]] = []
|
||||||
|
|
||||||
for designated_values in possible_designated_values:
|
for designated_values in possible_designated_values:
|
||||||
designated_values = set(designated_values)
|
designated_values = set(designated_values)
|
||||||
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, ordering)
|
|
||||||
|
|
||||||
|
possible_interps = possible_interpretations(logic, carrier_set, designated_values)
|
||||||
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, ordering)
|
model = Model(carrier_set, set(interpretation.values()), designated_values)
|
||||||
# Iteratively test possible interpretations
|
# Iteratively test possible interpretations
|
||||||
# by adding one axiom at a time
|
# by adding one axiom at a time
|
||||||
for rule in logic.rules:
|
for rule in logic.rules:
|
||||||
|
@ -127,7 +149,6 @@ def generate_model(logic: Logic, number_elements: int, num_solutions: int = -1,
|
||||||
|
|
||||||
if is_valid:
|
if is_valid:
|
||||||
solutions.append((model, interpretation))
|
solutions.append((model, interpretation))
|
||||||
# satisfied_models.append(model)
|
|
||||||
if print_model:
|
if print_model:
|
||||||
print(model, flush=True)
|
print(model, flush=True)
|
||||||
|
|
||||||
|
|
10
logic.py
10
logic.py
|
@ -1,4 +1,4 @@
|
||||||
from typing import Any, Set, Tuple
|
from typing import Optional, Set, Tuple
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
|
||||||
class Operation:
|
class Operation:
|
||||||
|
@ -95,9 +95,15 @@ class Rule:
|
||||||
return str(str_premises2) + "=>" + str(self.conclusion)
|
return str(str_premises2) + "=>" + str(self.conclusion)
|
||||||
|
|
||||||
class Logic:
|
class Logic:
|
||||||
def __init__(self, operations: Set[Operation], rules: Set[Rule]):
|
def __init__(self,
|
||||||
|
operations: Set[Operation], rules: Set[Rule],
|
||||||
|
name: Optional[str] = None):
|
||||||
self.operations = operations
|
self.operations = operations
|
||||||
self.rules = rules
|
self.rules = rules
|
||||||
|
self.name = str(abs(hash((
|
||||||
|
frozenset(operations),
|
||||||
|
frozenset(rules)
|
||||||
|
))))[:5] if name is None else name
|
||||||
|
|
||||||
|
|
||||||
def get_prop_var_from_term(t: Term) -> Set[PropositionalVariable]:
|
def get_prop_var_from_term(t: Term) -> Set[PropositionalVariable]:
|
||||||
|
|
204
model.py
204
model.py
|
@ -1,19 +1,19 @@
|
||||||
"""
|
"""
|
||||||
Defining what it means to be a model
|
Matrix model semantics and satisfiability of
|
||||||
|
a given logic.
|
||||||
"""
|
"""
|
||||||
from common import set_to_str
|
from common import set_to_str
|
||||||
from logic import (
|
from logic import (
|
||||||
PropositionalVariable, get_propostional_variables, Logic, Term,
|
get_propostional_variables, Logic,
|
||||||
Operation, Conjunction, Disjunction, Implication
|
Operation, PropositionalVariable, Term
|
||||||
)
|
)
|
||||||
from typing import Set, Dict, Tuple, Optional
|
from collections import defaultdict
|
||||||
from functools import lru_cache
|
from functools import cached_property, lru_cache, reduce
|
||||||
from itertools import combinations, chain, product, permutations
|
from itertools import chain, combinations_with_replacement, permutations, product
|
||||||
from copy import deepcopy
|
from typing import Dict, List, Optional, Set, Tuple
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['ModelValue', 'ModelFunction', 'Model']
|
__all__ = ['ModelValue', 'ModelFunction', 'Model', 'Interpretation']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ModelValue:
|
class ModelValue:
|
||||||
|
@ -29,20 +29,17 @@ class ModelValue:
|
||||||
return self.hashed_value
|
return self.hashed_value
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return isinstance(other, ModelValue) and self.name == other.name
|
return isinstance(other, ModelValue) and self.name == other.name
|
||||||
def __lt__(self, other):
|
def __deepcopy__(self, _):
|
||||||
assert isinstance(other, ModelValue)
|
|
||||||
return ModelOrderConstraint(self, other)
|
|
||||||
def __deepcopy__(self, memo):
|
|
||||||
return ModelValue(self.name)
|
return ModelValue(self.name)
|
||||||
|
|
||||||
|
|
||||||
class ModelFunction:
|
class ModelFunction:
|
||||||
def __init__(self, arity: int, mapping, operation_name = ""):
|
def __init__(self, arity: int, mapping, operation_name = ""):
|
||||||
self.operation_name = operation_name
|
self.operation_name = operation_name
|
||||||
self.arity = arity
|
self.arity = arity
|
||||||
|
|
||||||
# Correct input to always be a tuple
|
# Transform the mapping such that the
|
||||||
corrected_mapping = dict()
|
# key is always a tuple of model values
|
||||||
|
corrected_mapping: Dict[Tuple[ModelValue], ModelValue] = {}
|
||||||
for k, v in mapping.items():
|
for k, v in mapping.items():
|
||||||
if isinstance(k, tuple):
|
if isinstance(k, tuple):
|
||||||
assert len(k) == arity
|
assert len(k) == arity
|
||||||
|
@ -56,7 +53,21 @@ class ModelFunction:
|
||||||
|
|
||||||
self.mapping = corrected_mapping
|
self.mapping = corrected_mapping
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def domain(self):
|
||||||
|
result_set: Set[ModelValue] = set()
|
||||||
|
for args in self.mapping.keys():
|
||||||
|
for v in args:
|
||||||
|
result_set.add(v)
|
||||||
|
return result_set
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
if self.arity == 1:
|
||||||
|
return unary_function_str(self)
|
||||||
|
elif self.arity == 2:
|
||||||
|
return binary_function_str(self)
|
||||||
|
|
||||||
|
# Default return dictionary representation
|
||||||
str_dict = dict()
|
str_dict = dict()
|
||||||
for k, v in self.mapping.items():
|
for k, v in self.mapping.items():
|
||||||
inputstr = "(" + ", ".join(str(ki) for ki in k) + ")"
|
inputstr = "(" + ", ".join(str(ki) for ki in k) + ")"
|
||||||
|
@ -66,19 +77,34 @@ class ModelFunction:
|
||||||
def __call__(self, *args):
|
def __call__(self, *args):
|
||||||
return self.mapping[args]
|
return self.mapping[args]
|
||||||
|
|
||||||
# def __eq__(self, other):
|
|
||||||
# return isinstance(other, ModelFunction) and self.name == other.name and self.arity == other.arity
|
|
||||||
|
|
||||||
class ModelOrderConstraint:
|
def unary_function_str(f: ModelFunction) -> str:
|
||||||
# a < b
|
assert isinstance(f, ModelFunction) and f.arity == 1
|
||||||
def __init__(self, a: ModelValue, b: ModelValue):
|
sorted_domain = sorted(f.domain, key=lambda v : v.name)
|
||||||
self.a = a
|
header_line = f" {f.operation_name} | " + " ".join((str(v) for v in sorted_domain))
|
||||||
self.b = b
|
sep_line = "-" + ("-" * len(f.operation_name)) + "-+-" +\
|
||||||
def __hash__(self):
|
("-" * len(sorted_domain)) +\
|
||||||
return hash(self.a) * hash(self.b)
|
("-" * reduce(lambda sum, v : sum + len(v.name), sorted_domain, 0))
|
||||||
def __eq__(self, other):
|
data_line = (" " * (len(f.operation_name) + 2)) + "| " + " ".join((str(f.mapping[(v,)]) for v in sorted_domain))
|
||||||
return isinstance(other, ModelOrderConstraint) and \
|
return "\n".join((header_line, sep_line, data_line)) + "\n"
|
||||||
self.a == other.a and self.b == other.b
|
|
||||||
|
def binary_function_str(f: ModelFunction) -> str:
|
||||||
|
assert isinstance(f, ModelFunction) and f.arity == 2
|
||||||
|
sorted_domain = sorted(f.domain, key=lambda v : v.name)
|
||||||
|
max_col_width = max(chain((len(v.name) for v in sorted_domain), (len(f.operation_name),)))
|
||||||
|
header_line = f" {f.operation_name} " +\
|
||||||
|
(" " * (max_col_width - len(f.operation_name))) + "| " +\
|
||||||
|
" ".join((str(v) for v in sorted_domain))
|
||||||
|
sep_line = "-" + ("-" * max_col_width) + "-+-" +\
|
||||||
|
("-" * len(sorted_domain)) +\
|
||||||
|
("-" * reduce(lambda sum, v : sum + len(v.name), sorted_domain, 0))
|
||||||
|
data_lines = ""
|
||||||
|
for row_v in sorted_domain:
|
||||||
|
data_line = f" {row_v.name} | " + " ".join((str(f.mapping[(row_v, col_v)]) for col_v in sorted_domain))
|
||||||
|
data_lines += data_line + "\n"
|
||||||
|
return "\n".join((header_line, sep_line, data_lines))
|
||||||
|
|
||||||
|
Interpretation = Dict[Operation, ModelFunction]
|
||||||
|
|
||||||
class Model:
|
class Model:
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -86,32 +112,46 @@ class Model:
|
||||||
carrier_set: Set[ModelValue],
|
carrier_set: Set[ModelValue],
|
||||||
logical_operations: Set[ModelFunction],
|
logical_operations: Set[ModelFunction],
|
||||||
designated_values: Set[ModelValue],
|
designated_values: Set[ModelValue],
|
||||||
ordering: Optional[Set[ModelOrderConstraint]] = None
|
name: Optional[str] = None
|
||||||
):
|
):
|
||||||
assert designated_values <= carrier_set
|
assert designated_values <= carrier_set
|
||||||
self.carrier_set = carrier_set
|
self.carrier_set = carrier_set
|
||||||
self.logical_operations = logical_operations
|
self.logical_operations = logical_operations
|
||||||
self.designated_values = designated_values
|
self.designated_values = designated_values
|
||||||
self.ordering = ordering if ordering is not None else set()
|
self.name = str(abs(hash((
|
||||||
# TODO: Make sure ordering is "valid"
|
frozenset(carrier_set),
|
||||||
# That is: transitive, etc.
|
frozenset(logical_operations),
|
||||||
|
frozenset(designated_values)
|
||||||
|
))))[:5] if name is None else name
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
result = f"""Carrier Set: {set_to_str(self.carrier_set)}
|
result = ("=" * 25) + f"""
|
||||||
|
Model Name: {self.name}
|
||||||
|
Carrier Set: {set_to_str(self.carrier_set)}
|
||||||
Designated Values: {set_to_str(self.designated_values)}
|
Designated Values: {set_to_str(self.designated_values)}
|
||||||
"""
|
"""
|
||||||
for function in self.logical_operations:
|
for function in self.logical_operations:
|
||||||
result += f"{str(function)}\n"
|
result += f"{str(function)}\n"
|
||||||
|
|
||||||
return result
|
return result + ("=" * 25) + "\n"
|
||||||
|
|
||||||
|
|
||||||
def evaluate_term(t: Term, f: Dict[PropositionalVariable, ModelValue], interpretation: Dict[Operation, ModelFunction]) -> ModelValue:
|
def evaluate_term(
|
||||||
|
t: Term, f: Dict[PropositionalVariable, ModelValue],
|
||||||
|
interpretation: Dict[Operation, ModelFunction]) -> ModelValue:
|
||||||
|
"""
|
||||||
|
Given a term in a logic, mapping
|
||||||
|
between terms and model values,
|
||||||
|
as well as an interpretation
|
||||||
|
of operations to model functions,
|
||||||
|
return the evaluated model value.
|
||||||
|
"""
|
||||||
|
|
||||||
if isinstance(t, PropositionalVariable):
|
if isinstance(t, PropositionalVariable):
|
||||||
return f[t]
|
return f[t]
|
||||||
|
|
||||||
model_function = interpretation[t.operation]
|
model_function = interpretation[t.operation]
|
||||||
model_arguments = []
|
model_arguments: List[ModelValue] = []
|
||||||
for logic_arg in t.arguments:
|
for logic_arg in t.arguments:
|
||||||
model_arg = evaluate_term(logic_arg, f, interpretation)
|
model_arg = evaluate_term(logic_arg, f, interpretation)
|
||||||
model_arguments.append(model_arg)
|
model_arguments.append(model_arg)
|
||||||
|
@ -121,11 +161,15 @@ def evaluate_term(t: Term, f: Dict[PropositionalVariable, ModelValue], interpret
|
||||||
def all_model_valuations(
|
def all_model_valuations(
|
||||||
pvars: Tuple[PropositionalVariable],
|
pvars: Tuple[PropositionalVariable],
|
||||||
mvalues: Tuple[ModelValue]):
|
mvalues: Tuple[ModelValue]):
|
||||||
|
"""
|
||||||
|
Given propositional variables and model values,
|
||||||
|
produce every possible mapping between the two.
|
||||||
|
"""
|
||||||
|
|
||||||
all_possible_values = product(mvalues, repeat=len(pvars))
|
all_possible_values = product(mvalues, repeat=len(pvars))
|
||||||
|
|
||||||
for valuation in all_possible_values:
|
for valuation in all_possible_values:
|
||||||
mapping: Dict[PropositionalVariable, ModelValue] = dict()
|
mapping: Dict[PropositionalVariable, ModelValue] = {}
|
||||||
assert len(pvars) == len(valuation)
|
assert len(pvars) == len(valuation)
|
||||||
for pvar, value in zip(pvars, valuation):
|
for pvar, value in zip(pvars, valuation):
|
||||||
mapping[pvar] = value
|
mapping[pvar] = value
|
||||||
|
@ -137,98 +181,92 @@ def all_model_valuations_cached(
|
||||||
mvalues: Tuple[ModelValue]):
|
mvalues: Tuple[ModelValue]):
|
||||||
return list(all_model_valuations(pvars, mvalues))
|
return list(all_model_valuations(pvars, mvalues))
|
||||||
|
|
||||||
def rule_ordering_satisfied(model: Model, interpretation: Dict[Operation, ModelFunction]) -> bool:
|
|
||||||
"""
|
|
||||||
Currently testing whether this function helps with runtime...
|
|
||||||
"""
|
|
||||||
if Conjunction in interpretation:
|
|
||||||
possible_inputs = ((a, b) for (a, b) in product(model.carrier_set, model.carrier_set))
|
|
||||||
for a, b in possible_inputs:
|
|
||||||
output = interpretation[Conjunction](a, b)
|
|
||||||
if a < b in model.ordering and output != a:
|
|
||||||
print("RETURNING FALSE")
|
|
||||||
return False
|
|
||||||
if b < a in model.ordering and output != b:
|
|
||||||
print("RETURNING FALSE")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if Disjunction in interpretation:
|
|
||||||
possible_inputs = ((a, b) for (a, b) in product(model.carrier_set, model.carrier_set))
|
|
||||||
for a, b in possible_inputs:
|
|
||||||
output = interpretation[Disjunction](a, b)
|
|
||||||
if a < b in model.ordering and output != b:
|
|
||||||
print("RETURNING FALSE")
|
|
||||||
return False
|
|
||||||
if b < a in model.ordering and output != a:
|
|
||||||
print("RETURNING FALSE")
|
|
||||||
return False
|
|
||||||
|
|
||||||
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
|
||||||
|
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))
|
mappings = all_model_valuations_cached(pvars, tuple(model.carrier_set))
|
||||||
|
|
||||||
# NOTE: Does not look like rule ordering is helping for finding
|
|
||||||
# models of R...
|
|
||||||
if not rule_ordering_satisfied(model, interpretation):
|
|
||||||
return False
|
|
||||||
|
|
||||||
for mapping in mappings:
|
for mapping in mappings:
|
||||||
|
# Make sure that the model satisfies each of the rules
|
||||||
for rule in logic.rules:
|
for rule in logic.rules:
|
||||||
|
# The check only applies if the premises are designated
|
||||||
premise_met = True
|
premise_met = True
|
||||||
premise_ts = set()
|
premise_ts: Set[ModelValue] = set()
|
||||||
|
|
||||||
for premise in rule.premises:
|
for premise in rule.premises:
|
||||||
premise_t = evaluate_term(premise, mapping, interpretation)
|
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:
|
if premise_t not in model.designated_values:
|
||||||
premise_met = False
|
premise_met = False
|
||||||
break
|
break
|
||||||
|
# If designated, keep track of the evaluated term
|
||||||
premise_ts.add(premise_t)
|
premise_ts.add(premise_t)
|
||||||
|
|
||||||
if not premise_met:
|
if not premise_met:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# With the premises designated, make sure the consequent is designated
|
||||||
consequent_t = evaluate_term(rule.conclusion, mapping, interpretation)
|
consequent_t = evaluate_term(rule.conclusion, mapping, interpretation)
|
||||||
|
|
||||||
if consequent_t not in model.designated_values:
|
if consequent_t not in model.designated_values:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
from itertools import combinations_with_replacement
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
def model_closure(initial_set: Set[ModelValue], mfunctions: Set[ModelFunction]):
|
def model_closure(initial_set: Set[ModelValue], mfunctions: Set[ModelFunction]):
|
||||||
|
"""
|
||||||
|
Given an initial set of model values and a set of model functions,
|
||||||
|
compute the complete set of model values that are closed
|
||||||
|
under the operations.
|
||||||
|
"""
|
||||||
closure_set: Set[ModelValue] = initial_set
|
closure_set: Set[ModelValue] = initial_set
|
||||||
last_new = initial_set
|
last_new: Set[ModelValue] = initial_set
|
||||||
changed = True
|
changed: bool = True
|
||||||
|
|
||||||
while changed:
|
while changed:
|
||||||
changed = False
|
changed = False
|
||||||
new_elements = set()
|
new_elements: Set[ModelValue] = set()
|
||||||
old_closure = closure_set - last_new
|
old_closure: Set[ModelValue] = closure_set - last_new
|
||||||
|
|
||||||
# arity -> args
|
# arity -> args
|
||||||
cached_args = defaultdict(list)
|
cached_args = defaultdict(list)
|
||||||
|
|
||||||
|
# Pass elements into each model function
|
||||||
for mfun in mfunctions:
|
for mfun in mfunctions:
|
||||||
|
|
||||||
# Use cached args if this arity was looked at before
|
# If a previous function shared the same arity,
|
||||||
|
# we'll use the same set of computed arguments
|
||||||
|
# to pass into the model functions.
|
||||||
if mfun.arity in cached_args:
|
if mfun.arity in cached_args:
|
||||||
for args in cached_args[mfun.arity]:
|
for args in cached_args[mfun.arity]:
|
||||||
|
# Compute the new elements
|
||||||
|
# given the cached arguments.
|
||||||
element = mfun(*args)
|
element = mfun(*args)
|
||||||
if element not in closure_set:
|
if element not in closure_set:
|
||||||
new_elements.add(element)
|
new_elements.add(element)
|
||||||
# Move onto next function
|
|
||||||
|
# We don't need to compute the arguments
|
||||||
|
# thanks to the cache, so move onto the
|
||||||
|
# next function.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Iterate over how many new elements would be within the arguments
|
# At this point, we don't have cached arguments, so we need
|
||||||
# NOTE: To not repeat work, there must be at least one new element
|
# to compute this set.
|
||||||
|
|
||||||
|
# Each argument must have at least one new element to not repeat
|
||||||
|
# work. We'll range over the number of new model values within our
|
||||||
|
# argument.
|
||||||
for num_new in range(1, mfun.arity + 1):
|
for num_new in range(1, mfun.arity + 1):
|
||||||
new_args = combinations_with_replacement(last_new, r=num_new)
|
new_args = combinations_with_replacement(last_new, r=num_new)
|
||||||
old_args = combinations_with_replacement(old_closure, r=mfun.arity - num_new)
|
old_args = combinations_with_replacement(old_closure, r=mfun.arity - num_new)
|
||||||
|
# Determine every possible ordering of the concatenated
|
||||||
|
# new and old model values.
|
||||||
for new_arg, old_arg in product(new_args, old_args):
|
for new_arg, old_arg in product(new_args, old_args):
|
||||||
for args in permutations(new_arg + old_arg):
|
for args in permutations(new_arg + old_arg):
|
||||||
cached_args[mfun.arity].append(args)
|
cached_args[mfun.arity].append(args)
|
||||||
|
|
|
@ -49,27 +49,39 @@ def parse_matrices(infile: TextIO) -> List[Tuple[Model, Dict]]:
|
||||||
|
|
||||||
for mimplication in results:
|
for mimplication in results:
|
||||||
logical_operations = {
|
logical_operations = {
|
||||||
mnegation, mconjunction, mdisjunction,
|
mnegation, mimplication
|
||||||
mimplication
|
|
||||||
}
|
}
|
||||||
model = Model(carrier_set, logical_operations, designated_values)
|
model = Model(carrier_set, logical_operations, designated_values, name=str(len(solutions)))
|
||||||
interpretation = {
|
interpretation = {
|
||||||
Negation: mnegation,
|
Negation: mnegation,
|
||||||
Conjunction: mconjunction,
|
|
||||||
Disjunction: mdisjunction,
|
|
||||||
Implication: mimplication
|
Implication: mimplication
|
||||||
}
|
}
|
||||||
|
if mconjunction is not None:
|
||||||
|
logical_operations.add(mconjunction)
|
||||||
|
interpretation[Conjunction] = mconjunction
|
||||||
|
if mdisjunction is not None:
|
||||||
|
logical_operations.add(mdisjunction)
|
||||||
|
interpretation[Disjunction] = mdisjunction
|
||||||
|
|
||||||
solutions.append((model, interpretation))
|
solutions.append((model, interpretation))
|
||||||
|
print(f"Parsed Matrix {model.name}")
|
||||||
|
|
||||||
return solutions
|
return solutions
|
||||||
|
|
||||||
def carrier_set_from_size(size: int):
|
def carrier_set_from_size(size: int):
|
||||||
|
"""
|
||||||
|
Construct a carrier set of model values
|
||||||
|
based on the desired size.
|
||||||
|
"""
|
||||||
return {
|
return {
|
||||||
mvalue_from_index(i) for i in range(size + 1)
|
mvalue_from_index(i) for i in range(size + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse_size(infile: TextIO) -> Optional[int]:
|
def parse_size(infile: TextIO) -> Optional[int]:
|
||||||
# Elements are represented in hexidecimal
|
"""
|
||||||
|
Parse the line representing the matrix size.
|
||||||
|
NOTE: Elements are represented in hexidecimal.
|
||||||
|
"""
|
||||||
size = int(next(infile), 16)
|
size = int(next(infile), 16)
|
||||||
if size == -1:
|
if size == -1:
|
||||||
return None
|
return None
|
||||||
|
@ -77,6 +89,9 @@ def parse_size(infile: TextIO) -> Optional[int]:
|
||||||
return size
|
return size
|
||||||
|
|
||||||
def parse_negation(infile: TextIO, size: int) -> Optional[ModelFunction]:
|
def parse_negation(infile: TextIO, size: int) -> Optional[ModelFunction]:
|
||||||
|
"""
|
||||||
|
Parse the line representing the negation table.
|
||||||
|
"""
|
||||||
line = next(infile).strip()
|
line = next(infile).strip()
|
||||||
if line == '-1':
|
if line == '-1':
|
||||||
return None
|
return None
|
||||||
|
@ -90,18 +105,29 @@ def parse_negation(infile: TextIO, size: int) -> Optional[ModelFunction]:
|
||||||
y = parse_mvalue(j)
|
y = parse_mvalue(j)
|
||||||
mapping[(x, )] = y
|
mapping[(x, )] = y
|
||||||
|
|
||||||
return ModelFunction(1, mapping, "Negation")
|
return ModelFunction(1, mapping, "¬")
|
||||||
|
|
||||||
|
|
||||||
def mvalue_from_index(i: int):
|
def mvalue_from_index(i: int):
|
||||||
|
"""
|
||||||
|
Given an index, return the hexidecimal
|
||||||
|
representation of the model value.
|
||||||
|
"""
|
||||||
return ModelValue(f"a{hex(i)[-1]}")
|
return ModelValue(f"a{hex(i)[-1]}")
|
||||||
|
|
||||||
def parse_mvalue(x: str) -> ModelValue:
|
def parse_mvalue(x: str) -> ModelValue:
|
||||||
|
"""
|
||||||
|
Parse an element and return the model value.
|
||||||
|
"""
|
||||||
return mvalue_from_index(int(x, 16))
|
return mvalue_from_index(int(x, 16))
|
||||||
|
|
||||||
def determine_cresult(size: int, ordering: Dict[ModelValue, ModelValue], a: ModelValue, b: ModelValue) -> ModelValue:
|
def determine_cresult(size: int, ordering: Dict[ModelValue, ModelValue], a: ModelValue, b: ModelValue) -> ModelValue:
|
||||||
|
"""
|
||||||
|
Determine what a ∧ b should be given the ordering table.
|
||||||
|
"""
|
||||||
for i in range(size + 1):
|
for i in range(size + 1):
|
||||||
c = mvalue_from_index(i)
|
c = mvalue_from_index(i)
|
||||||
|
|
||||||
if not ordering[(c, a)]:
|
if not ordering[(c, a)]:
|
||||||
continue
|
continue
|
||||||
if not ordering[(c, b)]:
|
if not ordering[(c, b)]:
|
||||||
|
@ -119,9 +145,10 @@ def determine_cresult(size: int, ordering: Dict[ModelValue, ModelValue], a: Mode
|
||||||
if not invalid:
|
if not invalid:
|
||||||
return c
|
return c
|
||||||
|
|
||||||
print(a, "&", b, "is not defined")
|
|
||||||
|
|
||||||
def determine_dresult(size: int, ordering: Dict[ModelValue, ModelValue], a: ModelValue, b: ModelValue) -> ModelValue:
|
def determine_dresult(size: int, ordering: Dict[ModelValue, ModelValue], a: ModelValue, b: ModelValue) -> ModelValue:
|
||||||
|
"""
|
||||||
|
Determine what a ∨ b should be given the ordering table.
|
||||||
|
"""
|
||||||
for i in range(size + 1):
|
for i in range(size + 1):
|
||||||
c = mvalue_from_index(i)
|
c = mvalue_from_index(i)
|
||||||
if not ordering[(a, c)]:
|
if not ordering[(a, c)]:
|
||||||
|
@ -143,6 +170,9 @@ def determine_dresult(size: int, ordering: Dict[ModelValue, ModelValue], a: Mode
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def parse_order(infile: TextIO, size: int) -> Optional[Tuple[ModelFunction, ModelFunction]]:
|
def parse_order(infile: TextIO, size: int) -> Optional[Tuple[ModelFunction, ModelFunction]]:
|
||||||
|
"""
|
||||||
|
Parse the line representing the ordering table
|
||||||
|
"""
|
||||||
line = next(infile).strip()
|
line = next(infile).strip()
|
||||||
if line == '-1':
|
if line == '-1':
|
||||||
return None
|
return None
|
||||||
|
@ -170,16 +200,30 @@ def parse_order(infile: TextIO, size: int) -> Optional[Tuple[ModelFunction, Mode
|
||||||
for j in range(size + 1):
|
for j in range(size + 1):
|
||||||
y = mvalue_from_index(j)
|
y = mvalue_from_index(j)
|
||||||
|
|
||||||
cmapping[(x, y)] = determine_cresult(size, omapping, x, y)
|
cresult = determine_cresult(size, omapping, x, y)
|
||||||
dmapping[(x, y)] = determine_dresult(size, omapping, x, y)
|
if cresult is None:
|
||||||
|
print("[Warning] Conjunction and Disjunction are not well-defined")
|
||||||
|
print(f"{x} ∧ {y} = ??")
|
||||||
|
return None, None
|
||||||
|
cmapping[(x, y)] = cresult
|
||||||
|
|
||||||
|
dresult = determine_dresult(size, omapping, x, y)
|
||||||
|
if dresult is None:
|
||||||
|
print("[Warning] Conjunction and Disjunction are not well-defined")
|
||||||
|
print(f"{x} ∨ {y} = ??")
|
||||||
|
return None, None
|
||||||
|
dmapping[(x, y)] = dresult
|
||||||
|
|
||||||
|
|
||||||
mconjunction = ModelFunction(2, cmapping, "Conjunction")
|
mconjunction = ModelFunction(2, cmapping, "∧")
|
||||||
mdisjunction = ModelFunction(2, dmapping, "Disjunction")
|
mdisjunction = ModelFunction(2, dmapping, "∨")
|
||||||
|
|
||||||
return mconjunction, mdisjunction
|
return mconjunction, mdisjunction
|
||||||
|
|
||||||
def parse_designated(infile: TextIO, size: int) -> Optional[Set[ModelValue]]:
|
def parse_designated(infile: TextIO, size: int) -> Optional[Set[ModelValue]]:
|
||||||
|
"""
|
||||||
|
Parse the line representing which model values are designated.
|
||||||
|
"""
|
||||||
line = next(infile).strip()
|
line = next(infile).strip()
|
||||||
if line == '-1':
|
if line == '-1':
|
||||||
return None
|
return None
|
||||||
|
@ -198,6 +242,10 @@ def parse_designated(infile: TextIO, size: int) -> Optional[Set[ModelValue]]:
|
||||||
|
|
||||||
|
|
||||||
def parse_implication(infile: TextIO, size: int) -> Optional[List[ModelFunction]]:
|
def parse_implication(infile: TextIO, size: int) -> Optional[List[ModelFunction]]:
|
||||||
|
"""
|
||||||
|
Parse the line representing the list of implication
|
||||||
|
tables.
|
||||||
|
"""
|
||||||
line = next(infile).strip()
|
line = next(infile).strip()
|
||||||
if line == '-1':
|
if line == '-1':
|
||||||
return None
|
return None
|
||||||
|
@ -223,7 +271,7 @@ def parse_implication(infile: TextIO, size: int) -> Optional[List[ModelFunction]
|
||||||
|
|
||||||
mapping[(x, y)] = r
|
mapping[(x, y)] = r
|
||||||
|
|
||||||
mimplication = ModelFunction(2, mapping, "Implication")
|
mimplication = ModelFunction(2, mapping, "→")
|
||||||
mimplications.append(mimplication)
|
mimplications.append(mimplication)
|
||||||
|
|
||||||
return mimplications
|
return mimplications
|
||||||
|
@ -233,9 +281,5 @@ if __name__ == "__main__":
|
||||||
solutions: List[Model] = parse_matrices(sys.stdin)
|
solutions: List[Model] = parse_matrices(sys.stdin)
|
||||||
print(f"Parsed {len(solutions)} matrices")
|
print(f"Parsed {len(solutions)} matrices")
|
||||||
for i, (model, interpretation) in enumerate(solutions):
|
for i, (model, interpretation) in enumerate(solutions):
|
||||||
# print(model)
|
print(model)
|
||||||
if has_vsp(model, interpretation):
|
print(has_vsp(model, interpretation))
|
||||||
print(model)
|
|
||||||
print("Has VSP")
|
|
||||||
else:
|
|
||||||
print("Model", i, "does not have VSP")
|
|
||||||
|
|
54
vsp.py
54
vsp.py
|
@ -3,40 +3,64 @@ Check to see if the model has the variable
|
||||||
sharing property.
|
sharing property.
|
||||||
"""
|
"""
|
||||||
from itertools import chain, combinations, product
|
from itertools import chain, combinations, product
|
||||||
from typing import Dict, Set, Tuple, List
|
from typing import Dict, List, Optional, Set, Tuple
|
||||||
|
from common import set_to_str
|
||||||
from model import (
|
from model import (
|
||||||
Model, ModelFunction, ModelValue, model_closure
|
Model, model_closure, ModelFunction, ModelValue
|
||||||
)
|
)
|
||||||
from logic import Implication, Operation
|
from logic import Implication, Operation
|
||||||
|
|
||||||
def preseed(initial_set: Set[ModelValue], cache:List[Tuple[Set[ModelValue], Set[ModelValue]]]):
|
def preseed(
|
||||||
|
initial_set: Set[ModelValue],
|
||||||
|
cache:List[Tuple[Set[ModelValue], Set[ModelValue]]]):
|
||||||
"""
|
"""
|
||||||
Cache contains caches of model closure calls:
|
Given a cache of previous model_closure calls,
|
||||||
Ex:
|
use this to compute an initial model closure
|
||||||
{1, 2, 3} -> {....}
|
set based on the initial set.
|
||||||
|
|
||||||
|
Basic Idea:
|
||||||
|
Let {1, 2, 3} -> X be in the cache.
|
||||||
If {1,2,3} is a subset of initial set,
|
If {1,2,3} is a subset of initial set,
|
||||||
then {....} is the subset of the output of model_closure.
|
then X is the subset of the output of model_closure.
|
||||||
|
|
||||||
We'll use the output to speed up the saturation procedure
|
This is used to speed up subsequent calls to model_closure
|
||||||
"""
|
"""
|
||||||
candidate_preseed: Tuple[Set[ModelValue], int] = (None, None)
|
candidate_preseed: Tuple[Set[ModelValue], int] = (None, None)
|
||||||
|
|
||||||
for i, o in cache:
|
for i, o in cache:
|
||||||
if i < initial_set:
|
if i < initial_set:
|
||||||
cost = len(initial_set - i)
|
cost = len(initial_set - i)
|
||||||
|
# If i is a subset with less missing elements than
|
||||||
|
# the previous candidate, then it's the new candidate.
|
||||||
if candidate_preseed[1] is None or cost < candidate_preseed[1]:
|
if candidate_preseed[1] is None or cost < candidate_preseed[1]:
|
||||||
candidate_preseed = o, cost
|
candidate_preseed = o, cost
|
||||||
|
|
||||||
same_set = candidate_preseed[1] == 0
|
same_set = candidate_preseed[1] == 0
|
||||||
return candidate_preseed[0], same_set
|
return candidate_preseed[0], same_set
|
||||||
|
|
||||||
def has_vsp(model: Model, interpretation: Dict[Operation, ModelFunction]) -> bool:
|
class VSP_Result:
|
||||||
"""
|
def __init__(
|
||||||
Tells you whether a model has the
|
self, has_vsp: bool, model_name: Optional[str] = None,
|
||||||
variable sharing property.
|
subalgebra1: Optional[Set[ModelValue]] = None,
|
||||||
"""
|
subalgebra2: Optional[Set[ModelValue]] = None):
|
||||||
|
self.has_vsp = has_vsp
|
||||||
|
self.model_name = model_name
|
||||||
|
self.subalgebra1 = subalgebra1
|
||||||
|
self.subalgebra2 = subalgebra2
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if not self.has_vsp:
|
||||||
|
return f"Model {self.model_name} does not have the variable sharing property."
|
||||||
|
return f"""Model {self.model_name} has the variable sharing property.
|
||||||
|
Subalgebra 1: {set_to_str(self.subalgebra1)}
|
||||||
|
Subalgebra 2: {set_to_str(self.subalgebra2)}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def has_vsp(model: Model, interpretation: Dict[Operation, ModelFunction]) -> VSP_Result:
|
||||||
|
"""
|
||||||
|
Checks whether a model has the variable
|
||||||
|
sharing property.
|
||||||
|
"""
|
||||||
impfunction = interpretation[Implication]
|
impfunction = interpretation[Implication]
|
||||||
|
|
||||||
# Compute I the set of tuples (x, y) where
|
# Compute I the set of tuples (x, y) where
|
||||||
|
@ -109,6 +133,6 @@ def has_vsp(model: Model, interpretation: Dict[Operation, ModelFunction]) -> boo
|
||||||
break
|
break
|
||||||
|
|
||||||
if falsified:
|
if falsified:
|
||||||
return True
|
return VSP_Result(True, model.name, carrier_set_left, carrier_set_right)
|
||||||
|
|
||||||
return False
|
return VSP_Result(False, model.name)
|
||||||
|
|
Loading…
Reference in a new issue