14
14
15
15
from ..thermo .tmodel import ThermoModel
16
16
17
- from ..optim .variables import ReactionVariable , MetaboliteVariable
18
- from ..optim .constraints import ReactionConstraint , MetaboliteConstraint
17
+ from ..optim .variables import ReactionVariable , MetaboliteVariable , ModelVariable
18
+ from ..optim .constraints import ReactionConstraint , MetaboliteConstraint , ModelConstraint
19
19
20
20
from optlang .util import expr_to_json , parse_expr
21
21
22
+ from copy import copy
23
+
22
24
23
25
def get_all_subclasses (cls ):
24
26
all_subclasses = []
@@ -34,10 +36,17 @@ def make_subclasses_dict(cls):
34
36
the_dict [cls .__name__ ] = cls
35
37
return the_dict
36
38
37
- REACTION_VARIABLE_SUBCLASSES = make_subclasses_dict (ReactionVariable )
38
- REACTION_CONSTRAINT_SUBCLASSES = make_subclasses_dict (ReactionConstraint )
39
- METABOLITE_VARIABLE_SUBCLASSES = make_subclasses_dict (MetaboliteVariable )
40
- METABOLITE_CONSTRAINT_SUBCLASSES = make_subclasses_dict (MetaboliteConstraint )
39
+
40
+ MODEL_VARIABLE_SUBCLASSES = make_subclasses_dict (ModelVariable )
41
+ MODEL_CONSTRAINT_SUBCLASSES = make_subclasses_dict (ModelConstraint )
42
+
43
+ BASE_NAME2HOOK = {
44
+ ReactionVariable :'reactions' ,
45
+ ReactionConstraint :'reactions' ,
46
+ MetaboliteVariable :'metabolites' ,
47
+ MetaboliteConstraint :'metabolites' ,
48
+ }
49
+
41
50
42
51
SOLVER_DICT = {
43
52
'optlang.gurobi_interface' :'optlang-gurobi' ,
@@ -212,76 +221,79 @@ def _add_thermo_metabolite_info(met, met_dict):
212
221
if hasattr (met , 'thermo' ):
213
222
met_dict ['thermo' ] = metabolite_thermo_to_dict (met )
214
223
215
- def model_from_dict (obj , solver = None ):
224
+ def model_from_dict (obj , solver = None , custom_hooks = None ):
225
+ """
226
+ Custom_hooks looks like
227
+
228
+ .. code:: python
229
+
230
+ custom_hooks = {<EnzymeVariable Class at 0xffffff> : 'enzymes',
231
+ ... }
232
+
233
+ :param obj:
234
+ :param solver:
235
+ :param custom_hooks:
236
+ :return:
237
+ """
216
238
# Take advantage of cobra's serialization of mets and reactions
217
- new = cbd .model_from_dict (obj )
239
+ cbm = cbd .model_from_dict (obj )
218
240
219
241
if solver is not None :
220
242
try :
221
- new .solver = solver
243
+ cbm .solver = solver
222
244
except SolverNotFound as snf :
223
245
raise snf
224
246
else :
225
247
try :
226
- new .solver = obj ['solver' ]
248
+ cbm .solver = obj ['solver' ]
227
249
except KeyError :
228
250
pass
229
251
252
+ if custom_hooks is None :
253
+ custom_hooks = dict ()
254
+
255
+ custom_hooks .update (BASE_NAME2HOOK )
256
+
230
257
if obj ['kind' ] == 'ThermoModel' :
231
258
new = ThermoModel (thermo_data = obj ['thermo_data' ],
232
- model = new ,
259
+ model = cbm ,
233
260
name = obj ['name' ],
234
261
temperature = obj ['temperature' ],
235
262
min_ph = obj ['min_ph' ],
236
263
max_ph = obj ['max_ph' ])
237
264
new = init_thermo_model_from_dict (new , obj )
265
+ else :
266
+ new = ThermoModel (model = cbm ,
267
+ name = obj ['name' ])
238
268
239
269
new ._push_queue ()
240
270
271
+ name2class , name2hook = add_custom_classes (new ,custom_hooks )
272
+
241
273
for the_var_dict in obj ['variables' ]:
242
274
this_id = the_var_dict ['id' ]
243
275
classname = the_var_dict ['kind' ]
244
276
lb = the_var_dict ['lb' ]
245
277
ub = the_var_dict ['ub' ]
246
278
scaling_factor = the_var_dict ['scaling_factor' ]
247
279
248
- if classname in REACTION_VARIABLE_SUBCLASSES :
249
- hook = new .reactions .get_by_id (this_id )
250
- this_class = REACTION_VARIABLE_SUBCLASSES [classname ]
251
- nv = new .add_variable (kind = this_class ,
252
- hook = hook ,
253
- ub = ub ,
254
- lb = lb ,
255
- queue = True )
256
-
257
- elif classname in METABOLITE_VARIABLE_SUBCLASSES :
258
- hook = new .metabolites .get_by_id (this_id )
259
- this_class = METABOLITE_VARIABLE_SUBCLASSES [classname ]
260
- nv = new .add_variable (kind = this_class ,
261
- hook = hook ,
262
- ub = ub ,
263
- lb = lb ,
264
- queue = True )
265
-
266
- elif classname in ENZYME_VARIABLE_SUBCLASSES :
267
- hook = new .enzymes .get_by_id (this_id )
268
- this_class = ENZYME_VARIABLE_SUBCLASSES [classname ]
280
+ if classname in MODEL_VARIABLE_SUBCLASSES :
281
+ hook = new
282
+ this_class = MODEL_VARIABLE_SUBCLASSES [classname ]
269
283
nv = new .add_variable (kind = this_class ,
270
284
hook = hook ,
271
- ub = ub ,
272
- lb = lb ,
285
+ id_ = this_id ,
286
+ ub = ub ,
287
+ lb = lb ,
273
288
queue = True )
274
-
275
- elif classname in MODEL_VARIABLE_SUBCLASSES :
276
- hook = new
277
- this_class = MODEL_VARIABLE_SUBCLASSES [classname ]
289
+ elif classname in name2class :
290
+ hook = name2hook [classname ].get_by_id (this_id )
291
+ this_class = name2class [classname ]
278
292
nv = new .add_variable (kind = this_class ,
279
293
hook = hook ,
280
- id_ = this_id ,
281
294
ub = ub ,
282
295
lb = lb ,
283
296
queue = True )
284
-
285
297
else :
286
298
raise TypeError (
287
299
'Class {} serialization not handled yet' \
@@ -307,41 +319,24 @@ def model_from_dict(obj, solver=None):
307
319
lb = the_cons_dict ['lb' ]
308
320
ub = the_cons_dict ['ub' ]
309
321
310
- if classname in REACTION_CONSTRAINT_SUBCLASSES :
311
- hook = new .reactions .get_by_id (this_id )
312
- this_class = REACTION_CONSTRAINT_SUBCLASSES [classname ]
313
- nc = new .add_constraint (kind = this_class , hook = hook ,
314
- expr = new_expr ,
315
- ub = ub ,
316
- lb = lb ,
317
- queue = True )
322
+ # Look for the corresponding class:
318
323
319
- elif classname in METABOLITE_CONSTRAINT_SUBCLASSES :
320
- hook = new . metabolites . get_by_id ( this_id )
321
- this_class = METABOLITE_CONSTRAINT_SUBCLASSES [classname ]
324
+ if classname in MODEL_CONSTRAINT_SUBCLASSES :
325
+ hook = new
326
+ this_class = MODEL_CONSTRAINT_SUBCLASSES [classname ]
322
327
nc = new .add_constraint (kind = this_class , hook = hook ,
323
- expr = new_expr ,
328
+ expr = new_expr , id_ = this_id ,
324
329
ub = ub ,
325
330
lb = lb ,
326
331
queue = True )
327
-
328
- elif classname in ENZYME_CONSTRAINT_SUBCLASSES :
329
- hook = new .enzymes .get_by_id (this_id )
330
- this_class = ENZYME_CONSTRAINT_SUBCLASSES [classname ]
332
+ elif classname in name2class :
333
+ hook = name2hook [classname ].get_by_id (this_id )
334
+ this_class = name2class [classname ]
331
335
nc = new .add_constraint (kind = this_class , hook = hook ,
332
336
expr = new_expr ,
333
337
ub = ub ,
334
338
lb = lb ,
335
339
queue = True )
336
-
337
- elif classname in MODEL_CONSTRAINT_SUBCLASSES :
338
- hook = new
339
- this_class = MODEL_CONSTRAINT_SUBCLASSES [classname ]
340
- nc = new .add_constraint (kind = this_class , hook = hook ,
341
- expr = new_expr , id_ = this_id ,
342
- ub = ub ,
343
- lb = lb ,
344
- queue = True )
345
340
else :
346
341
raise TypeError ('Class {} serialization not handled yet' \
347
342
.format (classname ))
@@ -356,6 +351,39 @@ def model_from_dict(obj, solver=None):
356
351
357
352
return new
358
353
354
+ def add_custom_classes (model , custom_hooks ):
355
+ """
356
+ Allows custom variable serialization
357
+
358
+ :param model:
359
+ :param base_classes:
360
+ :param base_hooks:
361
+ :param custom_hooks:
362
+ :return:
363
+ """
364
+
365
+ base_classes = dict ()
366
+ base_hooks = dict ()
367
+
368
+ for this_class , hookname in custom_hooks .items ():
369
+ # Build the subclass dict of the shape
370
+ # {'MyClass':<MyClass Object>}
371
+ this_subclass_dict = make_subclasses_dict (this_class )
372
+ base_classes .update (this_subclass_dict )
373
+
374
+ # Build the Hook dict, of the shape
375
+ # {'MySubClass':model.my_attr}
376
+ hooks = getattr (model ,hookname )
377
+ this_hook_dict = {k :hooks for k in this_subclass_dict }
378
+ base_hooks .update (this_hook_dict )
379
+
380
+ return base_classes , base_hooks
381
+
382
+
383
+ def get_hook_dict (model ,custom_hooks ):
384
+
385
+ return {classname :getattr (model ,hookname )
386
+ for classname , hookname in custom_hooks .items ()}
359
387
360
388
def init_thermo_model_from_dict (new , obj ):
361
389
for rxn_dict in obj ['reactions' ]:
0 commit comments