# 6.00 Problem Set 8
#
# Name: Casey
# Collaborators:
# Time: ~8hr
import numpy
import random
import pylab
from ps7 import *
#
# PROBLEM 1
#
class ResistantVirus(SimpleVirus):
"""
Representation of a virus which can have drug resistance.
"""
def __init__(self, maxBirthProb, clearProb, resistances, mutProb):
"""
Initialize a ResistantVirus instance, saves all parameters as attributes
of the instance.
maxBirthProb: Maximum reproduction probability (a float between 0-1)
clearProb: Maximum clearance probability (a float between 0-1).
resistances: A dictionary of drug names (strings) mapping to the state
of this virus particle's resistance (either True or False) to each drug.
e.g. {'guttagonol':False, 'grimpex',False}, means that this virus
particle is resistant to neither guttagonol nor grimpex.
mutProb: Mutation probability for this virus particle (a float). This is
the probability of the offspring acquiring or losing resistance to a drug.
"""
# TODO
assert 0 < maxBirthProb < 1 and 0 < clearProb < 1 and 0 < mutProb < 1
assert isinstance(resistances, dict)
self.maxBirthProb = maxBirthProb
self.clearProb = clearProb
self.resistances = resistances
self.mutProb = mutProb
def isResistantTo(self, drug):
"""
Get the state of this virus particle's resistance to a drug. This method
is called by getResistPop() in Patient to determine how many virus
particles have resistance to a drug.
drug: The drug (a string)
returns: True if this virus instance is resistant to the drug, False
otherwise.
"""
# TODO
# resistances: A dictionary of drug names (strings)
return self.resistances[drug]
def reproduce(self, popDensity, activeDrugs):
"""
Stochastically determines whether this virus particle reproduces at a
time step. Called by the update() method in the Patient class.
If the virus particle is not resistant to any drug in activeDrugs,
then it does not reproduce. Otherwise, the virus particle reproduces
with probability:
self.maxBirthProb * (1 - popDensity).
If this virus particle reproduces, then reproduce() creates and returns
the instance of the offspring ResistantVirus (which has the same
maxBirthProb and clearProb values as its parent).
For each drug resistance trait of the virus (i.e. each key of
self.resistances), the offspring has probability 1-mutProb of
inheriting that resistance trait from the parent, and probability
mutProb of switching that resistance trait in the offspring.
For example, if a virus particle is resistant to guttagonol but not
grimpex, and `self.mutProb` is 0.1, then there is a 10% chance that
that the offspring will lose resistance to guttagonol and a 90%
chance that the offspring will be resistant to guttagonol.
There is also a 10% chance that the offspring will gain resistance to
grimpex and a 90% chance that the offspring will not be resistant to
grimpex.
popDensity: the population density (a float), defined as the current
virus population divided by the maximum population
activeDrugs: a list of the drug names acting on this virus particle
(a list of strings).
returns: a new instance of the ResistantVirus class representing the
offspring of this virus particle. The child should have the same
maxBirthProb and clearProb values as this virus. Raises a
NoChildException if this virus particle does not reproduce.
"""
# TODO
# if not resistant to a drug in activeDrugs: don't reproduce
# else: reproduce with birthProb and clearProb
resistant = False
newResistances = self.resistances
# if no active drugs, go to random.random()
if len(activeDrugs) > 0:
for i in activeDrugs:
if self.isResistantTo(i):
resistant = True
if resistant:
for k,v in self.resistances.items():
if random.random() < self.mutProb:
# reverse resistance in newResistances
newResistances[k] = not v
else:
return NoChildException()
if random.random() < self.maxBirthProb * (1 - popDensity):
return ResistantVirus(self.maxBirthProb, self.clearProb, newResistances, self.mutProb)
else:
return NoChildException()
#a = ResistantVirus(.99, .25, {'guttagonal': True, 'grimpex': True}, .99)
#b = a.reproduce(.1, ['grimpex'])
#print b.resistances
class Patient(SimplePatient):
"""
Representation of a patient. The patient is able to take drugs and his/her
virus population can acquire resistance to the drugs he/she takes.
"""
def __init__(self, viruses, maxPop):
"""
Initialization function, saves the viruses and maxPop parameters as
attributes. Also initializes the list of drugs being administered
(which should initially include no drugs).
viruses: the list representing the virus population (a list of
SimpleVirus instances)
maxPop: the maximum virus population for this patient (an integer)
"""
# TODO
assert isinstance(viruses, list)
assert isinstance(maxPop, int)
self.viruses = viruses
self.maxPop = maxPop
def addPrescription(self, newDrug):
"""
Administer a drug to this patient. After a prescription is added, the
drug acts on the virus population for all subsequent time steps. If the
newDrug is already prescribed to this patient, the method has no effect.
newDrug: The name of the drug to administer to the patient (a string).
postcondition: list of drugs being administered to a patient is updated
"""
# TODO
# should not allow one drug being added to the list multiple times
# if patient has no drug list: make one
# if newDrug on list: don't add
# else: add drug
assert isinstance(newDrug, str)
try:
if newDrug in self.drugs:
return
self.drugs.append(newDrug)
except AttributeError:
self.drugs = []
self.drugs.append(newDrug)
def getPrescriptions(self):
"""
Returns the drugs that are being administered to this patient.
returns: The list of drug names (strings) being administered to this
patient.
"""
# TODO
try:
return self.drugs
except AttributeError:
return []
def getResistPop(self, drugResist):
"""
Get the population of virus particles resistant to the drugs listed in
drugResist.
drugResist: Which drug resistances to include in the population (a list
of strings - e.g. ['guttagonol'] or ['guttagonol', 'grimpex'])
returns: the population of viruses (an integer) with resistances to all
drugs in the drugResist list.
"""
# TODO
assert isinstance(drugResist, list)
count = 0
for i in drugResist:
count += self.viruses.count(i)
return count
def update(self):
"""
Update the state of the virus population in this patient for a single
time step. update() should execute these actions in order:
- Determine whether each virus particle survives and update the list of
virus particles accordingly
- The current population density is calculated. This population density
value is used until the next call to update().
- Determine whether each virus particle should reproduce and add
offspring virus particles to the list of viruses in this patient.
The listof drugs being administered should be accounted for in the
determination of whether each virus particle reproduces.
returns: the total virus population at the end of the update (an
integer)
"""
# TODO
survive_list = []
for i in range(0, len(self.viruses)):
if not self.viruses[i].doesClear():
survive_list.append(self.viruses[i])
# debug print "survive list: ", len(survive_list)
self.viruses = survive_list
# update the pop density
now_density = self.getTotalPop() / float(self.maxPop)
# for each virus particle, reproduce or not
new_viruses = []
for i in range(0, len(self.viruses)):
virus = self.viruses[i]
a = virus.reproduce(now_density, self.getPrescriptions())
if isinstance(a, ResistantVirus):
new_viruses.append(a)
# debug print "new viruses: ", len(new_viruses)
self.viruses = self.viruses + new_viruses
# return total virus pop as an int
return self.getTotalPop()
#
# PROBLEM 2
#
def simulationWithDrug():
"""
Runs simulations and plots graphs for problem 4.
Instantiates a patient, runs a simulation for 150 timesteps, adds
guttagonol, and runs the simulation for an additional 150 timesteps.
total virus population vs. time and guttagonol-resistant virus population
vs. time are plotted
"""
# TODO
a = ResistantVirus(0.1, 0.05, {'guttagonal': False}, 0.005)
viruses = [a]*100
trialPatient = Patient(viruses, 1000)
controlPatient = Patient(viruses, 1000)
guttagonal = []
control = []
for i in range(0, 150):
guttagonal.append(trialPatient.update())
control.append(controlPatient.update())
trialPatient.addPrescription('guttagonal')
for i in range(0, 150):
guttagonal.append(trialPatient.update())
control.append(controlPatient.update())
pylab.plot(guttagonal, 'r-', label='guttagonal prescribed')
pylab.plot(control, 'b-', label='without medication')
pylab.xlabel("Time")
pylab.ylabel("Virus count")
pylab.legend(loc='best')
pylab.title("Virus when drugs applied at time 150")
pylab.show()
#simulationWithDrug()
'''
virus *was* dying off before we get to timestep 150
was not resistant to empty activeDrugs
'''
#
# PROBLEM 3
#
def simulationDelayedTreatment(stepsDelayed, numTrials):
"""
Runs simulations and make histograms for problem 5.
Runs multiple simulations to show the relationship between delayed treatment
and patient outcome.
Histograms of final total virus populations are displayed for delays of 300,
150, 75, 0 timesteps (followed by an additional 150 timesteps of
simulation).
"""
# TODO
# run 30 trials for each scenario
# displays histograms of final virus counts as four colored bars
virusPops = []
a = ResistantVirus(0.1, 0.05, {'guttagonal': True}, 0.005)
viruses = [a]*100
for k in range(0, numTrials):
# reset patients for each trial
myPatients = []
for i in range(0, len(stepsDelayed)):
a = Patient(viruses, 1000)
myPatients.append(a)
# run through all steps with delayed prescriptions
# capture final viral counts
statsStep = []
counter = 0
for i in range(0, max(stepsDelayed)+150):
if counter in stepsDelayed:
myPatients[stepsDelayed.index(counter)].addPrescription('guttagonal')
for j in myPatients:
j.update()
counter += 1
# one final result for each patient each trial
for z in myPatients:
statsStep.append(z.getTotalPop())
print statsStep
virusPops.append(statsStep)
# for hist to work, list should convert to numpy.array()
virusPops = numpy.array(virusPops)
'''
README
100 trials shows 389 patients free of virus and 11 with 1 virus left
needs legend
and axis labels
'''
stepsDelayed = [str(i) for i in stepsDelayed] # convert ints in list to strs
pylab.hist(virusPops, 8, histtype='bar', color=['blue', 'red', 'green', 'purple'],
label=stepsDelayed)
pylab.xlabel('Virus cells remaining')
pylab.ylabel('Patients')
pylab.title('Outcomes for delayed treatment')
pylab.legend() # labels won't appear without it
pylab.show()
#simulationDelayedTreatment([0,75,150,300], 100)
#
# PROBLEM 4
#
def simulationTwoDrugsDelayedTreatment(stepsDelayed, numTrials):
"""
Runs simulations and make histograms for problem 6.
Runs multiple simulations to show the relationship between administration
of multiple drugs and patient outcome.
Histograms of final total virus populations are displayed for lag times of
150, 75, 0 timesteps between adding drugs (followed by an additional 150
timesteps of simulation).
"""
# TODO
# run 30 trials for each scenario
# plot
virusPops = []
a = ResistantVirus(0.1, 0.05, {'guttagonal': False, 'grimpex': False}, 0.005)
viruses = [a]*100
for k in range(0, numTrials):
# reset patients for each trial
myPatients = []
for i in range(0, len(stepsDelayed)):
a = Patient(viruses, 1000)
myPatients.append(a)
# run an initial 150 steps prior to guttagonal
for i in range(0, 150):
for j in myPatients:
j.update()
for j in myPatients:
j.addPrescription('guttagonal')
# apply grimpex at delays defined in stepsDelayed
counter = 0
for i in range(0, max(stepsDelayed)+150):
if counter in stepsDelayed:
myPatients[stepsDelayed.index(counter)].addPrescription('grimpex')
for j in myPatients:
j.update()
counter += 1
# one final result for each patient each trial
statsStep = []
for z in myPatients:
statsStep.append(z.getTotalPop())
print statsStep
virusPops.append(statsStep)
# for hist to work, list should convert to numpy.array()
virusPops = numpy.array(virusPops)
stepsDelayed = [str(i) for i in stepsDelayed] # convert ints in list to strs
pylab.hist(virusPops, 8, histtype='bar', color=['blue', 'red', 'green', 'purple'],
label=stepsDelayed)
pylab.xlabel('Virus cells remaining')
pylab.ylabel('Patients')
pylab.title('Outcomes for delayed treatment w/ 2 drugs')
pylab.legend() # labels won't appear without it
pylab.show()
#simulationTwoDrugsDelayedTreatment([0,75,150,300], 10)
#
# PROBLEM 5
#
def simulationTwoDrugsVirusPopulations():
"""
Run simulations and plot graphs examining the relationship between
administration of multiple drugs and patient outcome.
Plots of total and drug-resistant viruses vs. time are made for a
simulation with a 300 time step delay between administering the 2 drugs and
a simulations for which drugs are administered simultaneously.
"""
#TODO
# go with stacked graphs
# scatter plot for multiple trials
# draw a best fit line
# groupA 150, guttagonal, 300, grimpex, 150
# groupB 150, guttagonal + grimpex, 150
a = ResistantVirus(0.1, 0.05, {'guttagonal':False, 'grimpex':False}, 0.005)
viruses = [a]*100
patientsA = []
patientsB = []
groupA = []
groupB = []
for i in range(0,100):
patientsA.append(Patient(viruses, 1000))
patientsB.append(Patient(viruses, 1000))
for i in range(0,150):
statsA = []
statsB = []
for j in patientsA:
statsA.append(j.update())
for j in patientsB:
statsB.append(j.update())
if i % 5 == 0:
groupA.append(statsA)
groupB.append(statsB)
# prescribe guttagonal
for j in patientsA:
j.addPrescription('guttagonal')
for k in patientsB:
k.addPrescription('guttagonal')
k.addPrescription('grimpex')
for i in range(0,300):
stats = []
for j in patientsA:
stats.append(j.update())
if i % 5 == 0:
groupA.append(stats)
# prescribe grimpex
for m in patientsA:
m.addPrescription('grimpex')
for i in range(0,150):
statsA = []
statsB = []
for j in patientsA:
statsA.append(j.update())
for j in patientsB:
statsB.append(j.update())
if i % 5 == 0:
groupA.append(statsA)
groupB.append(statsB)
# no virus left in simultaneous group
# 25 patients had > 0 (e.g. 1-2) virus count remaining in delayed group
print 100 - groupA[-1].count(0)
print 100 - groupB[-1].count(0)
# averaged line
axisA = [sum(i)/len(i) for i in groupA]
pylab.subplot(2, 1, 1)
pylab.xlim([20,60])
pylab.ylabel("Virus count")
pylab.title('Two stage prescriptions')
pylab.plot(groupA, 'bo')
pylab.plot(axisA, 'r', linewidth=4)
# stacked graphs
axisB = [sum(i)/len(i) for i in groupB]
pylab.subplot(2, 1, 2)
pylab.xlim([20,60])
pylab.title('Simultaneous prescriptions')
pylab.xlabel("Time")
pylab.plot(groupB, 'go')
pylab.plot(axisB, 'r', linewidth=4)
pylab.show()
'''
cannot tell a difference between one drug and two
so printed the final numbers at line 501
'''
#simulationTwoDrugsVirusPopulations()