Coverage for skema/skema_py/acsets.py: 69%
98 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-30 17:15 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-30 17:15 +0000
1from dataclasses import dataclass
2import json
3from typing import List, Dict
5@dataclass(eq=True, frozen=True)
6class Ob:
7 name: str
9class Property:
10 pass
12@dataclass(eq=True, frozen=True)
13class Hom(Property):
14 name: str
15 dom: Ob
16 hom: Ob
18@dataclass(eq=True, frozen=True)
19class AttrType:
20 name: str
21 ty: type
23@dataclass(eq=True, frozen=True)
24class Attr(Property):
25 name: str
26 dom: Ob
27 codom: AttrType
29@dataclass(eq=True, frozen=True)
30class Schema:
31 """Schema for an acset"""
32 obs: List[Ob]
33 homs: List[Hom]
34 attrtypes: List[AttrType]
35 attrs: List[Attr]
37 def props_outof(self, ob: Ob) -> List[Property]:
38 return filter(lambda f: f.dom == ob, self.homs + self.attrs)
40 def from_string(self, s: str):
41 x = next((x for x in self.obs if x.name == s), None)
42 if x != None:
43 return x
44 x = next((x for x in self.homs if x.name == s), None)
45 if x != None:
46 return x
47 x = next((x for x in self.attrtypes if x.name == s), None)
48 if x != None:
49 return x
50 x = next((x for x in self.attrs if x.name == s), None)
51 if x != None:
52 return x
54class ACSet:
55 schema: Schema
56 _parts: Dict[Ob, int]
57 _subparts: Dict[Property, Dict[int, any]]
59 def __init__(self, schema: Schema):
60 self.schema = schema
61 self._parts = { ob:0 for ob in schema.obs }
62 self._subparts = { f:{} for f in schema.homs + schema.attrs }
64 def add_parts(self, ob: Ob, n: int) -> range:
65 assert(ob in self.schema.obs)
66 i = self._parts[ob]
67 self._parts[ob] += n
68 return range(i, i+n)
70 def add_part(self, ob: Ob) -> int:
71 return self.add_parts(ob, 1)[0]
73 def _check_type(self, f: Property, x: any):
74 if f in self.schema.homs:
75 assert(isinstance(x, int))
76 elif f in self.schema.attrs:
77 assert(isinstance(x, f.codom.ty))
78 else:
79 raise(f"{f} not found in schema")
81 def set_subpart(self, i: int, f: Property, x: any):
82 self._check_type(f, x)
83 self._subparts[f][i] = x
85 def subpart(self, i: int, f: Property, oneindex=False):
86 if oneindex and type(f) == Hom:
87 return self._subparts[f][i] + 1
88 else:
89 return self._subparts[f].get(i, None)
91 def nparts(self, ob: Ob) -> int:
92 assert(ob in self.schema.obs)
93 return self._parts[ob]
95 def parts(self, ob: Ob) -> range:
96 return range(0, self.nparts(ob))
98 def incident(self, x: any, f: Property) -> List[int]:
99 self._check_type(f, x)
100 return filter(lambda i: self.subpart(i, f) == x, self.parts(f.dom))
102 def write_json(self):
103 print(self._subparts)
104 return json.dumps({
105 ob.name: [self.prop_dict(ob, i) for i in self.parts(ob)] for ob in self.schema.obs
106 })
108 def prop_dict(self, ob: Ob, i: int) -> Dict[str, any]:
109 return { f.name: self.subpart(i, f, oneindex=True) for f in self.schema.props_outof(ob) }
111 @classmethod
112 def read_json(cls, schema: Schema, s: str):
113 acs = cls(schema)
114 d = json.loads(s)
115 for (obname, proplist) in d.items():
116 ob = schema.from_string(obname)
117 for props in proplist:
118 i = acs.add_part(ob)
119 for (fname,v) in props.items():
120 f = schema.from_string(fname)
121 if type(f) == Hom:
122 acs.set_subpart(i, f, v-1)
123 else:
124 acs.set_subpart(i, f, v)
125 return acs