Code cleanup

This commit is contained in:
Brandon Rozek 2024-05-28 16:05:06 -04:00
parent 6b4d5828c8
commit 6bb863da97
No known key found for this signature in database
GPG key ID: 26E457DA82C9F480
3 changed files with 57 additions and 33 deletions

View file

@ -1,9 +1,13 @@
""" """
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,
satisfiable, ModelFunction
)
from itertools import combinations, chain, product from itertools import combinations, chain, product
from typing import Set, List, Dict, Tuple from typing import Set, List, Dict, Tuple
@ -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)

View file

@ -1,4 +1,4 @@
from typing import Any, Set, Tuple from typing import Set, Tuple
from functools import lru_cache from functools import lru_cache
class Operation: class Operation:

View file

@ -13,7 +13,8 @@ from itertools import combinations_with_replacement, permutations, product
from typing import Dict, List, Set, Tuple from typing import Dict, List, Set, Tuple
__all__ = ['ModelValue', 'ModelFunction', 'Model'] __all__ = ['ModelValue', 'ModelFunction', 'Model', 'Interpretation']
class ModelValue: class ModelValue:
def __init__(self, name): def __init__(self, name):
@ -63,6 +64,8 @@ class ModelFunction:
def __call__(self, *args): def __call__(self, *args):
return self.mapping[args] return self.mapping[args]
Interpretation = Dict[Operation, ModelFunction]
class Model: class Model:
def __init__( def __init__(
self, self,