bigraph-schema 0.0.71__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bigraph-schema might be problematic. Click here for more details.

@@ -0,0 +1,502 @@
1
+ import copy
2
+ from bigraph_schema.utilities import union_keys
3
+ from bigraph_schema import non_schema_keys, is_schema_key
4
+ from bigraph_schema.type_functions import (
5
+ apply_schema, TYPE_SCHEMAS, type_schema_keys, resolve_path)
6
+
7
+ class TypeSystemAdjunct():
8
+ """holds implementations of defunct or not yet ready TypeSystem methods"""
9
+ @staticmethod
10
+ def import_types(type_system, package, strict=False):
11
+ for type_key, type_data in package.items():
12
+ if not (strict and type_system.exists(type_key)):
13
+ type_system.register(
14
+ type_key,
15
+ type_data)
16
+
17
+ @staticmethod
18
+ def define(type_system, method_name, methods):
19
+ method_key = f'_{method_name}'
20
+ for type_key, method in methods.items():
21
+ type_system.register(
22
+ type_key,
23
+ {method_key: method})
24
+
25
+ @staticmethod
26
+ def link_place(type_system, place, link):
27
+ pass
28
+
29
+
30
+ @staticmethod
31
+ def compose(type_system, a, b):
32
+ pass
33
+
34
+
35
+ @staticmethod
36
+ def query(type_system, schema, instance, redex):
37
+ subschema = {}
38
+ return subschema
39
+
40
+ @staticmethod
41
+ def complete(type_system, initial_schema, initial_state):
42
+ full_schema = type_system.access(
43
+ initial_schema)
44
+
45
+ state = type_system.deserialize(
46
+ full_schema,
47
+ initial_state)
48
+
49
+ # fill in the parts of the composition schema
50
+ # determined by the state
51
+ schema, state = type_system.infer_schema(
52
+ full_schema,
53
+ state)
54
+
55
+ final_state = type_system.fill(schema, state)
56
+
57
+ # TODO: add flag to types.access(copy=True)
58
+ return type_system.access(schema), final_state
59
+
60
+ @staticmethod
61
+ def lookup(type_system, type_key, attribute):
62
+ return type_system.access(type_key).get(attribute)
63
+
64
+ @staticmethod
65
+ def resolve_parameters(type_system, type_parameters, schema):
66
+ """
67
+ find the types associated with any type parameters in the schema
68
+ """
69
+
70
+ return {
71
+ type_parameter: type_system.access(
72
+ schema.get(f'_{type_parameter}'))
73
+ for type_parameter in type_parameters}
74
+
75
+ @staticmethod
76
+ def types(type_system):
77
+ return {
78
+ type_key: type_data
79
+ for type_key, type_data in type_system.registry.items()}
80
+
81
+ @staticmethod
82
+ def merge_schemas(type_system, current, update):
83
+ if current == update:
84
+ return update
85
+ if current is None:
86
+ return update
87
+ if update is None:
88
+ return current
89
+ if not isinstance(current, dict):
90
+ return update
91
+ if not isinstance(update, dict):
92
+ return update
93
+
94
+ merged = {}
95
+
96
+ for key in union_keys(current, update):
97
+ if key in current:
98
+ if key in update:
99
+ subcurrent = current[key]
100
+ subupdate = update[key]
101
+ if subcurrent == current or subupdate == update:
102
+ continue
103
+
104
+ merged[key] = type_system.merge_schemas(
105
+ subcurrent,
106
+ subupdate)
107
+ else:
108
+ merged[key] = current[key]
109
+ else:
110
+ merged[key] = update[key]
111
+
112
+ return merged
113
+
114
+
115
+
116
+ # TODO: if its an edge, ensure ports match wires
117
+ # TODO: make this work again, return information about what is wrong
118
+ # with the schema
119
+ @staticmethod
120
+ def validate_state(type_system, original_schema, state):
121
+ schema = type_system.access(original_schema)
122
+ validation = {}
123
+
124
+ if '_serialize' in schema:
125
+ if '_deserialize' not in schema:
126
+ validation = {
127
+ '_deserialize': f'serialize found in type without deserialize: {schema}'
128
+ }
129
+ else:
130
+ serialize = type_system.serialize_registry.access(
131
+ schema['_serialize'])
132
+ deserialize = type_system.deserialize_registry.access(
133
+ schema['_deserialize'])
134
+ serial = serialize(state)
135
+ pass_through = deserialize(serial)
136
+
137
+ if state != pass_through:
138
+ validation = f'state and pass_through are not the same: {serial}'
139
+ else:
140
+ for key, subschema in schema.items():
141
+ if key not in type_schema_keys:
142
+ if key not in state:
143
+ validation[key] = f'key present in schema but not in state: {key}\nschema: {schema}\nstate: {state}\n'
144
+ else:
145
+ subvalidation = type_system.validate_state(
146
+ subschema,
147
+ state[key])
148
+ if not (subvalidation is None or len(subvalidation) == 0):
149
+ validation[key] = subvalidation
150
+
151
+ return validation
152
+
153
+
154
+
155
+ @staticmethod
156
+ def validate(type_system, schema, state):
157
+ # TODO:
158
+ # go through the state using the schema and
159
+ # return information about what doesn't match
160
+
161
+ return {}
162
+
163
+ @staticmethod
164
+ def apply_slice(type_system, schema, state, path, update):
165
+ path = path or ()
166
+ if len(path) == 0:
167
+ result = type_system.apply(
168
+ schema,
169
+ state,
170
+ update)
171
+
172
+ else:
173
+ subschema, substate = type_system.slice(
174
+ schema,
175
+ state,
176
+ path[0])
177
+
178
+ if len(path) == 1:
179
+ subresult = type_system.apply(
180
+ subschema,
181
+ substate,
182
+ update)
183
+
184
+ result = type_system.bind(
185
+ schema,
186
+ state,
187
+ path[1:],
188
+ subschema,
189
+ subresult)
190
+
191
+ else:
192
+ subresult = type_system.apply_slice(
193
+ subschema,
194
+ substate,
195
+ path[1:],
196
+ update)
197
+
198
+ result = state
199
+
200
+ return result
201
+
202
+
203
+ @staticmethod
204
+ def set_update(type_system, schema, state, update):
205
+ if '_apply' in schema:
206
+ apply_function = type_system.apply_registry.access('set')
207
+
208
+ state = apply_function(
209
+ schema,
210
+ state,
211
+ update,
212
+ type_system)
213
+
214
+ elif isinstance(schema, str) or isinstance(schema, list):
215
+ schema = type_system.access(schema)
216
+ state = type_system.set_update(schema, state, update)
217
+
218
+ elif isinstance(update, dict):
219
+ for key, branch in update.items():
220
+ if key not in schema:
221
+ raise Exception(
222
+ f'trying to update a key that is not in the schema'
223
+ f'for state: {key}\n{state}\nwith schema:\n{schema}')
224
+ else:
225
+ subupdate = type_system.set_update(
226
+ schema[key],
227
+ state[key],
228
+ branch)
229
+
230
+ state[key] = subupdate
231
+ else:
232
+ raise Exception(
233
+ f'trying to apply update\n {update}\nto state\n {state}\n'
234
+ f'with schema\n{schema}, but the update is not a dict')
235
+
236
+ return state
237
+
238
+
239
+ @staticmethod
240
+ def set(type_system, original_schema, initial, update):
241
+ schema = type_system.access(original_schema)
242
+ state = copy.deepcopy(initial)
243
+
244
+ return type_system.set_update(schema, state, update)
245
+
246
+ @staticmethod
247
+ def fill_ports(type_system, interface, wires=None, state=None,
248
+ top_schema=None, top_state=None, path=None):
249
+ # deal with wires
250
+ if wires is None:
251
+ wires = {}
252
+ if state is None:
253
+ state = {}
254
+ if top_schema is None:
255
+ top_schema = schema
256
+ if top_state is None:
257
+ top_state = state
258
+ if path is None:
259
+ path = []
260
+
261
+ if isinstance(interface, str):
262
+ interface = {'_type': interface}
263
+
264
+ for port_key, subwires in wires.items():
265
+ if port_key in interface:
266
+ port_schema = interface[port_key]
267
+ else:
268
+ port_schema, subwires = type_system.slice(
269
+ interface,
270
+ wires,
271
+ port_key)
272
+
273
+ if isinstance(subwires, dict):
274
+ if isinstance(state, dict):
275
+ state = type_system.fill_ports(
276
+ port_schema,
277
+ wires=subwires,
278
+ state=state,
279
+ top_schema=top_schema,
280
+ top_state=top_state,
281
+ path=path)
282
+
283
+ else:
284
+ if isinstance(subwires, str):
285
+ subwires = [subwires]
286
+
287
+ subschema, substate = type_system.set_slice(
288
+ top_schema,
289
+ top_state,
290
+ path[:-1] + subwires,
291
+ port_schema,
292
+ type_system.default(port_schema),
293
+ defer=True)
294
+
295
+ return state
296
+
297
+ # def infer_wires(self, ports, state,
298
+ # wires, top_schema=None,
299
+ # top_state=None, path=None, internal_path=None):
300
+ @staticmethod
301
+ def infer_wires(type_system, ports, wires, top_schema=None, top_state=None,
302
+ path=None, internal_path=None):
303
+ top_schema = top_schema or {}
304
+ top_state = top_state or state
305
+ path = path or ()
306
+ internal_path = internal_path or ()
307
+
308
+ if isinstance(ports, str):
309
+ ports = type_system.access(ports)
310
+
311
+ if isinstance(wires, (list, tuple)):
312
+ if len(wires) == 0:
313
+ destination_schema, destination_state = top_schema, top_state
314
+
315
+ else:
316
+ destination_schema, destination_state = type_system.slice(
317
+ top_schema,
318
+ top_state,
319
+ path[:-1] + wires)
320
+
321
+ merged_schema = apply_schema(
322
+ 'schema',
323
+ destination_schema,
324
+ ports,
325
+ type_system)
326
+
327
+ merged_state = type_system.complete(
328
+ merged_schema,
329
+ destination_state)
330
+
331
+ else:
332
+ for port_key, port_wires in wires.items():
333
+ subschema, substate = type_system.slice(
334
+ ports,
335
+ {},
336
+ port_key)
337
+
338
+ if isinstance(port_wires, dict):
339
+ top_schema, top_state = type_system.infer_wires(
340
+ subschema,
341
+ # substate,
342
+ port_wires,
343
+ top_schema=top_schema,
344
+ top_state=top_state,
345
+ path=path,
346
+ internal_path=internal_path+(port_key,))
347
+
348
+ # port_wires must be a list
349
+ elif len(port_wires) == 0:
350
+ raise Exception(f'no wires at port "{port_key}" in ports {ports} with state {state}')
351
+
352
+ else:
353
+ compound_path = resolve_path(
354
+ path[:-1] + tuple(port_wires))
355
+
356
+ compound_schema, compound_state = type_system.set_slice(
357
+ {}, {},
358
+ compound_path,
359
+ subschema or 'any',
360
+ type_system.default(subschema))
361
+
362
+ top_schema = type_system.resolve(
363
+ top_schema,
364
+ compound_schema)
365
+
366
+ top_state = type_system.merge_recur(
367
+ top_schema,
368
+ compound_state,
369
+ top_state)
370
+
371
+ return top_schema, top_state
372
+
373
+
374
+ @staticmethod
375
+ def infer_edge(type_system, schema, state,
376
+ top_schema=None, top_state=None, path=None):
377
+ '''
378
+ given the schema and state for this edge, and its path relative to
379
+ the top_schema and top_state, make all the necessary completions to
380
+ both the schema and the state according to the input and output schemas
381
+ of this edge in '_inputs' and '_outputs', along the wires in its state
382
+ under 'inputs' and 'outputs'.
383
+
384
+ returns the top_schema and top_state, even if the edge is deeply embedded,
385
+ as the particular wires could have implications anywhere in the tree.
386
+ '''
387
+
388
+ schema = schema or {}
389
+ top_schema = top_schema or schema
390
+ top_state = top_state or state
391
+ path = path or ()
392
+
393
+ if type_system.check('edge', state):
394
+ for port_key in ['inputs', 'outputs']:
395
+ ports = state.get(port_key)
396
+ schema_key = f'_{port_key}'
397
+ port_schema = schema.get(schema_key, {})
398
+ state_schema = state.get(schema_key, {})
399
+
400
+ schema[schema_key] = type_system.resolve(
401
+ port_schema,
402
+ type_system.access(
403
+ state_schema))
404
+
405
+ if ports:
406
+ top_schema, top_state = type_system.infer_wires(
407
+ schema[schema_key],
408
+ # state,
409
+ ports,
410
+ top_schema=top_schema,
411
+ top_state=top_state,
412
+ path=path)
413
+
414
+ return top_schema, top_state
415
+
416
+
417
+ @staticmethod
418
+ def infer_schema(type_system, schema, state,
419
+ top_schema=None, top_state=None, path=None):
420
+ """
421
+ Given a schema fragment and an existing state with _type keys,
422
+ return the full schema required to describe that state,
423
+ and whatever state was hydrated (edges) during this process
424
+
425
+ """
426
+
427
+ # during recursive call, schema is kept at the top level and the
428
+ # path is used to access it (!)
429
+
430
+ schema = schema or {}
431
+ top_schema = top_schema or schema
432
+ top_state = top_state or state
433
+ path = path or ()
434
+
435
+ if isinstance(state, dict):
436
+ state_schema = None
437
+ if '_type' in state:
438
+ state_type = {
439
+ key: value
440
+ for key, value in state.items()
441
+ if is_schema_key(key)}
442
+
443
+ schema = type_system.resolve(
444
+ schema,
445
+ state_type)
446
+
447
+ if '_type' in schema:
448
+ hydrated_state = type_system.deserialize(
449
+ schema,
450
+ state)
451
+
452
+ top_schema, top_state = type_system.set_slice(
453
+ top_schema,
454
+ top_state,
455
+ path,
456
+ schema,
457
+ hydrated_state)
458
+
459
+ top_schema, top_state = type_system.infer_edge(
460
+ schema,
461
+ hydrated_state,
462
+ top_schema,
463
+ top_state,
464
+ path)
465
+
466
+ else:
467
+ for key in state:
468
+ inner_path = path + (key,)
469
+ inner_schema, inner_state = type_system.slice(
470
+ schema,
471
+ state,
472
+ key)
473
+
474
+ top_schema, top_state = type_system.infer_schema(
475
+ inner_schema,
476
+ inner_state,
477
+ top_schema=top_schema,
478
+ top_state=top_state,
479
+ path=inner_path)
480
+
481
+ elif isinstance(state, str):
482
+ pass
483
+
484
+ else:
485
+ type_schema = TYPE_SCHEMAS.get(
486
+ type(state).__name__,
487
+ 'any')
488
+
489
+ top_schema, top_state = type_system.set_slice(
490
+ top_schema,
491
+ top_state,
492
+ path,
493
+ type_schema,
494
+ state)
495
+
496
+ return top_schema, top_state
497
+
498
+ @staticmethod
499
+ def hydrate(type_system, schema, state):
500
+ hydrated = type_system.deserialize(schema, state)
501
+ return type_system.fill(schema, hydrated)
502
+
@@ -0,0 +1,133 @@
1
+ """
2
+ =====
3
+ Units
4
+ =====
5
+
6
+ Register all of the unit types from the pint unit registry
7
+ """
8
+
9
+ from pint import UnitRegistry
10
+
11
+
12
+ units = UnitRegistry()
13
+
14
+
15
+ def render_coefficient(original_power):
16
+ power = abs(original_power)
17
+ int_part = int(power)
18
+ root_part = power % 1
19
+
20
+ if root_part != 0.0:
21
+ render = str(root_part)[2:]
22
+ render = f'{int_part}_{render}'
23
+ else:
24
+ render = str(int_part)
25
+
26
+ return render
27
+
28
+
29
+ def render_units_type(dimensionality):
30
+ unit_keys = list(dimensionality.keys())
31
+ unit_keys.sort()
32
+
33
+ numerator = []
34
+ denominator = []
35
+
36
+ for unit_key in unit_keys:
37
+ inner_key = unit_key.strip('[]')
38
+ power = dimensionality[unit_key]
39
+ negative = False
40
+
41
+ if power < 0:
42
+ negative = True
43
+ power = -power
44
+
45
+ if power == 1:
46
+ render = inner_key
47
+ else:
48
+ render = f'{inner_key}^{render_coefficient(power)}'
49
+
50
+ if negative:
51
+ denominator.append(render)
52
+ else:
53
+ numerator.append(render)
54
+
55
+ render = '*'.join(numerator)
56
+ if len(denominator) > 0:
57
+ render_denominator = '*'.join(denominator)
58
+ render = f'{render}/{render_denominator}'
59
+
60
+ return render
61
+
62
+
63
+ def parse_coefficient(s):
64
+ if s is None:
65
+ return 1
66
+ elif '_' in s:
67
+ parts = s.split('_')
68
+ if len(parts) > 1:
69
+ base, residue = parts
70
+ return int(base) + (float(residue) / 10.0)
71
+ else:
72
+ return int(parts)
73
+ else:
74
+ return int(s)
75
+
76
+
77
+ def parse_dimensionality(s):
78
+ numerator, denominator = s.split('/')
79
+ numerator_terms = numerator.split('*')
80
+ denominator_terms = denominator.split('*')
81
+
82
+ dimensionality = {}
83
+
84
+ for term in numerator_terms:
85
+ base = term.split('^')
86
+ exponent = None
87
+ if len(base) > 1:
88
+ exponent = base[1]
89
+ dimensionality[f'[{base[0]}]'] = parse_coefficient(exponent)
90
+
91
+ for term in denominator_terms:
92
+ power = term.split('^')
93
+ exponent = None
94
+ if len(power) > 1:
95
+ exponent = power[1]
96
+ dimensionality[f'[{power[0]}]'] = -parse_coefficient(exponent)
97
+
98
+ return dimensionality
99
+
100
+
101
+ def test_units_render():
102
+ dimensionality = units.newton.dimensionality
103
+ render = render_units_type(dimensionality)
104
+ recover = parse_dimensionality(render)
105
+
106
+ print(f'original: {dimensionality}')
107
+ print(f'render: {render}')
108
+ print(f'parsed: {recover}')
109
+
110
+ assert render == 'length*mass/time^2'
111
+ assert recover == dimensionality
112
+
113
+
114
+ def test_roots_cycle():
115
+ dimensionality = {
116
+ '[length]': 1.5,
117
+ '[time]': 3,
118
+ '[mass]': -2.5,
119
+ }
120
+ render = render_units_type(dimensionality)
121
+ recover = parse_dimensionality(render)
122
+
123
+ print(f'original: {dimensionality}')
124
+ print(f'render: {render}')
125
+ print(f'parsed: {recover}')
126
+
127
+ assert render == 'length^1_5*time^3/mass^2_5'
128
+ assert recover == dimensionality
129
+
130
+
131
+ if __name__ == '__main__':
132
+ test_units_render()
133
+ test_roots_cycle()