def test_polymerize_functionCall(self): sequences = np.array([[0, 1, 0, 1], [3, 1, 0, 2], [2, 2, 1, 1]]) baseAmounts = np.array([9, 9, 9, 9]) energy = 100 # Good calls test polymerize(sequences, baseAmounts, energy, np.random.RandomState())
def test_polymerize_testVariableSequenceLength(self): sequences = np.array([[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 1, 1], [3] * 12, [2] * 12, [1, 1, 1] + [P] * 9]) baseAmounts = np.array([30] * 4) energy = 50 result = polymerize(sequences, baseAmounts, energy, np.random.RandomState()) assert_equal(result.sequenceElongation, np.array([12, 12, 12, 3]))
def test_polymerize_testEnergyLimited(self): sequences = np.array([[0, 1, 0, 1], [1, 1, 1, 1]]) baseAmounts = np.array([9, 9]) energy = 6 result = polymerize(sequences, baseAmounts, energy, np.random.RandomState()) assert_equal(result.sequenceElongation, np.array([3, 3])) assert_equal(result.monomerUsages, np.array([2, 4])) self.assertEqual(6, result.nReactions)
def test_polymerize_testAllAvailableBases(self): sequences = np.array([[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 1, 1], [3] * 12, [2] * 12, [P] * 12]) baseAmounts = np.array([11] * 4) energy = 30 result = polymerize(sequences, baseAmounts, energy, np.random.RandomState()) assert_equal(result.sequenceElongation, np.array([12, 9, 9, 0])) assert_equal(result.monomerUsages, np.array([3, 5, 11, 11])) self.assertEqual(30, result.nReactions)
def test_polymerize_testFairness(self): sequences = np.array([[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3], [3] * 12, [2] * 12, [P] * 12]) baseAmounts = np.array([11] * 4) baseAmountsOriginal = baseAmounts.copy() energy = 30 result = polymerize(sequences, baseAmounts, energy, np.random.RandomState()) assert_equal(result.sequenceElongation, np.array([10, 9, 9, 0])) assert_equal(result.monomerUsages, np.array([3, 3, 11, 11])) self.assertEqual(28, result.nReactions) self.assertTrue((baseAmounts == baseAmountsOriginal).all(), 'should not modify monomerLimits')
def _fullProfile(): np.random.seed(0) sequences, monomerLimits, reactionLimit, randomState = _setupRealExample() # Recipe from https://docs.python.org/2/library/profile.html#module-cProfile pr = cProfile.Profile() pr.enable() sequenceElongation, monomerUsages, nReactions = polymerize( sequences, monomerLimits, reactionLimit, randomState) pr.disable() s = StringIO.StringIO() sortby = 'cumulative' ps = pstats.Stats(pr, stream=s).sort_stats(sortby) ps.print_stats() print s.getvalue()
def test_polymerize_testVariableElongation(self): sequences = np.array([ [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 1, 1], [3] * 12, [2] * 12, [1, 1, 1] + [P] * 9]) baseAmounts = np.array([30] * 4) energy = 20 rates = np.array([4, 3, 2, 1]) result = polymerize( sequences, baseAmounts, energy, np.random.RandomState(), rates, variable_elongation=True) assert_equal(result.sequenceElongation, np.array([8, 6, 4, 2]))
def _simpleProfile(): np.random.seed(0) sequences, monomerLimits, reactionLimit, randomState = _setupExample() nSequences, length = sequences.shape nMonomers = monomerLimits.size sequenceLengths = (sequences != PAD_VALUE).sum(axis=1) t = time.time() sequenceElongation, monomerUsages, nReactions = polymerize( sequences, monomerLimits, reactionLimit, randomState) evalTime = time.time() - t assert (sequenceElongation <= sequenceLengths + 1).all() assert (monomerUsages <= monomerLimits).all() assert nReactions <= reactionLimit assert nReactions == monomerUsages.sum() print """ Polymerize function report: For {} sequences of {} different monomers elongating by at most {}: {:0.1f} ms to evaluate {} polymerization reactions {:0.1f} average elongations per sequence {:0.1%} monomer utilization {:0.1%} energy utilization {:0.1%} fully elongated {:0.1%} completion """.format(nSequences, nMonomers, length, evalTime * 1000, nReactions, sequenceElongation.mean(), monomerUsages.sum() / monomerLimits.sum(), nReactions / reactionLimit, (sequenceElongation == sequenceLengths).sum() / nSequences, sequenceElongation.sum() / sequenceLengths.sum())
def evolveState(self): ## Module 1: Replication initiation # Get number of active DNA polymerases and oriCs activeDnaPoly = self.activeDnaPoly.molecules() activePolymerasePresent = (len(activeDnaPoly) > 0) oriCs = self.oriCs.molecules() n_oric = len(oriCs) n_chromosomes = self.full_chromosome.total()[0] # If there are no chromosomes and oriC's, return immediately if n_oric == 0 and n_chromosomes == 0: return # Get cell mass cellMass = (self.readFromListener("Mass", "cellMass") * units.fg) # Get critical initiation mass for simulation medium environment current_nutrients = self._external_states['Environment'].nutrients self.criticalInitiationMass = self.getDnaCriticalMass( self.nutrientToDoublingTime[current_nutrients]) # Calculate mass per origin of replication, and compare to critical # initiation mass. This is a rearrangement of the equation: # (Cell Mass)/(Number of origins) > Critical mass # If the above inequality holds true, initiate a round of chromosome # replication for every origin of replication massFactor = cellMass / self.criticalInitiationMass massPerOrigin = massFactor / n_oric replication_initiated = False # If conditions are true, initiate a round of replication on every # origin of replication if massPerOrigin >= 1.0 and self.partialChromosomes.counts().sum( ) == 0: replication_initiated = True # Get replication round indexes of active DNA polymerases if activePolymerasePresent: replicationRound = activeDnaPoly.attr('replicationRound') else: # Set to -1 to set values for new polymerases to 0 replicationRound = np.array([-1]) # Get chromosome indexes of current oriCs chromosomeIndexOriC = oriCs.attr('chromosomeIndex') # Calculate number of new DNA polymerases required (4 per origin) n_new_polymerase = 4 * n_oric # Add new polymerases and oriC's activeDnaPolyNew = self.activeDnaPoly.moleculesNew( "dnaPolymerase", n_new_polymerase) oriCsNew = self.oriCs.moleculesNew("originOfReplication", n_oric) # Calculate and set attributes of newly created polymerases sequenceIdx = np.tile(np.array([0, 1, 2, 3]), n_oric) sequenceLength = np.zeros(n_new_polymerase, dtype=np.int) replicationRound = np.ones( n_new_polymerase, dtype=np.int) * (replicationRound.max() + 1) # Polymerases inherit index of the OriC's they were initiated from chromosomeIndexPolymerase = np.repeat(chromosomeIndexOriC, 4) activeDnaPolyNew.attrIs( sequenceIdx=sequenceIdx, sequenceLength=sequenceLength, replicationRound=replicationRound, chromosomeIndex=chromosomeIndexPolymerase, ) # Calculate and set attributes of newly created oriCs # New OriC's share the index of the old OriC's they were # replicated from oriCsNew.attrIs(chromosomeIndex=chromosomeIndexOriC) # Write data from this module to a listener self.writeToListener("ReplicationData", "criticalMassPerOriC", massPerOrigin) self.writeToListener("ReplicationData", "criticalInitiationMass", self.criticalInitiationMass.asNumber(units.fg)) ## Module 2: replication elongation # If no active polymerases are present, return immediately # Note: the new DNA polymerases activated in the previous module are # not elongated until the next timestep. if len(activeDnaPoly) == 0: return # Build sequences to polymerize dNtpCounts = self.dntps.counts() sequenceIdx, sequenceLengths, massDiffDna = activeDnaPoly.attrs( 'sequenceIdx', 'sequenceLength', 'massDiff_DNA') sequences = buildSequences(self.sequences, sequenceIdx, sequenceLengths, self._dnaPolymeraseElongationRate()) # Use polymerize algorithm to quickly calculate the number of # elongations each "polymerase" catalyzes reactionLimit = dNtpCounts.sum() result = polymerize(sequences, dNtpCounts, reactionLimit, self.randomState) sequenceElongations = result.sequenceElongation dNtpsUsed = result.monomerUsages # Compute mass increase for each polymerizing chromosome massIncreaseDna = computeMassIncrease( sequences, sequenceElongations, self.polymerized_dntp_weights.asNumber(units.fg)) updatedMass = massDiffDna + massIncreaseDna # Update positions of each "polymerase" updatedLengths = sequenceLengths + sequenceElongations activeDnaPoly.attrIs(sequenceLength=updatedLengths, massDiff_DNA=updatedMass) # Update counts of polymerized metabolites self.dntps.countsDec(dNtpsUsed) self.ppi.countInc(dNtpsUsed.sum()) ## Module 3: replication termination # Determine if any polymerases reached the end of their sequences. If # so, terminate replication and update the attributes of the remaining # polymerases and OriC's to reflect the new chromosome structure. terminalLengths = self.sequenceLengths[sequenceIdx] didTerminate = (updatedLengths == terminalLengths) terminatedChromosomes = np.bincount(sequenceIdx[didTerminate], minlength=self.sequences.shape[0]) # If any of the polymerases were terminated, check if all polymerases # initiated the same round as the terminated polymerases has already # been removed - if they have, update attributes of the remaining # polymerases and oriC's, and remove the polymerases. This is not done # when some polymerases were initiated in the same timestep, as the # attributes for these new polymerases cannot be updated in the same # timestep. if didTerminate.sum() > 0 and replication_initiated == False: # Get attributes from active DNA polymerases and oriC's sequenceIdx, chromosomeIndexPolymerase, replicationRound = activeDnaPoly.attrs( 'sequenceIdx', 'chromosomeIndex', 'replicationRound') chromosomeIndexOriC = oriCs.attr('chromosomeIndex') # Check that all terminated polymerases were initiated in the same # replication round assert np.unique(replicationRound[didTerminate]).size == 1 # Get chromosome indexes of the terminated polymerases chromosomeIndexesTerminated = np.unique( chromosomeIndexPolymerase[didTerminate]) newChromosomeIndex = chromosomeIndexPolymerase.max() + 1 # Get replication round index of the terminated polymerases terminatedRound = replicationRound[didTerminate][0] for chromosomeIndexTerminated in chromosomeIndexesTerminated: # Get all remaining active polymerases initiated in the same # replication round and in the given chromosome replicationRoundMatch = (replicationRound == terminatedRound) chromosomeMatch = ( chromosomeIndexPolymerase == chromosomeIndexTerminated) remainingPolymerases = np.logical_and(replicationRoundMatch, chromosomeMatch) # Get all terminated polymerases in the given chromosome terminatedPolymerases = np.logical_and(didTerminate, chromosomeMatch) # If all active polymerases are terminated polymerases, we are # ready to split the chromosome and update the attributes. if remainingPolymerases.sum() == terminatedPolymerases.sum(): # For each set of polymerases initiated in the same # replication round, update the chromosome indexes to a new # index for half of the polymerases. for roundIdx in np.arange(terminatedRound + 1, replicationRound.max() + 1): replicationRoundMatch = (replicationRound == roundIdx) polymerasesToSplit = np.logical_and( replicationRoundMatch, chromosomeMatch) n_matches = polymerasesToSplit.sum() # Number of polymerases initiated in a single round # must be a multiple of eight. assert n_matches % 8 == 0 # Update the chromosome indexes for half of the polymerases secondHalfIdx = np.where(polymerasesToSplit)[0][( n_matches // 2):] chromosomeIndexPolymerase[ secondHalfIdx] = newChromosomeIndex # Reset chromosomeIndex for active DNA polymerases activeDnaPoly.attrIs( chromosomeIndex=chromosomeIndexPolymerase) # Get oriC's in the chromosome getting divided chromosomeMatchOriC = ( chromosomeIndexOriC == chromosomeIndexTerminated) n_matches = chromosomeMatchOriC.sum() # Number of OriC's in a dividing chromosome should be even assert n_matches % 2 == 0 # Update the chromosome indexes for half of the OriC's secondHalfIdx = np.where(chromosomeMatchOriC)[0][( n_matches // 2):] chromosomeIndexOriC[secondHalfIdx] = newChromosomeIndex # Reset chromosomeIndex for oriC's oriCs.attrIs(chromosomeIndex=chromosomeIndexOriC) # Increment the new chromosome index in case another # chromosome needs to be split newChromosomeIndex += 1 # Delete terminated polymerases activeDnaPoly.delByIndexes(np.where(didTerminate)[0]) # Update counts of newly created chromosome halves. These will be # "stitched" together in the ChromosomeFormation process self.partialChromosomes.countsInc(terminatedChromosomes)
def evolveState(self): ntpCounts = self.ntps.counts() self.writeToListener("GrowthLimits", "ntpAllocated", ntpCounts) activeRnaPolys = self.activeRnaPolys.molecules() if len(activeRnaPolys) == 0: return # Determine sequences that can be elongated rnaIndexes, transcriptLengths, massDiffRna = activeRnaPolys.attrs('rnaIndex', 'transcriptLength', 'massDiff_mRNA') sequences = buildSequences(self.rnaSequences, rnaIndexes, transcriptLengths, self.rnapElngRate) ntpCountInSequence = np.bincount(sequences[sequences != polymerize.PAD_VALUE], minlength = 4) # Polymerize transcripts based on sequences and available nucleotides reactionLimit = ntpCounts.sum() result = polymerize(sequences, ntpCounts, reactionLimit, self.randomState) sequenceElongations = result.sequenceElongation ntpsUsed = result.monomerUsages nElongations = result.nReactions # Calculate changes in mass associated with polymerization and update active polymerases massIncreaseRna = computeMassIncrease(sequences, sequenceElongations, self.ntWeights) updatedMass = massDiffRna + massIncreaseRna didInitialize = (transcriptLengths == 0) & (sequenceElongations > 0) updatedLengths = transcriptLengths + sequenceElongations updatedMass[didInitialize] += self.endWeight activeRnaPolys.attrIs(transcriptLength = updatedLengths, massDiff_mRNA = updatedMass) # Determine if transcript has reached the end of the sequence terminalLengths = self.rnaLengths[rnaIndexes] didTerminate = (updatedLengths == terminalLengths) terminatedRnas = np.bincount(rnaIndexes[didTerminate], minlength = self.rnaSequences.shape[0]) # Remove polymerases that have finished transcription from unique molecules activeRnaPolys.delByIndexes(np.where(didTerminate)[0]) nTerminated = didTerminate.sum() nInitialized = didInitialize.sum() nElongations = ntpsUsed.sum() # Update bulk molecule counts self.ntps.countsDec(ntpsUsed) self.bulkRnas.countsIs(terminatedRnas) self.inactiveRnaPolys.countInc(nTerminated) self.ppi.countInc(nElongations - nInitialized) # Calculate stalls expectedElongations = np.fmin(self.rnapElngRate, terminalLengths - transcriptLengths) rnapStalls = expectedElongations - sequenceElongations # Write outputs to listeners self.writeToListener("TranscriptElongationListener", "countRnaSynthesized", terminatedRnas) self.writeToListener("TranscriptElongationListener", "countNTPsUSed", nElongations) self.writeToListener("GrowthLimits", "ntpUsed", ntpsUsed) self.writeToListener("RnapData", "rnapStalls", rnapStalls) self.writeToListener("RnapData", "ntpCountInSequence", ntpCountInSequence) self.writeToListener("RnapData", "ntpCounts", ntpCounts) self.writeToListener("RnapData", "expectedElongations", expectedElongations.sum()) self.writeToListener("RnapData", "actualElongations", sequenceElongations.sum()) self.writeToListener("RnapData", "didTerminate", didTerminate.sum()) self.writeToListener("RnapData", "terminationLoss", (terminalLengths - transcriptLengths)[didTerminate].sum())
def evolveState(self): # Write allocation data to listener self.writeToListener("GrowthLimits", "gtpAllocated", self.gtp.count()) self.writeToListener("GrowthLimits", "aaAllocated", self.aas.counts()) # Get number of active ribosomes activeRibosomes = self.activeRibosomes.molecules() self.writeToListener("GrowthLimits", "activeRibosomeAllocated", len(activeRibosomes)) if len(activeRibosomes) == 0: return # Build amino acids sequences for each ribosome to polymerize proteinIndexes, peptideLengths, massDiffProtein = activeRibosomes.attrs( 'proteinIndex', 'peptideLength', 'massDiff_protein') sequences = buildSequences(self.proteinSequences, proteinIndexes, peptideLengths, self.ribosomeElongationRate) if sequences.size == 0: return # Calculate elongation resource capacity aaCountInSequence = np.bincount( sequences[(sequences != polymerize.PAD_VALUE)]) aaCounts = self.aas.counts() # Using polymerization algorithm elongate each ribosome up to the limits # of amino acids, sequence, and GTP result = polymerize( sequences, aaCounts, 10000000, # Set to a large number, the limit is now taken care of in metabolism self.randomState) sequenceElongations = result.sequenceElongation aasUsed = result.monomerUsages nElongations = result.nReactions # Update masses of ribosomes attached to polymerizing polypeptides massIncreaseProtein = computeMassIncrease(sequences, sequenceElongations, self.aaWeightsIncorporated) updatedLengths = peptideLengths + sequenceElongations didInitialize = ((sequenceElongations > 0) & (peptideLengths == 0)) updatedMass = massDiffProtein + massIncreaseProtein updatedMass[didInitialize] += self.endWeight # Write current average elongation to listener currElongRate = (sequenceElongations.sum() / len(activeRibosomes)) / self.timeStepSec() self.writeToListener("RibosomeData", "effectiveElongationRate", currElongRate) # Update active ribosomes, terminating if neccessary activeRibosomes.attrIs(peptideLength=updatedLengths, massDiff_protein=updatedMass) # Ribosomes that reach the end of their sequences are terminated and # dissociated into 30S and 50S subunits. The polypeptide that they are polymerizing # is converted into a protein in BulkMolecules terminalLengths = self.proteinLengths[proteinIndexes] didTerminate = (updatedLengths == terminalLengths) terminatedProteins = np.bincount( proteinIndexes[didTerminate], minlength=self.proteinSequences.shape[0]) activeRibosomes.delByIndexes(np.where(didTerminate)[0]) self.bulkMonomers.countsInc(terminatedProteins) nTerminated = didTerminate.sum() nInitialized = didInitialize.sum() self.ribosome30S.countInc(nTerminated) self.ribosome50S.countInc(nTerminated) # Update counts of amino acids and water to reflect polymerization reactions self.aas.countsDec(aasUsed) self.h2o.countInc(nElongations - nInitialized) # Report stalling information expectedElongations = np.fmin(self.ribosomeElongationRate, terminalLengths - peptideLengths) ribosomeStalls = expectedElongations - sequenceElongations # Write data to listeners self.writeToListener("GrowthLimits", "aasUsed", aasUsed) self.writeToListener("GrowthLimits", "gtpUsed", self.gtpUsed) self.writeToListener("RibosomeData", "ribosomeStalls", ribosomeStalls) self.writeToListener("RibosomeData", "aaCountInSequence", aaCountInSequence) self.writeToListener("RibosomeData", "aaCounts", aaCounts) self.writeToListener("RibosomeData", "expectedElongations", expectedElongations.sum()) self.writeToListener("RibosomeData", "actualElongations", sequenceElongations.sum()) self.writeToListener( "RibosomeData", "actualElongationHist", np.histogram(sequenceElongations, bins=np.arange(0, 23))[0]) self.writeToListener( "RibosomeData", "elongationsNonTerminatingHist", np.histogram(sequenceElongations[~didTerminate], bins=np.arange(0, 23))[0]) self.writeToListener("RibosomeData", "didTerminate", didTerminate.sum()) self.writeToListener("RibosomeData", "terminationLoss", (terminalLengths - peptideLengths)[didTerminate].sum()) self.writeToListener("RibosomeData", "numTrpATerminated", terminatedProteins[self.trpAIndex]) self.writeToListener("RibosomeData", "processElongationRate", self.ribosomeElongationRate / self.timeStepSec())