mirror of
				https://github.com/Brandon-Rozek/matmod.git
				synced 2025-11-03 03:11:12 +00:00 
			
		
		
		
	Cleanup and small optimizations
This commit is contained in:
		
							parent
							
								
									de8637bca4
								
							
						
					
					
						commit
						6f5074584b
					
				
					 3 changed files with 85 additions and 70 deletions
				
			
		
							
								
								
									
										127
									
								
								parse_magic.py
									
										
									
									
									
								
							
							
						
						
									
										127
									
								
								parse_magic.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -5,6 +5,7 @@ Assumes the base logic is R with no extra connectives
 | 
			
		|||
"""
 | 
			
		||||
import re
 | 
			
		||||
from typing import TextIO, List, Iterator, Optional, Tuple, Set, Dict
 | 
			
		||||
from itertools import product
 | 
			
		||||
 | 
			
		||||
from model import Model, ModelValue, ModelFunction, OrderTable
 | 
			
		||||
from logic import (
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +20,7 @@ from logic import (
 | 
			
		|||
class SourceFile:
 | 
			
		||||
    def __init__(self, fileobj: TextIO):
 | 
			
		||||
        self.fileobj = fileobj
 | 
			
		||||
        self.current_line = 0
 | 
			
		||||
        self.line_in_file = 0
 | 
			
		||||
        self.reststr = ""
 | 
			
		||||
 | 
			
		||||
    def next_line(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +35,7 @@ class SourceFile:
 | 
			
		|||
            return reststr
 | 
			
		||||
 | 
			
		||||
        contents = next(self.fileobj).strip()
 | 
			
		||||
        self.current_line += 1
 | 
			
		||||
        self.line_in_file += 1
 | 
			
		||||
        return contents
 | 
			
		||||
 | 
			
		||||
    def __next__(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +44,7 @@ class SourceFile:
 | 
			
		|||
        """
 | 
			
		||||
        if self.reststr == "":
 | 
			
		||||
            self.reststr = next(self.fileobj).strip()
 | 
			
		||||
            self.current_line += 1
 | 
			
		||||
            self.line_in_file += 1
 | 
			
		||||
 | 
			
		||||
        next_token, _, self.reststr = self.reststr.partition(" ")
 | 
			
		||||
        return next_token
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +61,7 @@ class UglyHeader:
 | 
			
		|||
class ModelBuilder:
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.size : int = 0
 | 
			
		||||
        self.carrier_set : Set[ModelValue] = set()
 | 
			
		||||
        self.carrier_list : List[ModelValue] = []
 | 
			
		||||
        self.mnegation: Optional[ModelFunction] = None
 | 
			
		||||
        self.ordering: Optional[OrderTable] = None
 | 
			
		||||
        self.mconjunction: Optional[ModelFunction] = None
 | 
			
		||||
| 
						 | 
				
			
			@ -97,8 +98,6 @@ class Stages:
 | 
			
		|||
 | 
			
		||||
    def add(self, name: str):
 | 
			
		||||
        stage = Stage(name)
 | 
			
		||||
        stage.next = stage
 | 
			
		||||
 | 
			
		||||
        stage.previous = self.last_added_stage
 | 
			
		||||
 | 
			
		||||
        # End stage is a sink so don't
 | 
			
		||||
| 
						 | 
				
			
			@ -115,11 +114,16 @@ class Stages:
 | 
			
		|||
 | 
			
		||||
    def reset_after(self, name):
 | 
			
		||||
        """
 | 
			
		||||
        Resets the stage counters after a given stage.
 | 
			
		||||
        This is to accurately reflect the name of the
 | 
			
		||||
        model within MaGIC.
 | 
			
		||||
        Resets the counters of every stage after
 | 
			
		||||
        a given stage.
 | 
			
		||||
 | 
			
		||||
        This is to accurately reflect how names are
 | 
			
		||||
        generated within magic.
 | 
			
		||||
        Example: 1.1, 1.2, (reset after 1), 2.1, 2.2
 | 
			
		||||
        """
 | 
			
		||||
        stage = self.stages[name]
 | 
			
		||||
        # NOTE: The process_model stage doesn't
 | 
			
		||||
        # have a counter associated with it.
 | 
			
		||||
        while stage.name != "process_model":
 | 
			
		||||
            stage.reset()
 | 
			
		||||
            stage = stage.next
 | 
			
		||||
| 
						 | 
				
			
			@ -128,15 +132,26 @@ class Stages:
 | 
			
		|||
        return self.stages[name]
 | 
			
		||||
 | 
			
		||||
    def name(self):
 | 
			
		||||
        """
 | 
			
		||||
        Get the full name of where we are within
 | 
			
		||||
        the parsing process. Takes into account
 | 
			
		||||
        the stage number of all the stages seen
 | 
			
		||||
        so far.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Stages haven't been added yet
 | 
			
		||||
        result = ""
 | 
			
		||||
        stage = self.first_stage
 | 
			
		||||
        if stage is None:
 | 
			
		||||
            return ""
 | 
			
		||||
 | 
			
		||||
        # There's only one stage
 | 
			
		||||
        result = f"{stage.num}"
 | 
			
		||||
        if stage.next == "process_model":
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
        # Add every subsequent stage counter
 | 
			
		||||
        # by appending .stage_num
 | 
			
		||||
        stage = stage.next
 | 
			
		||||
        while stage is not None:
 | 
			
		||||
            result += f".{stage.num}"
 | 
			
		||||
| 
						 | 
				
			
			@ -172,6 +187,7 @@ def parse_matrices(infile: SourceFile) -> Iterator[Tuple[Model, Dict[Operation,
 | 
			
		|||
    stages = derive_stages(header)
 | 
			
		||||
    first_run = True
 | 
			
		||||
    current_model_parts = ModelBuilder()
 | 
			
		||||
 | 
			
		||||
    stage = stages.first_stage
 | 
			
		||||
    while True:
 | 
			
		||||
        match stage.name:
 | 
			
		||||
| 
						 | 
				
			
			@ -245,22 +261,24 @@ def parse_matrices(infile: SourceFile) -> Iterator[Tuple[Model, Dict[Operation,
 | 
			
		|||
                    stage = stage.previous
 | 
			
		||||
 | 
			
		||||
def process_sizes(infile: SourceFile, current_model_parts: ModelBuilder, first_run: bool) -> bool:
 | 
			
		||||
    size: Optional[int] = None
 | 
			
		||||
    try:
 | 
			
		||||
        size = parse_size(infile, first_run)
 | 
			
		||||
    except StopIteration:
 | 
			
		||||
        return False
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    if size is None:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    carrier_set = carrier_set_from_size(size)
 | 
			
		||||
    current_model_parts.carrier_set = carrier_set
 | 
			
		||||
    carrier_list = carrier_list_from_size(size)
 | 
			
		||||
    current_model_parts.carrier_list = carrier_list
 | 
			
		||||
    current_model_parts.size = size
 | 
			
		||||
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
def process_negations(infile: SourceFile, current_model_parts: ModelBuilder) -> bool:
 | 
			
		||||
    """Stage 2 (Optional)"""
 | 
			
		||||
    mnegation = parse_single_monadic_connective(infile, "¬", current_model_parts.size)
 | 
			
		||||
    mnegation = parse_single_monadic_connective(infile, "¬", current_model_parts.size, current_model_parts.carrier_list)
 | 
			
		||||
    if mnegation is None:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -269,7 +287,7 @@ def process_negations(infile: SourceFile, current_model_parts: ModelBuilder) ->
 | 
			
		|||
 | 
			
		||||
def process_orders(infile: SourceFile, current_model_parts: ModelBuilder) -> bool:
 | 
			
		||||
    """Stage 3"""
 | 
			
		||||
    result = parse_single_order(infile, current_model_parts.size)
 | 
			
		||||
    result = parse_single_order(infile, current_model_parts.size, current_model_parts.carrier_list)
 | 
			
		||||
    if result is None:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -299,7 +317,7 @@ def process_implications(infile: SourceFile, current_model_parts: ModelBuilder)
 | 
			
		|||
    return True
 | 
			
		||||
 | 
			
		||||
def process_necessitations(infile: SourceFile, current_model_parts: ModelBuilder) -> bool:
 | 
			
		||||
    mnecessitation = parse_single_monadic_connective(infile, "!", current_model_parts.size)
 | 
			
		||||
    mnecessitation = parse_single_monadic_connective(infile, "!", current_model_parts.size, current_model_parts.carrier_list)
 | 
			
		||||
    if mnecessitation is None:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -310,7 +328,7 @@ def process_custom_connective(infile: SourceFile, symbol: str, adicity: int, cur
 | 
			
		|||
    if adicity == 0:
 | 
			
		||||
        mfunction = parse_single_nullary_connective(infile, symbol)
 | 
			
		||||
    elif adicity == 1:
 | 
			
		||||
        mfunction = parse_single_monadic_connective(infile, symbol, current_model_parts.size)
 | 
			
		||||
        mfunction = parse_single_monadic_connective(infile, symbol, current_model_parts.size, current_model_parts.carrier_list)
 | 
			
		||||
    elif adicity == 2:
 | 
			
		||||
        mfunction = parse_single_dyadic_connective(infile, symbol, current_model_parts.size)
 | 
			
		||||
    else:
 | 
			
		||||
| 
						 | 
				
			
			@ -325,8 +343,8 @@ def process_custom_connective(infile: SourceFile, symbol: str, adicity: int, cur
 | 
			
		|||
def process_model(model_name: str, mp: ModelBuilder) -> Tuple[Model, Dict[Operation, ModelFunction]]:
 | 
			
		||||
    """Create Model"""
 | 
			
		||||
    assert mp.size > 0
 | 
			
		||||
    assert mp.size + 1 == len(mp.carrier_set)
 | 
			
		||||
    assert len(mp.designated_values) <= len(mp.carrier_set)
 | 
			
		||||
    assert mp.size + 1 == len(mp.carrier_list)
 | 
			
		||||
    assert len(mp.designated_values) <= len(mp.carrier_list)
 | 
			
		||||
    assert mp.mimplication is not None
 | 
			
		||||
 | 
			
		||||
    logical_operations = { mp.mimplication }
 | 
			
		||||
| 
						 | 
				
			
			@ -351,8 +369,8 @@ def process_model(model_name: str, mp: ModelBuilder) -> Tuple[Model, Dict[Operat
 | 
			
		|||
            logical_operations.add(custom_mf)
 | 
			
		||||
            op = Operation(custom_mf.operation_name, custom_mf.arity)
 | 
			
		||||
            interpretation[op] = custom_mf
 | 
			
		||||
    
 | 
			
		||||
    model = Model(mp.carrier_set, logical_operations, mp.designated_values, ordering=mp.ordering, name=model_name)
 | 
			
		||||
 | 
			
		||||
    model = Model(set(mp.carrier_list), logical_operations, mp.designated_values, ordering=mp.ordering, name=model_name)
 | 
			
		||||
    return (model, interpretation)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -375,14 +393,14 @@ def parse_header(infile: SourceFile) -> UglyHeader:
 | 
			
		|||
        custom_model_functions.append((arity, symbol))
 | 
			
		||||
    return UglyHeader(negation_defined, necessitation_defined, custom_model_functions)
 | 
			
		||||
 | 
			
		||||
def carrier_set_from_size(size: int) -> Set[ModelValue]:
 | 
			
		||||
def carrier_list_from_size(size: int) -> List[ModelValue]:
 | 
			
		||||
    """
 | 
			
		||||
    Construct a carrier set of model values
 | 
			
		||||
    based on the desired size.
 | 
			
		||||
    """
 | 
			
		||||
    return {
 | 
			
		||||
    return [
 | 
			
		||||
        mvalue_from_index(i) for i in range(size + 1)
 | 
			
		||||
    }
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
def parse_size(infile: SourceFile, first_run: bool) -> Optional[int]:
 | 
			
		||||
    """
 | 
			
		||||
| 
						 | 
				
			
			@ -399,7 +417,7 @@ def parse_size(infile: SourceFile, first_run: bool) -> Optional[int]:
 | 
			
		|||
 | 
			
		||||
    if size == -1:
 | 
			
		||||
        return None
 | 
			
		||||
    assert size > 0, f"Unexpected size at line {infile.current_line}"
 | 
			
		||||
    assert size > 0, f"Unexpected size at line {infile.line_in_file}"
 | 
			
		||||
    return size
 | 
			
		||||
 | 
			
		||||
def mvalue_from_index(i: int) -> ModelValue:
 | 
			
		||||
| 
						 | 
				
			
			@ -463,7 +481,7 @@ def determine_dresult(size: int, ordering: Dict[ModelValue, ModelValue], a: Mode
 | 
			
		|||
        if not invalid:
 | 
			
		||||
            return c
 | 
			
		||||
 | 
			
		||||
def parse_single_order(infile: SourceFile, size: int) -> Optional[Tuple[OrderTable, ModelFunction, ModelFunction]]:
 | 
			
		||||
def parse_single_order(infile: SourceFile, size: int, carrier_list: List[ModelValue]) -> Optional[Tuple[OrderTable, ModelFunction, ModelFunction]]:
 | 
			
		||||
    """
 | 
			
		||||
    Parse the line representing the ordering table
 | 
			
		||||
    """
 | 
			
		||||
| 
						 | 
				
			
			@ -473,43 +491,35 @@ def parse_single_order(infile: SourceFile, size: int) -> Optional[Tuple[OrderTab
 | 
			
		|||
 | 
			
		||||
    table = line.split(" ")
 | 
			
		||||
 | 
			
		||||
    assert len(table) == (size + 1)**2, f"Order table doesn't match expected size at line {infile.current_line}"
 | 
			
		||||
    assert len(table) == (size + 1)**2, f"Order table doesn't match expected size at line {infile.line_in_file}"
 | 
			
		||||
 | 
			
		||||
    ordering = OrderTable()
 | 
			
		||||
    omapping = {}
 | 
			
		||||
    table_i = 0
 | 
			
		||||
 | 
			
		||||
    for i in range(size + 1):
 | 
			
		||||
        x = mvalue_from_index(i)
 | 
			
		||||
        for j in range(size + 1):
 | 
			
		||||
            y = mvalue_from_index(j)
 | 
			
		||||
            omapping[(x, y)] = table[table_i] == '1'
 | 
			
		||||
            if table[table_i] == '1':
 | 
			
		||||
                ordering.add(x, y)
 | 
			
		||||
            table_i += 1
 | 
			
		||||
    for x, y in product(carrier_list, carrier_list):
 | 
			
		||||
        omapping[(x, y)] = table[table_i] == '1'
 | 
			
		||||
        if table[table_i] == '1':
 | 
			
		||||
            ordering.add(x, y)
 | 
			
		||||
        table_i += 1
 | 
			
		||||
 | 
			
		||||
    cmapping = {}
 | 
			
		||||
    dmapping = {}
 | 
			
		||||
 | 
			
		||||
    for i in range(size + 1):
 | 
			
		||||
        x = mvalue_from_index(i)
 | 
			
		||||
        for j in range(size + 1):
 | 
			
		||||
            y = mvalue_from_index(j)
 | 
			
		||||
 | 
			
		||||
            cresult = determine_cresult(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
 | 
			
		||||
    for x, y in product(carrier_list, carrier_list):
 | 
			
		||||
        cresult = determine_cresult(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, "∧")
 | 
			
		||||
    mdisjunction = ModelFunction(2, dmapping, "∨")
 | 
			
		||||
| 
						 | 
				
			
			@ -525,7 +535,7 @@ def parse_single_designated(infile: SourceFile, size: int) -> Optional[Set[Model
 | 
			
		|||
        return None
 | 
			
		||||
 | 
			
		||||
    row = line.split(" ")
 | 
			
		||||
    assert len(row) == size + 1, f"Designated table doesn't match expected size at line {infile.current_line}"
 | 
			
		||||
    assert len(row) == size + 1, f"Designated table doesn't match expected size at line {infile.line_in_file}"
 | 
			
		||||
 | 
			
		||||
    designated_values = set()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -543,23 +553,22 @@ def parse_single_nullary_connective(infile: SourceFile, symbol: str) -> Optional
 | 
			
		|||
        return None
 | 
			
		||||
 | 
			
		||||
    row = line.split(" ")
 | 
			
		||||
    assert len(row) == 1, f"More than one assignment for a nullary connective was provided at line {infile.current_line}"
 | 
			
		||||
    assert len(row) == 1, f"More than one assignment for a nullary connective was provided at line {infile.line_in_file}"
 | 
			
		||||
 | 
			
		||||
    mapping = {}
 | 
			
		||||
    mapping[()] = parse_mvalue(row[0])
 | 
			
		||||
    return ModelFunction(0, mapping, symbol)
 | 
			
		||||
 | 
			
		||||
def parse_single_monadic_connective(infile: SourceFile, symbol: str, size: int) -> Optional[ModelFunction]:
 | 
			
		||||
def parse_single_monadic_connective(infile: SourceFile, symbol: str, size: int, carrier_list: List[ModelValue]) -> Optional[ModelFunction]:
 | 
			
		||||
    line = infile.next_line()
 | 
			
		||||
    if line == '-1':
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    row = line.split(" ")
 | 
			
		||||
    assert len(row) == size + 1, f"{symbol} table doesn't match size at line {infile.current_line}"
 | 
			
		||||
    assert len(row) == size + 1, f"{symbol} table doesn't match size at line {infile.line_in_file}"
 | 
			
		||||
    mapping = {}
 | 
			
		||||
 | 
			
		||||
    for i, j in zip(range(size + 1), row):
 | 
			
		||||
        x = mvalue_from_index(i)
 | 
			
		||||
    for x, j in zip(carrier_list, row):
 | 
			
		||||
        y = parse_mvalue(j)
 | 
			
		||||
        mapping[(x, )] = y
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -576,7 +585,7 @@ def parse_single_dyadic_connective(infile: SourceFile, symbol: str, size: int) -
 | 
			
		|||
    except StopIteration:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    assert len(table) == (size + 1)**2, f"{symbol} table does not match expected size at line {infile.current_line}"
 | 
			
		||||
    assert len(table) == (size + 1)**2, f"{symbol} table does not match expected size at line {infile.line_in_file}"
 | 
			
		||||
 | 
			
		||||
    mapping = {}
 | 
			
		||||
    table_i = 0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue