Module transdisrupt
[hide private]
[frames] | no frames]

Source Code for Module transdisrupt

  1  #!/usr/bin/env python 
  2   
  3  # $Id: transdisrupt.py 115 2006-05-11 19:42:20Z jtk $ 
  4   
  5  # $Log$ 
  6  # Revision 1.6  2005/10/28 16:29:59  jtk 
  7  # added CVS tags 
  8  # 
  9   
 10   
 11  import string 
 12  import copy 
 13  import math 
 14  import transsys 
 15  import types 
 16  import sys 
 17   
18 -class ErrorLogger :
19
20 - def __init__(self, fname = None, vlevel = 0) :
21 """might still need some modification... 22 The higher the level the more gets printed. 23 """ 24 self.errorfilename = fname 25 if fname is None : 26 self.errfile = sys.stderr 27 else : 28 self.errfile = open(fname, 'w') 29 self.vlevel = vlevel 30 self.errors=0
31 32
33 - def message(self, msg, level = 0) :
34 """print errormessage to screen or file fname, if it has lower or equall verbositylevel than vlevel. 35 """ 36 if level < self.vlevel: 37 self.errors=self.errors + 1 38 if self.vlevel >= level : 39 self.errfile.write('%s\n' % msg) 40 self.errfile.flush()
41 42
43 -def uniform_cmp(element1, element2) :
44 if not isinstance(element1, int) : 45 raise StandardError, 'uniform_cmp:: type of element1 is not INT' 46 if not isinstance(element2, int) : 47 raise StandardError, 'uniform_cmp:: type of element2 is not INT' 48 if element1 == element2 : 49 if element1 == 1 : 50 return 1 51 return 0
52 53
54 -class ExpressionSimilarityMatrix :
55 """The ExpressionSimilarityMatrix is basically an implementation of a matrix, that is suited for our purposes. This means that entries can easily be extracted by factornames instead of indices. 56 """ 57
58 - def __init__(self, name, gf_dict, default_element = 0.0) :
59 """Constructor. Initialize ExpressionSimilarityMatrix with all factors given as values in the dictionary gf_dict. When an error is raised the network has factors that are encoded by multiple genes. this functionality is not (yet) implemented. 60 """ 61 self.name = name 62 self.matrix = [] 63 self.gf_dict = gf_dict 64 self.factor_list = [] 65 for fn in self.gf_dict.values() : 66 if fn in self.factor_list : 67 raise StandardError, 'multiple genes encode same factor' 68 self.factor_list.append(fn) 69 self.factor_list.sort() 70 self.fill_all(default_element)
71 72
73 - def __str__(self, factor_fmt = '%10s', element_fmt = ' %7.3f', none_symbol = ' None ') :
74 """Prints the matrix. 75 """ 76 s = '' 77 flist = self.get_fnamelist() 78 for i in xrange(len(flist)) : 79 s = s + (factor_fmt % flist[i]) 80 glue = ': ' 81 for j in xrange(len(flist)) : 82 s = s + glue 83 if self.matrix[i][j] is None: 84 s = s + none_symbol 85 else : 86 s = s + (element_fmt % float(self.matrix[i][j])) 87 glue = ' ' 88 s = s + '\n' 89 return s
90 91
92 - def gnu_comparison(self, other, cmp_function) :
93 """this function seems to be half-implemented -- it does not do anything 94 reasonable or useful""" 95 raise StandardError, 'ExpressionSimilarityMatrix::gnu_comparison: unimplemented' 96 if self.factor_list != other.factor_list : 97 raise StandardError, 'ExpressionSimilarityMatrix.gnu_comparison:: matrices have not got the same factor_list' 98 for factor1 in self.factor_list : 99 for factor2 in self.factor_list : 100 element1 = self.get_element(factor1, factor2) 101 element2 = other.get_element(factor1, factor2) 102 if cmp_function(element1, element2) == 1 : 103 pass
104 105
106 - def gene_factor_dictionary(self):
107 """Returns the gene-factor dictionary. 108 """ 109 return self.gf_dict
110 111
112 - def factor_gene_dictionary(self) :
113 fg_dict = {} 114 for k in self.gf_dict.keys() : 115 v = self.gf_dict[k] 116 if v in fg_dict.keys() : 117 raise StandardError, 'ExpressionSimilarityMatrix.factor_gene_dictionary: multiple genes encode factor "%s"' % v 118 fg_dict[v] = k 119 return fg_dict
120 121
122 - def return_as_list(self) :
123 """Returns the matrix as a list where rows follow each other. 124 """ 125 matrix2list = [] 126 flist = self.get_fnamelist() 127 for i in flist : 128 for j in flist : 129 matrix2list.append(self.get_element(i,j)) 130 return matrix2list
131 132
133 - def fill_all(self, v) :
134 """Writes v in each entry of the matrix. 135 """ 136 flist = self.get_fnamelist() 137 self.matrix = [] 138 for i in xrange(len(flist)) : 139 row = [v] * len(flist) 140 self.matrix.append(row)
141 142
143 - def get_index(self, fname) :
144 """Returns the index of the factor fname in the membervariable factor_list owned by the matrix. 145 """ 146 if self.factor_list.count(fname) != 1 : 147 raise StandardError, 'factor "%s" not in matrix' % fname 148 return self.factor_list.index(fname)
149 150
151 - def get_fname(self, gname) :
152 """Returns the product of the gene gname. 153 """ 154 return self.gf_dict[gname]
155 156
157 - def get_element(self, f1, f2) :
158 """Returns the entry of the matrix associated with the factors f1 and f2. 159 """ 160 i = self.get_index(f1) 161 j = self.get_index(f2) 162 return self.matrix[i][j]
163 164
165 - def set_element(self, f1, f2, element) :
166 """Fill entry of the matrix associated with the factors f1, f2 with element. 167 """ 168 i = self.get_index(f1) 169 j = self.get_index(f2) 170 self.matrix[i][j] = element
171 172
173 - def get_gnamelist(self) :
174 """Returns the gene names in a list. 175 """ 176 return self.gf_dict.keys()
177 178
179 - def get_fnamelist(self) :
180 """Returns the factor names in a list. 181 """ 182 return self.factor_list
183 184
185 - def replace_none(self, element) :
186 flist = len(self.get_fnamelist()) 187 for i in xrange(flist) : 188 for j in xrange(flist) : 189 if self.matrix[i][j] is None : 190 self.matrix[i][j] = element
191 192
193 -class KnockoutTranssysProgram(transsys.TranssysProgram) :
194 """KnockoutTranssysProgram 195 """ 196
197 - def __init__(self, tp, nonfunc_name = "nonfunc") :
198 """docu missing 199 """ 200 self.basename = tp.name 201 self.name = tp.name 202 self.factor_list = tp.factor_list[:] 203 self.gene_list = tp.gene_list[:] 204 self.comments = tp.comments 205 self.nonfunc_name = nonfunc_name 206 self.add_nonfunctional_factor() 207 self.functional_factor = None 208 self.knockout_index = None
209 210
211 - def add_nonfunctional_factor(self) :
212 """docu missing 213 """ 214 # FIXME: should check for existence of nonfunc factor somewhere 215 # cbouyio adition: The decay and diffusibility expressions should be 216 # declared explicity as ExpressionNodeValue instances. (decay and 217 # diffusibility have been set to one). 218 # self.nonfunc = transsys.Factor(self.nonfunc_name) 219 # FIXME: default decay / diffusibility values 220 self.nonfunc = transsys.Factor(self.nonfunc_name, transsys.ExpressionNodeValue(1), transsys.ExpressionNodeValue(1)) 221 self.factor_list.append(self.nonfunc)
222 223
224 - def do_knockout(self, knockout_index) :
225 """docu missing 226 """ 227 if self.knockout_index is not None : 228 raise StandardError, 'multiple knockout backtrace not (yet) implemented' 229 self.name = '%s_ko_%d' % (self.basename, knockout_index) 230 self.knockout_index = knockout_index 231 self.functional_factor = self.gene_list[knockout_index].product 232 self.gene_list[knockout_index].product = self.nonfunc
233 234
235 - def undo_knockout(self) :
236 """docu missing 237 """ 238 if self.knockout_index is None : 239 raise StandardError, 'no knockout done which could be undone' 240 self.name = self.basename 241 self.gene_list[self.knockout_index].product = self.functional_factor 242 self.knockout_index = None 243 self.functional_factor = None
244 245 253 254
255 -def score_matrix(network, promoter_element_scorefunc) :
256 """compute a distance adjacency matrix in which entries connecting 257 a regulator to a cognate regulatee contain a score value computed by 258 the scorefunc on the promoter element. All other entries are None. 259 """ 260 gf_dict = gene_factor_dictionary(network) 261 adj_matrix = ExpressionSimilarityMatrix('adj_matrix', gf_dict) 262 fg_dict = adj_matrix.factor_gene_dictionary() 263 adj_matrix.fill_all(None) 264 for gene_i in gf_dict.keys(): 265 fi = gf_dict[gene_i] 266 gene = network.find_gene(gene_i) 267 for p in gene.promoter : 268 if isinstance(p, transsys.PromoterElementLink) : 269 for f in p.factor_list : 270 fj = f.name 271 # 272 # FIXME ??? what's wrong here? 273 if adj_matrix.get_element(fj, fi) is not None : 274 # perhaps, the bug was relying on this global ErrorLogger instance...? 275 # errlogger.message('score_matrix_error: multiple links %s --> %s' % (fj, fi), 0) 276 raise StandardError, 'score_matrix: multiple links %s --> %s' % (fj, fi) 277 # 278 # 279 adj_matrix.set_element(fj, fi, promoter_element_scorefunc(p)) 280 return adj_matrix
281 282
283 -def pathscore_matrix(tp, scorefunc, no_connection_default_value) :
284 """get cost matrix of a transsys network. Calculation rules are implemented in a generic score_function(). 285 """ 286 gf_dict = gene_factor_dictionary(tp) 287 scoreMatrix = score_matrix(tp, scorefunc) 288 scoreMatrix.name = 'scoreMatrix' 289 # obviously distance from gene i to itself is zero 290 for i in gf_dict.keys() : 291 scoreMatrix.set_element(gf_dict[i], gf_dict[i], 0) 292 for n in gf_dict.keys() : 293 for i in gf_dict.keys() : 294 for j in gf_dict.keys() : 295 s1 = scoreMatrix.get_element(gf_dict[i], gf_dict[j]) 296 s2 = scoreMatrix.get_element(gf_dict[i], gf_dict[n]) 297 s3 = scoreMatrix.get_element(gf_dict[n], gf_dict[j]) 298 if s1 is not None : 299 if s2 is not None : 300 if s3 is not None : 301 if s1 > s2 + s3 : 302 scoreMatrix.set_element(gf_dict[i], gf_dict[j], s2 + s3) 303 else : 304 scoreMatrix.set_element(gf_dict[i], gf_dict[j], s1) 305 else : 306 scoreMatrix.set_element(gf_dict[i], gf_dict[j], s1) 307 else : 308 if s2 is not None : 309 if s3 is not None : 310 scoreMatrix.set_element(gf_dict[i], gf_dict[j], s2 + s3) 311 scoreMatrix.replace_none(no_connection_default_value) 312 return scoreMatrix
313 314 315 # -1 := activating 316 # +1 := repressing 317 # must be consistent with matrix D !!!
318 -def adjacency_matrix_scorefunc(promoter) :
319 if isinstance(promoter, transsys.PromoterElementLink) : 320 if isinstance(promoter, transsys.PromoterElementActivate) : 321 return -1 322 elif isinstance(promoter, transsys.PromoterElementRepress) : 323 return 1 324 else : 325 # errlogger.message('adjacency_error: unknown PromoterElementLink subclass "%s"' % promoter.__class__.__name__ , 0) 326 raise StandardError, 'adjacency_matrix_scorefunc: unknown PromoterElementLink subclass "%s"' % promoter.__class__.__name__ 327 else : 328 # errlogger.message('adjacency_error: unknown PromoterElement class "%s"' % promoter.__class__.__name__ , 0) 329 raise StandardError, 'adjacency_matrix_scorefunc: unknown PromoterElement class "%s"' % promoter.__class__.__name__
330 331
332 -def adjacency_matrix(network) :
333 adj_matrix = score_matrix(network, adjacency_matrix_scorefunc) 334 adj_matrix.replace_none(0) 335 return adj_matrix
336 337
338 -def gene_factor_dictionary(tp) :
339 """make a dictionary, where gene is the key and the product is the value, for easy and convenient access. It also provides a more understandable code and prevents errors, which could occur when using indices. 340 """ 341 d = {} 342 for gene in tp.gene_list : 343 d[gene.name] = gene.product.name 344 return d
345 346
347 -def identity_perturber(c, fn) :
348 """docu missing 349 """ 350 return c
351 352 353 #FIXME: a_spec, a_max is both used for activation and repression
354 -def create_disruption_network(name, matrix_D, gene_order, decay_rate, a_spec, a_max, constit) :
355 """docu missing 356 """ 357 factor_list = [] 358 gene_list = [] 359 for gname in gene_order : 360 fname = matrix_D.get_fname(gname) 361 factor_list.append(transsys.Factor(fname, transsys.ExpressionNodeValue(decay_rate))) 362 for gname in gene_order : 363 fname = matrix_D.get_fname(gname) 364 temp_promoter_list = [] 365 for reg_gname in gene_order : 366 reg_fname = matrix_D.get_fname(reg_gname) 367 if matrix_D.get_element(reg_fname, fname) == -1 : 368 temp_promoter_list.append(transsys.PromoterElementActivate(transsys.ExpressionNodeValue(a_spec), transsys.ExpressionNodeValue(a_max), [reg_fname])) 369 if matrix_D.get_element(reg_fname, fname) == 1 : 370 temp_promoter_list.append(transsys.PromoterElementRepress(transsys.ExpressionNodeValue(a_spec), transsys.ExpressionNodeValue(a_max), [reg_fname])) 371 temp_promoter_list.append(transsys.PromoterElementConstitutive(transsys.ExpressionNodeValue(constit))) 372 gene_list.append(transsys.Gene(gname, fname, temp_promoter_list)) 373 disruption_network = transsys.TranssysProgram(name, factor_list, gene_list) 374 return disruption_network
375 376 377 # matrix_D: 378 # --------- 379 # dim(matrix_D) = n x n 380 # 381 # value_k e {-1, 0, 1} 382 # list_k = [value_0, .., value_n] 383 # matrix_D = [[list_0], .., [list_n]] 384 # 385 # column_i of matrix_D = list_i 386 # row_j of matrix_D = list_1[j], .., list_n[j] 387
388 -def reconstructed_adjacency_matrix(matrix_E, gamma, sigma) :
389 """docu missing 390 """ 391 # initialize matrix D 392 if sigma is None : 393 sigma = {} 394 for fname in matrix_E.get_fnamelist() : 395 sigma[fname] = 1.0 396 if len(sigma) != len(matrix_E.get_fnamelist()) : 397 # errlogger.message('reconstructed_adjacency_matrix: sigma_error: invalid size ',errorlogger_verbositylevel) 398 raise StandardError, 'matrix_D: invalid size of vector sigma' 399 matrix_D = copy.deepcopy(matrix_E) 400 matrix_D.name = 'matrix_D' 401 # build matrix D from matrix E 402 for fname in matrix_D.get_fnamelist() : 403 for reg_fname in matrix_D.get_fnamelist() : 404 # "normalize" 405 x = matrix_E.get_element(fname, reg_fname) / sigma[fname] 406 # d_ij = ... 407 # ignore selfregulation, meaning edges with in/out gene is the same 408 if fname == reg_fname : 409 matrix_D.set_element(fname, reg_fname, 0) 410 # gene is activating 411 elif x <= -gamma : 412 matrix_D.set_element(fname, reg_fname, -1) 413 # gene is repressing 414 elif x >= gamma : 415 matrix_D.set_element(fname, reg_fname, 1) 416 # gene is not regulating 417 else : 418 matrix_D.set_element(fname, reg_fname, 0) 419 return matrix_D
420 421 422 #FIXME: i want to be an array not a matrix anymore...! 423 ####
424 -def referencestate_concentration_matrix(network, timesteps) :
425 """docu missing 426 """ 427 gfd = gene_factor_dictionary(network) 428 ti = transsys.TranssysInstance(network) 429 tseries = ti.time_series(timesteps, timesteps - 1) 430 reference_state = tseries[max(tseries.keys())] 431 ref_conc_matrix = ExpressionSimilarityMatrix('ref_conc_matrix', gfd ) 432 for fi in network.factor_names() : 433 for fj in network.factor_names() : 434 ref_conc_matrix.set_element(fj, fi, reference_state.factor_concentration[network.find_factor_index(fi)]) 435 return ref_conc_matrix
436 437 438 #### 439 #### FIXME: see referencestate_concentration_matrix
440 -def knockout_concentration_matrix(knockout_network, ref_conc_matrix, timesteps) :
441 """compute a matrix of expression values obtained from single gene 442 knockout networks. The matrix element m[i][j] contains the expression 443 level of factor j observed in a network in which factor i is knocked out 444 (by replacement with a nonfunc product in the encoding gene). Expression 445 starts with the reference state, taken from ref_conc_matrix, and is 446 carried out for timesteps timesteps. 447 """ 448 gfd_wildtype = gene_factor_dictionary(knockout_network) 449 conc_matrix = ExpressionSimilarityMatrix('conc_matrix', gfd_wildtype) 450 ref_state = transsys.TranssysInstance(knockout_network) 451 wildtype_factor_names = knockout_network.factor_names()[:] 452 wildtype_factor_names.remove(knockout_network.nonfunc_name) 453 for factor in wildtype_factor_names : 454 ref_state.factor_concentration[knockout_network.find_factor_index(factor)] = ref_conc_matrix.get_element(factor, factor) 455 # make 'experiments' 456 for gi in knockout_network.gene_names() : 457 # mutate gene i 458 knockout_network.do_knockout(knockout_network.find_gene_index(gi)) 459 initial_state = ref_state.perturbed_copy(identity_perturber) 460 initial_state.transsys_program = knockout_network 461 m_tseries = initial_state.time_series(timesteps, timesteps - 1) 462 this_mutant = m_tseries[max(m_tseries.keys())] 463 for fi in wildtype_factor_names : 464 # expression of fi is affected by knockout of gi as quantified by concentration of fi 465 conc_matrix.set_element(gfd_wildtype[gi], fi, this_mutant.factor_concentration[knockout_network.find_factor_index(fi)]) 466 # demutate gene i 467 knockout_network.undo_knockout() 468 return conc_matrix
469 470
471 -def knockout_concentration_matrix_lsys(knockout_network, aux_network, lsys_lines, timesteps) :
472 """docu missing 473 """ 474 # gfd_wildtype contains only the wildtype genes because 475 # KnockoutTranssysProgram has only one factor added, genes are 476 # unchanged (unless in knockout state, which we don't expect at the start). 477 gfd_wildtype = gene_factor_dictionary(knockout_network) 478 conc_matrix = ExpressionSimilarityMatrix('conc_matrix', gfd_wildtype) 479 wildtype_factor_names = knockout_network.factor_names()[:] 480 wildtype_factor_names.remove(knockout_network.nonfunc_name) 481 # make 'experiments' 482 for gi in knockout_network.gene_names() : 483 # mutate gene i 484 # sys.stderr.write('knocking out gene "%s"\n' % gi) 485 knockout_network.do_knockout(knockout_network.find_gene_index(gi)) 486 if aux_network is None : 487 merged_network = knockout_network 488 else : 489 merged_network = copy.deepcopy(aux_network) 490 merged_network.merge(knockout_network) 491 # print merged_network 492 initial_state = transsys.TranssysInstance(merged_network) 493 m_tseries = initial_state.time_series(timesteps, timesteps - 1, lsys_lines) 494 this_mutant = m_tseries[max(m_tseries.keys())] 495 for fi in wildtype_factor_names : 496 # expression of fi is affected by knockout of gi as quantified by concentration of fi 497 i = merged_network.find_factor_index(fi) 498 c = this_mutant.factor_concentration[i] 499 # sys.stderr.write(' [%s] (index %d) is now %g\n' % (fi, i, c)) 500 conc_matrix.set_element(gfd_wildtype[gi], fi, c) 501 # demutate gene i 502 knockout_network.undo_knockout() 503 return conc_matrix
504 505 506 #### 507 ####
508 -def logratio_matrix(ref_conc_matrix, conc_matrix) :
509 """docu missing 510 """ 511 overflows = 0 512 log2 = math.log(2) 513 # initializing logratio matrix (matrix E in paper) 514 matrix_E = ExpressionSimilarityMatrix('matrix_E', conc_matrix.gene_factor_dictionary()) 515 for fi in conc_matrix.get_fnamelist() : 516 for fj in conc_matrix.get_fnamelist() : 517 # FIXME: no selfregulation yet 518 if fi == fj : 519 matrix_E.set_element(fi, fj, 0.0) 520 elif ((ref_conc_matrix.get_element(fi, fj) == 0.0) and (conc_matrix.get_element(fi, fj) == 0.0)) : 521 matrix_E.set_element(fi, fj, 0.0) 522 elif (ref_conc_matrix.get_element(fi, fj) == 0.0) : 523 matrix_E.set_element(fi, fj, float(sys.maxint)) 524 elif conc_matrix.get_element(fi, fj) == (0.0) : 525 matrix_E.set_element(fi, fj, float(-sys.maxint)) 526 # calculate logratio (basis 2) 527 else : 528 try : 529 # !!! caution with log(0) 530 # positive overflow should not be possible, since the arguments of the logarithm should by then 531 # cause an overflow. so if an OverflowError is caught it surely is going to be a negative one. 532 # to bypass this problem we chose to set matrix_E[i][j] to some arbitrary floating point number, 533 # that makes sure that the according edge is going to be activating. 534 # underflow seems to be caught by python internally. 535 matrix_E.set_element(fi, fj, (math.log(conc_matrix.get_element(fi, fj) / ref_conc_matrix.get_element(fi, fj)))/log2 ) 536 except OverflowError : 537 overflows = overflows + 1 538 matrix_E.set_element(fi, fj, float(-sys.maxint)) 539 if overflows > 0 : 540 # errlogger.message('number of overflows, when calculating the logarithm: '+str(overflows), errorlogger_verbositylevel) 541 sys.stderr.write('transdisrupt.logratio_matrix: number of overflows, when calculating the logarithm: %d\n' % overflows) 542 return matrix_E
543 544
545 -def overlaps (net_a, net_b) :
546 """docu missing 547 """ 548 promoter_only_net_a = [] 549 promoter_only_net_b = [] 550 promoter_net_a_and_net_b = [] 551 #collect equivalent (correct) edges, count original edges and reconstructed edges 552 genelist = net_a.gene_names() 553 for gene in genelist : 554 gene_a = net_a.find_gene(gene) 555 gene_b = net_b.find_gene(gene) 556 for promoter_element_net_a in gene_a.promoter : 557 if not isinstance(promoter_element_net_a, transsys.PromoterElementConstitutive): 558 promoter_only_net_a.append(promoter_element_net_a) 559 for promoter_element_net_b in gene_b.promoter : 560 if not isinstance(promoter_element_net_b, transsys.PromoterElementConstitutive): 561 promoter_only_net_b.append(promoter_element_net_b) 562 for promoter_element_net_a in gene_a.promoter : 563 if isinstance(promoter_element_net_a, transsys.PromoterElementLink) : 564 factor_list_net_a = [] 565 for factor in promoter_element_net_a.factor_list : 566 factor_list_net_a.append(factor.name) 567 factor_list_net_a.sort() 568 for promoter_element_net_b in gene_b.promoter : 569 if isinstance(promoter_element_net_b, transsys.PromoterElementLink) : 570 factor_list_net_b = [] 571 for factor in promoter_element_net_b.factor_list : 572 factor_list_net_b.append(factor.name) 573 factor_list_net_b.sort() 574 # Activate 575 if isinstance(promoter_element_net_a, transsys.PromoterElementActivate) and isinstance(promoter_element_net_b, transsys.PromoterElementActivate) : 576 if factor_list_net_a == factor_list_net_b : 577 promoter_net_a_and_net_b.append((promoter_element_net_a, promoter_element_net_b)) 578 promoter_element_net_b.dot_attributes["color"]="green" 579 promoter_element_net_a.dot_attributes["color"]="green" 580 promoter_only_net_a.remove(promoter_element_net_a) 581 promoter_only_net_b.remove(promoter_element_net_b) 582 # Repress 583 elif isinstance(promoter_element_net_a, transsys.PromoterElementRepress) and isinstance(promoter_element_net_b, transsys.PromoterElementRepress) : 584 if factor_list_net_a == factor_list_net_b : 585 promoter_net_a_and_net_b.append((promoter_element_net_a, promoter_element_net_b)) 586 promoter_element_net_b.dot_attributes["color"]="green" 587 promoter_element_net_a.dot_attributes["color"]="green" 588 promoter_only_net_a.remove(promoter_element_net_a) 589 promoter_only_net_b.remove(promoter_element_net_b) 590 return [ promoter_net_a_and_net_b, promoter_only_net_a, promoter_only_net_b ]
591