def make_combinatorial_derivation(document, display_id, part_lists, reverse_complements, constraints): # Make the combinatorial derivation and its template template = sbol3.Component(display_id + "_template", sbol3.SBO_DNA) document.add(template) cd = sbol3.CombinatorialDerivation(display_id, template) cd.strategy = sbol3.SBOL_ENUMERATE # for each part, make a SubComponent or LocalSubComponent in the template and link them together in sequence template_part_list = [] for part_list, rc in zip(part_lists, reverse_complements): # it's a variable if there are multiple values or if there's a single value that's a combinatorial derivation if len(part_list) > 1 or not isinstance(part_list[0], sbol3.Component): sub = sbol3.LocalSubComponent({sbol3.SBO_DNA }) # make a template variable sub.name = "Part " + str(len(template_part_list) + 1) template.features.append(sub) var = sbol3.VariableFeature(cardinality=sbol3.SBOL_ONE, variable=sub) cd.variable_features.append(var) # add all of the parts as variables for part in part_list: if isinstance(part, sbol3.Component): var.variants.append(part) elif isinstance(part, sbol3.CombinatorialDerivation): var.variant_derivations.append(part) else: raise ValueError( "Don't know how to make library element for " + part.name + ", a " + str(part)) else: # otherwise it's a fixed element of the template sub = sbol3.SubComponent(part_list[0]) template.features.append(sub) # in either case, orient and order the template elements sub.orientation = (sbol3.SBOL_REVERSE_COMPLEMENT if rc else sbol3.SBOL_INLINE) if template_part_list: template.constraints.append( sbol3.Constraint(sbol3.SBOL_MEETS, template_part_list[-1], sub)) template_part_list.append(sub) # next, add all of the constraints to the template #template.constraints = (make_constraint(c.strip(),template_part_list) for c in (constraints.split(',') if constraints else [])) # impacted by pySBOL3 appending c_list = (make_constraint(c.strip(), template_part_list) for c in (constraints.split(',') if constraints else [])) for c in c_list: template.constraints.append(c) # return the completed part return cd
def order(five_prime: Union[sbol3.Feature, sbol3.Component], three_prime: Union[sbol3.Feature, sbol3.Component], system: Optional[sbol3.Component] = None) -> sbol3.Feature: """Assert a topological ordering constraint between two features (e.g., a CDS followed by a terminator) Implicitly identifies system and creates/adds features as necessary :param five_prime: containing feature :param three_prime: feature that is contained :param system: optional explicit statement of system :return: three_prime feature """ # transform implicit arguments into explicit system = ensure_singleton_system(system, five_prime, three_prime) five_prime = ensure_singleton_feature(system, five_prime) three_prime = ensure_singleton_feature(system, three_prime) # add a containment relation system.constraints.append( sbol3.Constraint(sbol3.SBOL_MEETS, subject=five_prime, object=three_prime)) return three_prime
def contains(container: Union[sbol3.Feature, sbol3.Component], contained: Union[sbol3.Feature, sbol3.Component], system: Optional[sbol3.Component] = None) -> sbol3.Feature: """Assert a topological containment constraint between two features (e.g., a promoter contained in a plasmid) Implicitly identifies system and creates/adds features as necessary :param container: containing feature :param contained: feature that is contained :param system: optional explicit statement of system :return: contained feature """ # transform implicit arguments into explicit system = ensure_singleton_system(system, container, contained) container = ensure_singleton_feature(system, container) contained = ensure_singleton_feature(system, contained) # add a containment relation system.constraints.append( sbol3.Constraint(sbol3.SBOL_CONTAINS, subject=container, object=contained)) return contained
def make_constraint(constraint, part_list): m = constraint_pattern.match(constraint) if not m: raise ValueError( f'Constraint "{constraint}" does not match pattern "Part X relation Part Y"' ) try: restriction = constraint_dict[m.group(2)] except KeyError: raise ValueError( f'Do not recognize constraint relation in "{constraint}"') x = int(m.group(1)) y = int(m.group(3)) if x is y: raise ValueError(f'A part cannot constrain itself: {constraint}') for n in [x, y]: if not (0 < n <= len(part_list)): raise ValueError( f'Part number "{str(n)}" is not between 1 and {len(part_list)}' ) return sbol3.Constraint(restriction, part_list[x - 1], part_list[y - 1])
def make_composite_component(display_id, part_lists, reverse_complements): # Make the composite as an engineered region composite_part = sbol3.Component(display_id, sbol3.SBO_DNA) composite_part.roles.append(sbol3.SO_ENGINEERED_REGION) # for each part, make a SubComponent and link them together in sequence last_sub = None for part_list, rc in zip(part_lists, reverse_complements): if not len(part_list) == 1: raise ValueError( f'Part list should have precisely one element, but is {part_list}' ) sub = sbol3.SubComponent(part_list[0]) sub.orientation = (sbol3.SBOL_REVERSE_COMPLEMENT if rc else sbol3.SBOL_INLINE) composite_part.features.append(sub) if last_sub: composite_part.constraints.append( sbol3.Constraint(sbol3.SBOL_MEETS, last_sub, sub)) last_sub = sub # return the completed part return composite_part
def to_sbol(self, sbol_doc: sbol3.Document = None) -> sbol3.Document: """Convert the genetic network to SBOL. :param sbol_doc: The SBOL document to add the genetic network to. """ if sbol_doc: doc=sbol_doc else: print('No SBOL Document provided') print('Generating a new SBOL Document') doc = sbol3.Document() products = set() geneticnetwork = sbol3.Component('geneticnetwork', sbol3.SBO_DNA) geneticnetwork.roles.append(sbol3.SO_ENGINEERED_REGION) loica_set = set() for op in self.operators: # Operator Component operator_comp = op.sbol_comp operator_sc = sbol3.SubComponent(operator_comp) # GeneProduct Outputs Component output_str = '' output_scs = [] if type(op.output) != list: outputs = [op.output] else: outputs = op.output for op_output in outputs: output_comp = op_output.sbol_comp output_scs.append(sbol3.SubComponent(output_comp)) output_str += f'_{op_output.name}' # TODO output string for policistronic operators # TU Component if type(op)==Source: input_str= 'c' tu = sbol3.Component(f'TU_{input_str}_{op}{output_str}', sbol3.SBO_DNA) #generalize to multi input/output TUs tu.roles.append(sbol3.SO_ENGINEERED_REGION) tu.features = [operator_sc] for sc in output_scs: tu.features.append(sc) elif type(op)==Hill2: # type(op.input)==List: input_str = '' for inp in op.input: input_str += f'_{inp.name}' tu = sbol3.Component(f'TU{input_str}_{op}{output_str}', sbol3.SBO_DNA) #generalize to multi input/output TUs tu.features = [operator_sc] for sc in output_scs: tu.features.append(sc) for inp in op.input: input_comp = inp.sbol_comp if type(input_comp)==sbol3.Component: input_sc = sbol3.SubComponent(input_comp) tu.features.append(input_sc) else: tu.features.append(input_comp) else: input_str= f'_{op.input.name}' tu = sbol3.Component(f'TU{input_str}_{op}{output_str}', sbol3.SBO_DNA) #generalize to multi input/output TUs tu.features = [operator_sc] for sc in output_scs: tu.features.append(sc) input_comp = op.input.sbol_comp if type(input_comp)==sbol3.Component: input_sc = sbol3.SubComponent(input_comp) tu.features.append(input_sc) else: tu.features.append(input_comp) tu.roles.append(sbol3.SO_ENGINEERED_REGION) for i in range(len(tu.features)-1): constraint = sbol3.Constraint(sbol3.SBOL_PRECEDES, tu.features[i], tu.features[i + 1]) tu.constraints = [constraint] #tu.constraints = [sbol3.Constraint(sbol3.SBOL_PRECEDES, operator_sc, output_sc)] # generate a sequence for the TU assuming assembly by type IIS REsnf both parts will have the fusion sites. # compare last 4 bp with thefirst 4 bp of the next part, given the preceds constraint. # if they are the same then delete one of them and concatenate the rest. # else error or comment TU sequence can not be generated, provide ways to add it. # Output GeneProduct Component for op_output, sc in zip(outputs, output_scs): #make list of tuples? for output_participation if type(op_output)==Regulator: if op_output.type_ == 'PRO': output_gp_comp = sbol3.Component(f'{op_output.name}_protein', sbol3.SBO_PROTEIN) output_gp_comp.roles.append(sbol3.SO_TRANSCRIPTION_FACTOR) elif op_output.type_ == 'RNA': output_gp_comp = sbol3.Component(f'{op_output.name}_rna', sbol3.SBO_RNA) output_gp_comp.roles.append(sbol3.SO_TRANSCRIPTION_FACTOR) else: print('Unsupported output molecule type') elif type(op_output)==Reporter: # For now just support fluorescent reporters if op_output.type_ == 'PRO': output_gp_comp = sbol3.Component(f'{op_output.name}_protein', sbol3.SBO_PROTEIN) output_gp_comp.roles.append('http://purl.obolibrary.org/obo/NCIT_C37894') elif op_output.type_ == 'RNA': output_gp_comp = sbol3.Component(f'{op_output.name}_rna', sbol3.SBO_RNA) output_gp_comp.roles.append('http://purl.obolibrary.org/obo/NCIT_C37894') else: print('Unsupported output molecule type') else: print('Unsupported output Type') output_gp_sc = sbol3.SubComponent(output_gp_comp) tu.features.append(output_gp_sc) if op_output not in products: products.add(op_output) loica_set.add(output_gp_comp) # Genetic Production Interaction pf the output output_participation = sbol3.Participation(roles=[sbol3.SBO_TEMPLATE], participant=sc) gp_participation = sbol3.Participation(roles=[sbol3.SBO_PRODUCT], participant=output_gp_sc) production = sbol3.Interaction(types=[sbol3.SBO_GENETIC_PRODUCTION], participations=[output_participation, gp_participation]) tu.interactions.append(production) # obtain TU subcomponents sequences, specially CDS and flanking parts sequences # look for ATG on the CDS and upstream part sequences (in the case of MoClo the ATG is in the fusion sites) # look for stop codons on frame with the ATG. # add translated the sequence between the ATG and the stop codon as protein sequence. #protein.sequence = tu.cds.sequence # Input Product Component if type(op) == Source: inputs=[] elif type(op) == Hill2: #type(op.input) != List: inputs = op.input else: inputs = [op.input] #inputs_prod_sc = [] for op_input in inputs: if type(op_input)==Regulator: if op_input.type_ == 'PRO': input_prod_comp = sbol3.Component(f'{op_input.name}_protein', sbol3.SBO_PROTEIN) input_prod_comp.roles.append(sbol3.SO_TRANSCRIPTION_FACTOR) elif op_input.type_ == 'RNA': input_prod_comp = sbol3.Component(f'{op_input.name}_rna', sbol3.SBO_RNA) input_prod_comp.roles.append(sbol3.SO_TRANSCRIPTION_FACTOR) else: print('Unsupported input molecule type') elif type(op_input)==Supplement: input_prod_comp = sbol3.Component(f'{op_input.name}_chemical', sbol3.SBO_SIMPLE_CHEMICAL) input_prod_comp.roles.append(sbol3.SO_TRANSCRIPTION_FACTOR) else: print('Unsupported input Type') # adds two times prod comp on the repressilator but necessary for normal circuits if op_input not in products: products.add(op_input) loica_set.add(input_prod_comp) input_prod_sc = sbol3.SubComponent(input_prod_comp) tu.features.append(input_prod_sc) #inputs_prod_sc.append(input_prod_sc) #how can I not create 2 times the same component? if type(op_input)!=Regulator: # if it is a regulator it is already created loica_set.add(input_prod_comp) # Input Interaction if type(op)==Hill1 and op.alpha[0]>op.alpha[1]: input_participation = sbol3.Participation(roles=[sbol3.SBO_INHIBITOR], participant=input_prod_sc) op_participation = sbol3.Participation(roles=[sbol3.SBO_INHIBITED], participant=operator_sc) interaction = sbol3.Interaction(types=[sbol3.SBO_INHIBITION], participations=[input_participation, op_participation]) tu.interactions.append(interaction) elif type(op)==Hill2 and op.alpha[0]== max(op.alpha): input_participation = sbol3.Participation(roles=[sbol3.SBO_INHIBITOR], participant=input_prod_sc) op_participation = sbol3.Participation(roles=[sbol3.SBO_INHIBITED], participant=operator_sc) interaction = sbol3.Interaction(types=[sbol3.SBO_INHIBITION], participations=[input_participation, op_participation]) tu.interactions.append(interaction) elif type(op)==Receiver: input_participation = sbol3.Participation(roles=[sbol3.SBO_STIMULATOR], participant=input_prod_sc) op_participation = sbol3.Participation(roles=[sbol3.SBO_STIMULATED], participant=operator_sc) interaction = sbol3.Interaction(types=[sbol3.SBO_STIMULATION], participations=[input_participation, op_participation]) tu.interactions.append(interaction) elif type(op)==Hill1 and op.alpha[0]<op.alpha[1]: input_participation = sbol3.Participation(roles=[sbol3.SBO_STIMULATOR], participant=input_prod_sc) op_participation = sbol3.Participation(roles=[sbol3.SBO_STIMULATED], participant=operator_sc) interaction = sbol3.Interaction(types=[sbol3.SBO_STIMULATION], participations=[input_participation, op_participation]) tu.interactions.append(interaction) elif type(op)==Source: pass else: print('Unsupported operator Type') # Model #model_string = str(op.__dict__) op_model = sbol3.Model(f'LOICA{input_str}_{op}{output_str}_model', source='https://github.com/SynBioUC/LOICA/blob/master/loica/operators', language='http://identifiers.org/EDAM:format_3996', framework='http://identifiers.org/SBO:0000062',) #attachments=[model_string]) doc.add(op_model) tu.models.append(op_model) doc.add(tu) tu_sc = sbol3.SubComponent(tu) geneticnetwork.features.append(tu_sc) loica_list = list(loica_set) doc.add(loica_list) if len(geneticnetwork.features) > 1: for i in range(len(geneticnetwork.features)-1): geneticnetwork.constraints = [sbol3.Constraint(sbol3.SBOL_PRECEDES, geneticnetwork.features[i], geneticnetwork.features[i+1])] else: pass doc.add(geneticnetwork) return doc
gfp.name = 'GFP' gfp.description = 'GFP Coding Sequence' # Wrap it together circuit = sbol3.Component('circuit', sbol3.SBO_DNA) circuit.roles.append(sbol3.SO_ENGINEERED_REGION) ptet_sc = sbol3.SubComponent(ptet) op1_sc = sbol3.SubComponent(op1) utr1_sc = sbol3.SubComponent(utr1) gfp_sc = sbol3.SubComponent(gfp) # circuit.features can be set and appended to like any Python list circuit.features = [ptet_sc, op1_sc] circuit.features += [utr1_sc] circuit.features.append(gfp_sc) circuit.constraints = [ sbol3.Constraint(sbol3.SBOL_PRECEDES, ptet_sc, op1_sc), sbol3.Constraint(sbol3.SBOL_PRECEDES, op1_sc, utr1_sc), sbol3.Constraint(sbol3.SBOL_PRECEDES, utr1_sc, gfp_sc) ] doc = sbol3.Document() # TODO: Enhancement: doc.addAll([ptet, op1, utr1, ...]) doc.add(ptet) doc.add(op1) doc.add(utr1) doc.add(gfp) doc.add(circuit) doc.write('circuit.nt', sbol3.SORTED_NTRIPLES)
def make_composite_part(document, row, composite_parts, linear_products, final_products, config): """ Create a composite part from a row in the composites sheet :param document: Document to add parts to :param row: Excel row to be processed :param composite_parts: collection of parts to add to :param linear_products: collection of linear parts to add to :param final_products: collection of final parts to add to :param config: dictionary of sheet parsing configuration variables """ # Parse material from sheet row name = row[config['composite_name_col']].value if name is None: return # skip lines without names else: name = name.strip() # make sure we're discarding whitespace display_id = sbol3.string_to_display_id(name) design_notes = (row[config['composite_notes_col']].value if row[config['composite_notes_col']].value else "") description = \ (row[config['composite_description_col']].value if row[config['composite_description_col']].value else "") final_product = row[config['composite_final_col']].value # boolean transformed_strain = row[config['composite_strain_col']].value if config[ 'composite_strain_col'] else None backbone_or_locus_raw = row[ config['composite_context_col']].value if config[ 'composite_context_col'] else None backbone_or_locus = part_names( backbone_or_locus_raw) if backbone_or_locus_raw else [] constraints = row[config['composite_constraints_col']].value if config[ 'composite_constraints_col'] else None reverse_complements = [ is_RC(spec) for spec in part_specifications(row, config) ] part_lists = \ [[partname_to_part(document, name) for name in part_names(spec)] for spec in part_specifications(row, config)] combinatorial = any( x for x in part_lists if len(x) > 1 or isinstance(x[0], sbol3.CombinatorialDerivation)) # Build the composite logging.debug( f'Creating {"library" if combinatorial else "composite part"} "{name}"' ) linear_dna_display_id = (f'{display_id}_ins' if backbone_or_locus else display_id) if combinatorial: composite_part = make_combinatorial_derivation(document, linear_dna_display_id, part_lists, reverse_complements, constraints) else: composite_part = make_composite_component(linear_dna_display_id, part_lists, reverse_complements) composite_part.name = (f'{name} insert' if backbone_or_locus else name) composite_part.description = f'{design_notes}\n{description}'.strip() # add the component to the appropriate collections document.add(composite_part) composite_parts.members.append(composite_part.identity) if final_product: linear_products.members.append(composite_part.identity) ############### # Consider strain and locus information if transformed_strain: warnings.warn("Not yet handling strain information: " + transformed_strain) if backbone_or_locus: # TODO: handle integration locuses as well as plasmid backbones backbones = [ partname_to_part(document, name) for name in backbone_or_locus ] if any(b is None for b in backbones): raise ValueError( f'Could not find specified backbone(s) "{backbone_or_locus}"') if any(not is_plasmid(b) for b in backbones): raise ValueError( f'Specified backbones "{backbone_or_locus}" are not all plasmids' ) if combinatorial: logging.debug( f"Embedding library '{composite_part.name}' in plasmid backbone(s) '{backbone_or_locus}'" ) plasmid = sbol3.Component(f'{display_id}_template', sbol3.SBO_DNA) document.add(plasmid) part_sub = sbol3.LocalSubComponent([sbol3.SBO_DNA], name="Inserted Construct") plasmid.features.append(part_sub) plasmid_cd = sbol3.CombinatorialDerivation(display_id, plasmid, name=name) document.add(plasmid_cd) part_var = sbol3.VariableFeature(cardinality=sbol3.SBOL_ONE, variable=part_sub) plasmid_cd.variable_features.append(part_var) part_var.variant_derivations.append(composite_part) if final_product: final_products.members.append(plasmid_cd) else: if len(backbones) == 1: logging.debug( f'Embedding part "{composite_part.name}" in plasmid backbone "{backbone_or_locus}"' ) plasmid = sbol3.Component(display_id, sbol3.SBO_DNA, name=name) document.add(plasmid) part_sub = sbol3.SubComponent(composite_part) plasmid.features.append(part_sub) if final_product: final_products.members += {plasmid} else: logging.debug( f'Embedding part "{composite_part.name}" in plasmid library "{backbone_or_locus}"' ) plasmid = sbol3.Component(f'{display_id}_template', sbol3.SBO_DNA) document.add(plasmid) part_sub = sbol3.SubComponent(composite_part) plasmid.features.append(part_sub) plasmid_cd = sbol3.CombinatorialDerivation(display_id, plasmid, name=name) document.add(plasmid_cd) if final_product: final_products.members.append(plasmid_cd) if len(backbones) == 1: backbone_sub = sbol3.SubComponent(backbones[0]) plasmid.features.append(backbone_sub) else: backbone_sub = sbol3.LocalSubComponent([sbol3.SBO_DNA]) backbone_sub.name = "Vector" plasmid.features.append(backbone_sub) backbone_var = sbol3.VariableFeature(cardinality=sbol3.SBOL_ONE, variable=backbone_sub) plasmid_cd.variable_features.append(backbone_var) backbone_var.variants += backbones plasmid.constraints.append( sbol3.Constraint(sbol3.SBOL_MEETS, part_sub, backbone_sub)) plasmid.constraints.append( sbol3.Constraint(sbol3.SBOL_MEETS, backbone_sub, part_sub))