mcmas.fmtk
A growing formal methods toolkit. This is focused on abstract base classes (pydantic models) that are intended to be useful for describing things like "specifications" and "simulations" in general. Classes like ispl.Specification and ispl.Simulation extend these, but the hopefully the base classes are more reusable.
This might be reused to wrap other kinds of formalisms like:
- Other model-checkers (.. Alloy lang?)
- SAT & SMT Provers (.. Z3?)
- Constraints, Discrete-events, Systems Simulations (.. SimPy / CPMpy?)
- Games & Protocols, etc (.. py-mfglib?)
1""" 2mcmas.fmtk: 3 4A growing formal methods toolkit. This is focused on abstract base classes 5(pydantic models) that are intended to be useful for describing things like 6"specifications" and "simulations" in general. Classes like ispl.Specification 7and ispl.Simulation extend these, but the hopefully the base classes are more 8reusable. 9 10This might be reused to wrap other kinds of formalisms like: 11 12* Other model-checkers (.. Alloy lang?) 13* SAT & SMT Provers (.. Z3?) 14* Constraints, Discrete-events, Systems Simulations (.. SimPy / CPMpy?) 15* Games & Protocols, etc (.. py-mfglib?) 16""" 17 18import typing 19 20import pydantic 21from pydantic import Field 22 23from mcmas import util 24from mcmas.typing import PathType 25 26LOGGER = util.get_logger(__name__) 27 28 29class SpecificationMetadata(pydantic.BaseModel): 30 """ 31 Metadata for this Specification. 32 """ 33 34 file: PathType = Field( 35 description="File associated with this specification", default=None 36 ) 37 engine: str = Field( 38 description="The engine that will be used for this specification", 39 default="mcmas", 40 ) 41 parser: str = Field( 42 description="The parser that will be used for this specification", 43 default="mcmas.parser", 44 ) 45 46 47SpecificationMetadataType = typing.Union[SpecificationMetadata, None] 48 49 50class SpecificationFragment(pydantic.BaseModel): 51 """ 52 A Fragment of a Specification. 53 54 NB: Fragments are incomplete by definition. 55 """ 56 57 logger: typing.ClassVar = LOGGER 58 Metadata: typing.ClassVar = SpecificationMetadata 59 metadata: SpecificationMetadata = Field( 60 description=( 61 "Known metadata about this Specification.\n\n" 62 "(Updated if/when the specification is analyzed or simulated)" 63 ), 64 default=SpecificationMetadata(), 65 exclude=True, 66 ) 67 68 @classmethod 69 def get_trivial(kls, **kwargs): 70 """ 71 Subclassers must implement this. 72 73 Returns the trivial fragment for this type. 74 """ 75 raise NotImplementedError(f"{kls}") 76 77 @classmethod 78 def load_from_source(kls, txt) -> typing.Self: 79 """ 80 Subclassers must implement this. 81 82 Creates spec-fragment from raw source-code. 83 """ 84 raise NotImplementedError(f"{kls}") 85 86 def model_dump_source(self) -> str: 87 """ 88 Subclassers must implement this. 89 90 Dumps raw source-code for this fragment. 91 """ 92 raise NotImplementedError(f"{self}") 93 94 @property 95 def valid(self): 96 """ 97 True if this agent is valid, i.e. ready to run and not a 98 fragment. 99 """ 100 return not any( 101 [self.__class__.__name__ == "SpecificationFragment", self.advice] 102 ) 103 104 concrete = valid 105 106 @property 107 def advice(self) -> list: 108 """ 109 Returns a list of any problems with this object. 110 111 See also `valid` property 112 """ 113 required = getattr(self.__class__, "REQUIRED", []) 114 if not required: 115 LOGGER.warning( 116 f"no attrs listed as required for {self}; advice unavailable" 117 ) 118 return [ 119 f"MISSING-REQUIRED {f} from {self.__class__.__name__}" 120 for f in required 121 if not bool(getattr(self, f)) 122 ] + self.local_advice 123 124 @property 125 def local_advice(self) -> list: 126 """ 127 Subclassers must implement this. 128 """ 129 return [] 130 131 def __add__(self, other): 132 """ 133 Subclassers must implement this. 134 """ 135 raise TypeError(f"Cannot add {self} and {other}") 136 137 def __and__(self, other): 138 """ 139 Subclassers must implement this. 140 """ 141 raise TypeError(f"Cannot `and` {self} and {other}") 142 143 def __or__(self, other): 144 """ 145 Subclassers must implement this. 146 """ 147 raise TypeError(f"Cannot `or` {self} and {other}") 148 149 150Fragment = SpecificationFragment 151 152 153class Specification(SpecificationFragment): 154 """ 155 Pydantic models for a Specification. 156 """ 157 158 159##################### 160from mcmas import typing 161 162 163class AnalysisMeta(pydantic.BaseModel): 164 """ 165 Metadata for a Specification Analysis. 166 """ 167 168 formula_index: int = Field(default=-1) 169 base_complexity: float = Field(default=0.0) 170 nesting_coefficient: int = Field(default=1) 171 raw_score: float = Field(default=0.0) 172 173 174class SymbolMetadata(pydantic.BaseModel): 175 """ 176 Result of running ISPL Analysis. 177 178 This extracts details about symbols, namespaces, etc. 179 """ 180 181 actions: typing.SymbolList2 = Field( 182 default=[], 183 description="Actions list", 184 ) 185 agents: typing.SymbolList2 = Field( 186 default=[], 187 description="Agents list", 188 ) 189 vars: typing.SymbolList2 = Field( 190 default=[], 191 description="Var list", 192 ) 193 194 195######################### 196 197 198class SpecificationAnalysis(pydantic.BaseModel): 199 """ 200 Base class for all Analysis results. 201 """
30class SpecificationMetadata(pydantic.BaseModel): 31 """ 32 Metadata for this Specification. 33 """ 34 35 file: PathType = Field( 36 description="File associated with this specification", default=None 37 ) 38 engine: str = Field( 39 description="The engine that will be used for this specification", 40 default="mcmas", 41 ) 42 parser: str = Field( 43 description="The parser that will be used for this specification", 44 default="mcmas.parser", 45 )
Metadata for this Specification.
51class SpecificationFragment(pydantic.BaseModel): 52 """ 53 A Fragment of a Specification. 54 55 NB: Fragments are incomplete by definition. 56 """ 57 58 logger: typing.ClassVar = LOGGER 59 Metadata: typing.ClassVar = SpecificationMetadata 60 metadata: SpecificationMetadata = Field( 61 description=( 62 "Known metadata about this Specification.\n\n" 63 "(Updated if/when the specification is analyzed or simulated)" 64 ), 65 default=SpecificationMetadata(), 66 exclude=True, 67 ) 68 69 @classmethod 70 def get_trivial(kls, **kwargs): 71 """ 72 Subclassers must implement this. 73 74 Returns the trivial fragment for this type. 75 """ 76 raise NotImplementedError(f"{kls}") 77 78 @classmethod 79 def load_from_source(kls, txt) -> typing.Self: 80 """ 81 Subclassers must implement this. 82 83 Creates spec-fragment from raw source-code. 84 """ 85 raise NotImplementedError(f"{kls}") 86 87 def model_dump_source(self) -> str: 88 """ 89 Subclassers must implement this. 90 91 Dumps raw source-code for this fragment. 92 """ 93 raise NotImplementedError(f"{self}") 94 95 @property 96 def valid(self): 97 """ 98 True if this agent is valid, i.e. ready to run and not a 99 fragment. 100 """ 101 return not any( 102 [self.__class__.__name__ == "SpecificationFragment", self.advice] 103 ) 104 105 concrete = valid 106 107 @property 108 def advice(self) -> list: 109 """ 110 Returns a list of any problems with this object. 111 112 See also `valid` property 113 """ 114 required = getattr(self.__class__, "REQUIRED", []) 115 if not required: 116 LOGGER.warning( 117 f"no attrs listed as required for {self}; advice unavailable" 118 ) 119 return [ 120 f"MISSING-REQUIRED {f} from {self.__class__.__name__}" 121 for f in required 122 if not bool(getattr(self, f)) 123 ] + self.local_advice 124 125 @property 126 def local_advice(self) -> list: 127 """ 128 Subclassers must implement this. 129 """ 130 return [] 131 132 def __add__(self, other): 133 """ 134 Subclassers must implement this. 135 """ 136 raise TypeError(f"Cannot add {self} and {other}") 137 138 def __and__(self, other): 139 """ 140 Subclassers must implement this. 141 """ 142 raise TypeError(f"Cannot `and` {self} and {other}") 143 144 def __or__(self, other): 145 """ 146 Subclassers must implement this. 147 """ 148 raise TypeError(f"Cannot `or` {self} and {other}")
A Fragment of a Specification.
NB: Fragments are incomplete by definition.
69 @classmethod 70 def get_trivial(kls, **kwargs): 71 """ 72 Subclassers must implement this. 73 74 Returns the trivial fragment for this type. 75 """ 76 raise NotImplementedError(f"{kls}")
Subclassers must implement this.
Returns the trivial fragment for this type.
78 @classmethod 79 def load_from_source(kls, txt) -> typing.Self: 80 """ 81 Subclassers must implement this. 82 83 Creates spec-fragment from raw source-code. 84 """ 85 raise NotImplementedError(f"{kls}")
Subclassers must implement this.
Creates spec-fragment from raw source-code.
87 def model_dump_source(self) -> str: 88 """ 89 Subclassers must implement this. 90 91 Dumps raw source-code for this fragment. 92 """ 93 raise NotImplementedError(f"{self}")
Subclassers must implement this.
Dumps raw source-code for this fragment.
95 @property 96 def valid(self): 97 """ 98 True if this agent is valid, i.e. ready to run and not a 99 fragment. 100 """ 101 return not any( 102 [self.__class__.__name__ == "SpecificationFragment", self.advice] 103 )
True if this agent is valid, i.e. ready to run and not a fragment.
95 @property 96 def valid(self): 97 """ 98 True if this agent is valid, i.e. ready to run and not a 99 fragment. 100 """ 101 return not any( 102 [self.__class__.__name__ == "SpecificationFragment", self.advice] 103 )
True if this agent is valid, i.e. ready to run and not a fragment.
107 @property 108 def advice(self) -> list: 109 """ 110 Returns a list of any problems with this object. 111 112 See also `valid` property 113 """ 114 required = getattr(self.__class__, "REQUIRED", []) 115 if not required: 116 LOGGER.warning( 117 f"no attrs listed as required for {self}; advice unavailable" 118 ) 119 return [ 120 f"MISSING-REQUIRED {f} from {self.__class__.__name__}" 121 for f in required 122 if not bool(getattr(self, f)) 123 ] + self.local_advice
Returns a list of any problems with this object.
See also valid property
154class Specification(SpecificationFragment): 155 """ 156 Pydantic models for a Specification. 157 """
Pydantic models for a Specification.
164class AnalysisMeta(pydantic.BaseModel): 165 """ 166 Metadata for a Specification Analysis. 167 """ 168 169 formula_index: int = Field(default=-1) 170 base_complexity: float = Field(default=0.0) 171 nesting_coefficient: int = Field(default=1) 172 raw_score: float = Field(default=0.0)
Metadata for a Specification Analysis.
175class SymbolMetadata(pydantic.BaseModel): 176 """ 177 Result of running ISPL Analysis. 178 179 This extracts details about symbols, namespaces, etc. 180 """ 181 182 actions: typing.SymbolList2 = Field( 183 default=[], 184 description="Actions list", 185 ) 186 agents: typing.SymbolList2 = Field( 187 default=[], 188 description="Agents list", 189 ) 190 vars: typing.SymbolList2 = Field( 191 default=[], 192 description="Var list", 193 )
Result of running ISPL Analysis.
This extracts details about symbols, namespaces, etc.
199class SpecificationAnalysis(pydantic.BaseModel): 200 """ 201 Base class for all Analysis results. 202 """
Base class for all Analysis results.