@@ -188,63 +188,24 @@ def __repr__(self):
188
188
189
189
190
190
class Graph :
191
- def __init__ (self , paths : List = None ):
192
- """Factory for generating graphs from a representation"""
193
- self .slices = [] # only get populated by compute_slices()
191
+ def __init__ (self , paths : Iterable = None ):
194
192
# This can create orphan Nodes with no traversals
195
193
self .nodes = keydefaultdict (lambda key : Node (key , [])) # node id = Node object
196
194
if all (isinstance (x , str ) for x in paths ):
197
195
self .paths = {x : Path (x ) for x in paths }
198
196
elif all (isinstance (x , Path ) for x in paths ):
199
- self .paths = {path .name : path for path in paths }
197
+ self .paths = {path .accession : path for path in paths }
200
198
else :
201
199
self .paths = {}
202
- #TODO: calculate slices?
203
-
204
- @staticmethod
205
- def build (cmd ):
206
- """This factory uses existing slice declarations to build a graph with Paths populated in the order
207
- that they are mentioned in the slices. Currently, this is + only and does not support non-linear
208
- orderings. Use Path.append_node() to build non-linear graphs."""
209
- path_dict = keydefaultdict (lambda key : Path (key )) # construct blank path if new
210
- slices = []
211
- if isinstance (cmd , str ):
212
- cmd = eval (cmd )
213
- for sl in cmd :
214
- current_slice = []
215
- if isinstance (sl , Slice ):
216
- slices .append (sl )
217
- else :
218
- if isinstance (sl [0 ], Node ): # already Nodes, don't need to build
219
- current_slice = sl
220
- else :
221
- try :
222
- for i in range (0 , len (sl ), 2 ):
223
- paths = [path_dict [key ] for key in sl [i + 1 ]]
224
- current_slice .append (Node (sl [i ], paths ))
225
- except IndexError :
226
- raise IndexError ("Expecting two terms: " , sl [0 ]) # sl[i:i+2])
227
-
228
- slices .append (Slice (current_slice ))
229
- return Graph .load_from_slices (slices )
230
-
231
- @classmethod
232
- def load_from_slices (cls , slices ):
233
- graph = cls ([])
234
- graph .slices = slices
235
- return graph
236
200
237
201
def __repr__ (self ):
238
202
"""Warning: the representation strings are very sensitive to whitespace"""
239
- return self .slices .__repr__ ()
240
-
241
- def __getitem__ (self , i ):
242
- return self .slices [i ]
203
+ return self .paths .__repr__ ()
243
204
244
205
def __eq__ (self , representation ):
245
206
if isinstance (representation , Graph ):
246
- return all (slice_a == slice_b for slice_a , slice_b in zip_longest (self .slices , representation .slices ))
247
- return self == Graph . build ( representation ) # build a graph then compare it
207
+ return all (path_a == path_b for path_a , path_b in zip_longest (self .paths , representation .paths ))
208
+ raise TypeError ( "Graphs can only compare with other Graphs" , type ( representation ))
248
209
249
210
def load_from_pickle (self , file : str ):
250
211
self = pickle .load (file )
@@ -274,19 +235,83 @@ def append_node_to_path(self, node_id, strand, path_name):
274
235
raise ValueError ("Provide the id of the node, not" , node_id )
275
236
self .paths [path_name ].append_node (self .nodes [node_id ], strand )
276
237
238
+ def compute_slices (self ):
239
+ """Alias: Upgrades a Graph to a SlicedGraph"""
240
+ return SlicedGraph .from_graph (self )
241
+
242
+
243
+ class SlicedGraph (Graph ):
244
+ def __init__ (self , paths ):
245
+ super (SlicedGraph , self ).__init__ (paths )
246
+ """Factory for generating graphs from a representation"""
247
+ self .slices = [] # only get populated by compute_slices()
248
+
249
+ if not self .slices :
250
+ self .compute_slices ()
251
+
252
+ def __eq__ (self , representation ):
253
+ if isinstance (representation , SlicedGraph ):
254
+ return all (slice_a == slice_b for slice_a , slice_b in zip_longest (self .slices , representation .slices ))
255
+ return self == SlicedGraph .build (representation ) # build a graph then compare it
256
+
257
+ def __repr__ (self ):
258
+ """Warning: the representation strings are very sensitive to whitespace"""
259
+ return self .slices .__repr__ ()
260
+
261
+ def __getitem__ (self , i ):
262
+ return self .slices [i ]
263
+
264
+ @staticmethod
265
+ def from_graph (graph ):
266
+ g = SlicedGraph ([])
267
+ g .paths = graph .paths # shallow copy all relevant fields
268
+ g .nodes = graph .nodes
269
+ g .compute_slices ()
270
+ return g
271
+
277
272
def compute_slices (self ):
278
273
"""TODO: This is a mockup stand in for the real method."""
274
+ if not self .paths : # nothing to do
275
+ return self
279
276
first_path = next (iter (self .paths .values ()))
280
277
for node_traversal in first_path :
281
278
node = node_traversal .node
282
279
self .slices .append (Slice ([node ]))
283
280
return self
284
281
282
+ @staticmethod
283
+ def build (cmd ):
284
+ """This factory uses existing slice declarations to build a graph with Paths populated in the order
285
+ that they are mentioned in the slices. Currently, this is + only and does not support non-linear
286
+ orderings. Use Path.append_node() to build non-linear graphs."""
287
+ if isinstance (cmd , str ):
288
+ cmd = eval (cmd )
289
+ # preemptively grab all the path names from every odd list entry
290
+ paths = {key for sl in cmd for i in range (0 , len (sl ), 2 ) for key in sl [i + 1 ]}
291
+ graph = SlicedGraph (paths )
292
+ for sl in cmd :
293
+ current_slice = []
294
+ if isinstance (sl , Slice ):
295
+ graph .slices .append (sl )
296
+ else :
297
+ if isinstance (sl [0 ], Node ): # already Nodes, don't need to build
298
+ current_slice = sl
299
+ else :
300
+ try :
301
+ for i in range (0 , len (sl ), 2 ):
302
+ paths = [graph .paths [key ] for key in sl [i + 1 ]]
303
+ current_slice .append (Node (sl [i ], paths ))
304
+ except IndexError :
305
+ raise IndexError ("Expecting two terms: " , sl [0 ]) # sl[i:i+2])
285
306
286
- # class SlicedGraph(Graph):
287
- # def __init__(self, paths):
288
- # super(SlicedGraph, self).__init__(paths)
307
+ graph .slices .append (Slice (current_slice ))
308
+ return graph
289
309
310
+ @classmethod
311
+ def load_from_slices (cls , slices , paths ):
312
+ graph = cls (paths )
313
+ graph .slices = slices
314
+ return graph
290
315
291
316
292
317
if __name__ == "__main__" :
0 commit comments