Python imported code taking long to run...why? - python

Here is the code from one python file that I import into another file...
class Crop():
def water(self):
print('not')
def harvest(self):
print('not')
def __init__(self):
self.height = 0
class Corn(Crop):
def water(self):
self.height = self.height + 2
def harvest(self):
if self.height >= 9:
return 1
else:
return 0
class Wheat(Crop):
def water(self):
self.height = self.height + 1
def harvest(self):
if self.height >= 5:
return 1
else:
return 0
class Irrigator():
def __init__(self, load):
self.load = load
def irrigate(self, field):
while self.load > 0:
self.load = self.load - 1
field.rain()
I take the above code and then I import it into another python file...
from farmhelper import *
from random import *
# Field for holding the crops.
class Field():
def rain(self):
for i in range(len(self.plants)):
self.plants[i].water()
def __init__(self, size, crop):
self.plants = [0] * size
for i in range(size):
self.plants[i] = crop()
class Combine():
def harvest(self, field):
quantity = 0
for i in range(len(field.plants)):
quantity += field.plants[i].harvest()
return quantity
# Create fields with 10,000 of each crop
cornField = Field(10000, Corn)
wheatField = Field(10000, Wheat)
# Create irrigators for each field
cornIrrigator = Irrigator(20000)
wheatIrrigator = Irrigator(500)
# Create a combine for harvesting
combine = Combine()
# 90 days ~3 months of growth
for i in range(90):
# Low chance of rain
if randint(0, 100) > 95:
print("It rained")
cornField.rain()
wheatField.rain()
# Always run the irrigators. Since they are never
# refilled they will quickly run out
cornIrrigator.irrigate(cornField)
wheatIrrigator.irrigate(wheatField)
# Gather the crops - DONE
earsOfCorn = combine.harvest(cornField)
headsOfWheat = combine.harvest(wheatField)
# Print the result - DONE
print("Grew", earsOfCorn, "ears of corn")
print("and", headsOfWheat, "heads of wheat")
But for some reason it takes around 2 to 3 minutes for the code to run. I believe there is a problem with the latter code posted. If anyone has a solution, lemme know!

A more efficient design would be to not model every single plant as a separate instance. As it stands you perform the exact same operation on each plant of a field. Just give the Field a size attribute and a crop attribute thus modelling just that one plant per field and multiply any size related outputs by size.
Something along the lines of this:
class Field():
def rain(self):
self.crop.water()
def __init__(self, size, crop):
self.size = size
self.crop = crop()
class Combine():
def harvest(self, field):
quantity = field.crop.harvest() * field.size
return quantity

Related

How can I make it so agents only perform action during a certain time step in mesa (python)?

I'm trying to create an agent-based model using the mesa python framework. I want my agents to reproduce only on certain days of the year. Specifically, on days 203 through 209. Here is my model so far:
import mesa
import random
#set up class for agent
class Oyster(mesa.Agent):
"""An agent with randomly assigned initial energy & age."""
#define init values
def __init__(self, unique_id, model):
super().__init__(unique_id, model)
self.age = random.randint(0, 3649)
self.energy = random.randint(1,10)
#define what happens at each step
def step(self):
living = True
self.age += 1
self.energy += random.randint(-5, 5)
# Death
if self.energy < 0 or self.age > 3650:
self.model.grid.remove_agent(self)
self.model.schedule.remove(self)
living = False
# Repoduction
if living & self.energy >= 1.5 and self.age > 365:
for i in range(random.randint(1,6)):
babyOyster = Oyster(
self.model.next_id(), self.model, self.energy, self.age
)
self.model.grid.place_agent(babyOyster, self.pos)
self.model.schedule.add(babyOyster)
#set up class for model
class OysterModel(mesa.Model):
"""A model with some number of agents."""
#define init parameters
def __init__(self, N, width, height):
self.num_agents = N
self.grid = mesa.space.MultiGrid(width, height, True)
self.schedule = mesa.time.RandomActivation(self)
self.running = True
# Create agents
for i in range(self.num_agents):
a = Oyster(i, self)
self.schedule.add(a)
# Add the agent to a random grid cell
x = self.random.randrange(self.grid.width)
y = self.random.randrange(self.grid.height)
self.grid.place_agent(a, (x, y))
#define step
def step(self):
"""Advance the model by one step."""
self.schedule.step()
I feel like the code should be something like "reproduce when the step number is divisible by 203-209" but I'm very new to python so I don't know how to get this. I also don't know how the agent can access the step number?
So this is my current solution!
First, I added a new attribute to the Model class called step_count and I initialized it to be equal to 0. Then under the step function in the Model class, I added 1 to step-count.
In the Agent class created an if statement where reproduction only happens if step_count is divisible by my desired interval.
There was also an issue with next_id which was preventing my reproduction code from working. I would get an error saying AttributeError: 'Model' object has no attribute 'current_id'. I fixed that by setting current_id = 0 when I initialized the model. See code below:
#this tutorial uses the mesa package
import mesa
import random
#set up class for agent
class Oyster(mesa.Agent):
"""An agent with randomly assigned initial energy & age."""
#define init values
def __init__(self, unique_id, model, age = 0):
super().__init__(unique_id, model)
self.energy = random.randint(1,10)
self.age = age
#define what happens at each step
def step(self):
living = True
self.age += 1
self.energy += random.randint(-5, 5)
# Death
if (self.energy < 0) or (self.age > 3650):
self.model.grid.remove_agent(self)
self.model.schedule.remove(self)
living = False
#reproduction
if living & (self.age > 365) and (self.energy > 2) and self.model.step_count%50 == 0 :
for i in range(3):
babyOyster = Oyster(
self.model.next_id(), self.model
)
x = self.random.randrange(self.model.grid.width)
y = self.random.randrange(self.model.grid.height)
self.model.grid.place_agent(babyOyster, (x, y))
self.model.schedule.add(babyOyster)
#set up class for model
class OysterModel(mesa.Model):
"""A model with some number of agents."""
#define init parameters
def __init__(self, N, width, height):
self.num_agents = N
self.grid = mesa.space.MultiGrid(width, height, True)
self.schedule = mesa.time.RandomActivation(self)
self.running = True
self.step_count = 0
self.current_id = 0
# Create agents
for i in range(self.num_agents):
x = self.random.randrange(self.grid.width)
y = self.random.randrange(self.grid.height)
age = random.randint(1, 3649)
oyster = Oyster(self.next_id(), self, age)
self.grid.place_agent(oyster, (x, y))
self.schedule.add(oyster)
#definte step
def step(self):
"""Advance the model by one step."""
self.schedule.step()
self.step_count += 1
I also changed a few things to make sure baby wasn't staying in the same cell as the parent and the baby's age starts at 0. Hopefully this helps someone else, and if anyone finds a better solution let me know!

How to get python variable within class and method

I am currently creating my genetic algorithm and want to print the number of generations at the very end of the program when it finishes. However I am unsure how to access the counter variable that is the number of generations when it is outside of the class and method. So for example, at the end it would be like
Generation 100, average fit 18966, best fit 18947
Your best chromosone at generation 100
'\x06pzÂ\x8cYÆr¯n0q\x07l¿M8\x93Þ\x19\x87"\x01\x85\x1er\x89[F_VyER\x9b\x0bm=)\x9a\x9a¿¥\x10F\x12A\x84\x0fZ^\x14\x99\x8a4®\x9f¿*\\\xa0yi\x19E\x8aÇ+6(_<¾£cO~\x9c\x99\x932\x06\x0f\x82\x7f¤\x808xǸñA\x13\x0e<%\x06ÿ#í\x91Pô\x98 ®\r\x1b}\x89y¦\x0cqAK\tp\x95\x99ÔNj=Wn\x16\x94\x0cu!¯ñ\x13Qü[e8_ÂóU\x10\x1av_+%Q_¡ù\x87=\x08~ciÎ_Ï[\x8f#AëT\x14©qG\x89#Z«L\x9b¢\x94WL\x1dV¶R03\x84æ^ßr\x1fÃÈ\x1d\x8e Læª&®x\x94?TAÒD\x14£i\x82J\x15=w~\x03\x0c\xa0¾5\x02f5T\x91ol¢bIÞfk¬¡27W16(}6\x92\x87\n®xm0\x1a\n<8(à}ñ\x88̾\x17g\x9bj6\x8fI&\x12\x9aÂ\x9a_F\x1a\r[\x1dK\x15<.±DjcIy`98d>\x197Z\x91£%tIJ\x820\x93|\x07\x8dnÚ QÂ!Pf\x1d\nåòf\x91\x1d#S¾|\x9ff[d>O=T$ݶI\x9e»QÛÂ\x1d"¿U=û´F÷\x83C}wA\xa0É\x8aD\x93x»\x85\x7f\x14^\x0eL'
done:
100 generations
How do I exactly access the 100 from the method in the class?
import random
class GeneticAlgorithm(object):
def __init__(self, genetics):
self.genetics = genetics
pass
def run(self):
population = self.genetics.initial()
while True:
fits_pops = [(self.genetics.fitness(ch), ch) for ch in population]
if self.genetics.check_stop(fits_pops): break
population = self.next(fits_pops)
pass
return population
def next(self, fits):
parents_generator = self.genetics.parents(fits)
size = len(fits)
nexts = []
while len(nexts) < size:
parents = next(parents_generator)
cross = random.random() < self.genetics.probability_crossover()
children = self.genetics.crossover(parents) if cross else parents
for ch in children:
mutate = random.random() < self.genetics.probability_mutation()
nexts.append(self.genetics.mutation(ch) if mutate else ch)
pass
pass
return nexts[0:size]
pass
class GeneticFunctions(object):
def probability_crossover(self):
r"""returns rate of occur crossover(0.0-1.0)"""
return 1.0
def probability_mutation(self):
r"""returns rate of occur mutation(0.0-1.0)"""
return 0.0
def initial(self):
r"""returns list of initial population
"""
return []
def fitness(self, chromosome):
r"""returns domain fitness value of chromosome
"""
return len(chromosome)
def check_stop(self, fits_populations):
r"""stop run if returns True
- fits_populations: list of (fitness_value, chromosome)
"""
return False
def parents(self, fits_populations):
r"""generator of selected parents
"""
gen = iter(sorted(fits_populations))
while True:
f1, ch1 = next(gen)
f2, ch2 = next(gen)
yield (ch1, ch2)
pass
return
def crossover(self, parents):
r"""breed children
"""
return parents
def mutation(self, chromosome):
r"""mutate chromosome
"""
return chromosome
pass
if __name__ == "__main__":
"""
example: Mapped guess prepared Text
"""
class GuessText(GeneticFunctions):
def __init__(self, target_text,
limit=100, size=100,
prob_crossover=0.9, prob_mutation=0.2):
self.target = self.text2chromo(target_text)
self.counter = 0
self.limit = limit
self.size = size
self.prob_crossover = prob_crossover
self.prob_mutation = prob_mutation
pass
# GeneticFunctions interface impls
def probability_crossover(self):
return self.prob_crossover
def probability_mutation(self):
return self.prob_mutation
def initial(self):
return [self.random_chromo() for j in range(self.size)]
def fitness(self, chromo):
# larger is better, matched == 0
return -sum(abs(c - t) for c, t in zip(chromo, self.target))
def check_stop(self, fits_populations):
self.counter += 1
if self.counter % 100 == 0:
best_match = list(sorted(fits_populations))[-1][1]
fits = [f for f, ch in fits_populations]
best = -(max(fits))
ave = -(sum(fits) / len(fits))
print(
"Generation %3d, average fit %4d, best fit %4d" %
(self.counter, ave, best,
))
print("Your best chromosone at generation %3d" % self.counter)
print("%r" % self.chromo2text(best_match))
pass
return self.counter >= self.limit
def parents(self, fits_populations):
while True:
father = self.tournament(fits_populations)
mother = self.tournament(fits_populations)
yield (father, mother)
pass
pass
def crossover(self, parents):
father, mother = parents
index1 = random.randint(1, len(self.target) - 2)
index2 = random.randint(1, len(self.target) - 2)
if index1 > index2: index1, index2 = index2, index1
child1 = father[:index1] + mother[index1:index2] + father[index2:]
child2 = mother[:index1] + father[index1:index2] + mother[index2:]
return (child1, child2)
def mutation(self, chromosome):
index = random.randint(0, len(self.target) - 1)
vary = random.randint(-5, 5)
mutated = list(chromosome)
mutated[index] += vary
return mutated
# internals
def tournament(self, fits_populations):
alicef, alice = self.select_random(fits_populations)
bobf, bob = self.select_random(fits_populations)
return alice if alicef > bobf else bob
def select_random(self, fits_populations):
return fits_populations[random.randint(0, len(fits_populations)-1)]
def text2chromo(self, text):
return [ord(ch) for ch in text]
def chromo2text(self, chromo):
return "".join(chr(max(1, min(ch, 255))) for ch in chromo)
def random_chromo(self):
return [random.randint(1, 255) for i in range(len(self.target))]
pass
GeneticAlgorithm(GuessText("""The smartest and fastest Pixel yet.
Google Tensor: Our first custom-built processor.
The first processor designed by Google and made for Pixel, Tensor makes the new Pixel phones our most powerful yet.
The most advanced Pixel Camera ever.
Capture brilliant color and vivid detail with Pixels best-in-class computational photography and new pro-level lenses.""")).run()
print('done:')
print("%3d " 'generations' % counter)
pass
Define the GuessText first. Then access the counter.
gt = GuessText("""The smartest and fastest Pixel yet.
Google Tensor: Our first custom-built processor.
The first processor designed by Google and made for Pixel, Tensor makes the new Pixel phones our most powerful yet.
The most advanced Pixel Camera ever.
Capture brilliant color and vivid detail with Pixels best-in-class computational photography and new pro-level lenses.""")
GeneticAlgorithm(gt).run()
print('done:')
print("%3d " 'generations' % gt.counter)

QDial ange per revolution

I'm trying to make an Application with PyQt5, Python 3.7.3 using a Raspberry pi4B and a 5 inch touch screen.
The thing is that I need to make a QDial, but I want it to make more than one revolution if it goes from min range to max range. For example, if the Qdial has range from 0 to 500, I want it to make 100 points per revolution, so you have to do a full rotation 5 times to go from the min value to the max value.
This is what I've tried:
`
from PyQt5.QtWidgets import *
import sys
class Window(QWidget):
def __init__(self):
QWidget.__init__(self)
layout = QGridLayout()
self.setLayout(layout)
self.dial = QDial()
self.dial.setMinimum(0)
self.dial.setMaximum(100)
self.dial.setValue(40)
self.dial.valueChanged.connect(self.sliderMoved)
self.dial.setWrapping(True)
self.text=QLabel()
layout.addWidget(self.dial)
layout.addWidget(self.text)
self.isHigher=False
def sliderMoved(self):
print("Dial value = %i" % (self.dial.value()))
self.text.setText(str(self.dial.value()))
if(self.dial.value()==100 and self.isHigher==False):
self.higher_range()
self.isHigher=True
if(self.dial.value()==100 and self.isHigher==True):
self.lower_range()
self.isHigher=False
def higher_range(self):
self.dial.setRange(100,200)
self.dial.setValue(105)
def lower_range(self):
self.dial.setRange(0,100)
self.dial.setValue(95)
app = QApplication(sys.argv)
screen = Window()
screen.show()
sys.exit(app.exec_())
`
But this doesn't work, It keeps changing from 95 to 105 and viceversa.
QDial is a pretty peculiar control. While it's still supported, it's poorly implemented, and I believe it's by choice: due to its nature, it's really hard to add more features. I had quite an amount of experience with it, and I know it's not an easy element to deal with.
One of its issues is that it represents a monodimensional range but, visually and UI speaking, it is a bidimensional object.
What you're trying to achieve is possible, but consider that an UI element should always display its state in a clear way and have a corresponding proper behavior; that's the only way UI can tell the user the state. Physical dials don't have this issue: you also have a tactile response that tells you when the gear reaches its end.
From my experience I could tell you that you should avoid it as much as possible: it seems a nice and intuitive widget, but in reality it's very difficult to get a proper result that is actually intuitive to the user. There are some instances for which it makes sense to use it (in my case, representation of a physical knob of an electronic musical instrument). I suggest you to do some research on skeumorphism and UX aspects.
That said, this is a possible raw implementation. I've overridden some aspects (most importantly, the valueChanged signal, for naming consistency), but for a proper implementation you should do much more work (and testing).
The trick is to set the range based on the number of "revolutions": if the maximum is 500 and 5 revolutions are chosen, then the dial will have an actual maximum of 100. Then, whenever the value changes, we check whether previous value was below or above the minimum/maximum of the actual range, and change the revolution count accordingly.
Two important notes:
since QDial inherits from QAbstractSlider, it has a range(minimum, maximum + 1), and since the division could have some rest, the "last" revolution will have a different range;
I didn't implement the wheel event, as that requires further inspection and choosing the appropriate behavior depending on the "previous" value and revolution;
class SpecialDial(QDial):
_cycleValueChange = pyqtSignal(int)
def __init__(self, minimum=0, maximum=100, cycleCount=2):
super().__init__()
assert cycleCount > 1, 'cycles must be 2 or more'
self.setWrapping(True)
self.cycle = 0
self.cycleCount = cycleCount
self._minimum = minimum
self._maximum = maximum
self._normalMaximum = (maximum - minimum) // cycleCount
self._lastMaximum = self._normalMaximum + (maximum - minimum) % self._normalMaximum
self._previousValue = super().value()
self._valueChanged = self.valueChanged
self.valueChanged = self._cycleValueChange
self._valueChanged.connect(self.adjustValueChanged)
self.setRange(0, self._normalMaximum)
def value(self):
return super().value() + self._normalMaximum * self.cycle
def minimum(self):
return self._minimum
def maximum(self):
return self._maximum()
def dialMinimum(self):
return super().minimum()
def dialMaximum(self):
return super().maximum()
def adjustValueChanged(self, value):
if value < self._previousValue:
if (value < self.dialMaximum() * .3 and self._previousValue > self.dialMaximum() * .6 and
self.cycle + 1 < self.cycleCount):
self.cycle += 1
if self.cycle == self.cycleCount - 1:
self.setMaximum(self._lastMaximum)
elif (value > self.dialMaximum() * .6 and self._previousValue < self.dialMaximum() * .3 and
self.cycle > 0):
self.cycle -= 1
if self.cycle == 0:
self.setMaximum(self._normalMaximum)
new = self.value()
if self._previousValue != new:
self._previousValue = value
self.valueChanged.emit(self.value())
def setValue(self, value):
value = max(self._minimum, min(self._maximum, value))
if value == self.value():
return
block = self.blockSignals(True)
self.cycle, value = divmod(value, self._normalMaximum)
if self.dialMaximum() == self._normalMaximum and self.cycle == self.cycleCount - 1:
self.setMaximum(self._lastMaximum)
elif self.dialMaximum() == self._lastMaximum and self.cycle < self.cycleCount - 1:
self.setMaximum(self._normalMaximum)
super().setValue(value)
self.blockSignals(block)
self._previousValue = self.value()
self.valueChanged.emit(self._previousValue)
def keyPressEvent(self, event):
key = event.key()
if key in (Qt.Key_Right, Qt.Key_Up):
step = self.singleStep()
elif key in (Qt.Key_Left, Qt.Key_Down):
step = -self.singleStep()
elif key == Qt.Key_PageUp:
step = self.pageStep()
elif key == Qt.Key_PageDown:
step = -self.pageStep()
elif key in (Qt.Key_Home, Qt.Key_End):
if key == Qt.Key_Home or self.invertedControls():
if super().value() > 0:
self.cycle = 0
block = self.blockSignals(True)
super().setValue(0)
self.blockSignals(block)
self.valueChanged.emit(self.value())
else:
if self.cycle != self.cycleCount - 1:
self.setMaximum(self._lastMaximum)
self.cycle = self.cycleCount - 1
if super().value() != self._lastMaximum:
block = self.blockSignals(True)
super().setValue(self._lastMaximum)
self.blockSignals(block)
self.valueChanged.emit(self.value())
return
else:
super().keyPressEvent(event)
return
if self.invertedControls():
step *= -1
current = self.value()
new = max(self._minimum, min(self._maximum, current + step))
if current != new:
super().setValue(super().value() + (new - current))
class Window(QWidget):
def __init__(self):
QWidget.__init__(self)
layout = QGridLayout()
self.setLayout(layout)
self.dial = SpecialDial()
self.dial.valueChanged.connect(self.sliderMoved)
self.text=QLabel()
layout.addWidget(self.dial)
layout.addWidget(self.text)
def sliderMoved(self):
self.text.setText(str(self.dial.value()))
I strongly suggest you to take your time to:
consider is this is really what you want, since, as said, this kind of control can be very tricky from the UX perspective;
carefully read the code and understand its logics;

What is the minimum number of case (or if/else) statements required to calculate all unknown values corresponding to properties of an object?

Consider a right-angle triangle, which has the properties
Hypotenuse (side)
Adjacent (side)
Opposite (side)
Area
Given any 2 of these properties, it is always possible to calculate the value of the other 2. My question relates to what the most efficient/elegant way of doing this is.
At present, the only way of doing this that I can think of is to use (4C2)*2 = 12 case statements, each relating to a possible combination of inputsa that may be provided.
For example, using python you might have something like
class RightAngleTriangle():
def __init__(this, propertyType1, propertyValue1, propertyType2, propertyValue2):
this.adjacent = 0
this.opposite = 0
this.hypotenuse = 0
this.area = 0
if (propertyType1 == "adjacent" and propertyType2 == "opposite"):
this.adjacent = propertyValue1
this.opposite = propertyValue2
this.hypotenuse = (propertyValue1**2 + propertyValue2**2)**0.5
this.area = (propertyValue1 * propertyValue2)/2
elif (propertyType1 == "opposite" and propertyType2 == "adjacent"):
this.adjacent = propertyValue2
this.opposite = propertyValue1
this.hypotenuse = (propertyValue1**2 + propertyValue2**2)**0.5
this.area = (propertyValue1 * propertyValue2)/2
elif (propertyType1 == "adjacent" and propertyType2 == "hypotenuse"):
this.adjacent = propertyValue1
this.hypotenuse = propertyValue2
this.opposite = (propertyValue2**2 + propertyValue1**2)**0.5
this.area = (this.opposite * this.adjacent)/2
...and so on...
You could then create your triangle object, and print its four properties, using code (in this case python) like the below.
t1 = RightAngleTriangle("adjacent", 10, "opposite", 12)
print(t1.adjacent)
print(t1.opposite)
print(t1.hypotenuse)
print(t1.area)
This is hideous. Is there a more eligant solution to this problem?
Yes, at least two - one using args and one using key word args. So:
class RightAngleTriangle():
def __init__(self, *args):
self.adjacent = 0
self.opposite = 0
self.hypotenuse = 0
self.area = 0
for property_type, property_value in zip(args[::2], args[1::2]):
setattr(self, property_type, property_value)
if not self.adjacent:
# calculate
elif not self.opposite:
# calculate
elif not self.hypotenuse:
# calculate
self.area = (this.opposite * this.adjacent) / 2
This would work with your current input, but let's agree - it's still not very elegant solution. So, let's use kwargs:
class RightAngleTriangle():
def __init__(self, adjacent=0, opposite=0, hypotenuse=0):
self.adjacent = adjacent
self.opposite = opposite
self.hypotenuse = hypotenuse
self.area = 0
if not self.adjacent:
# calculate
elif not self.opposite:
# calculate
elif not self.hypotenuse:
# calculate
self.area = (this.opposite * this.adjacent) / 2
And now you can simply call this code as:
t1 = RightAngleTriangle(adjacent=10, opposite=12)

Python - high disk usage in SumTree

I've encountered some weird behaviour of my python program. Basically when I tried to create adn fill a SumTree of length larger than 1000, my disk usage increases a lot to ~300MB/s then the programme died.
I'm pretty sure there's no file r/w involved in this process, and the problem is with the add function. The code is shown below.
import numpy as np
class SumTree():
trans_idx = 0
def __init__(self, capacity):
self.num_samples = 0
self.capacity = capacity
self.tree = np.zeros(2 * capacity - 1)
self.transitions = np.empty(self.capacity, dtype=object)
def add(self, p, experience):
tree_idx = self.trans_idx + self.capacity - 1
self.transitions[self.trans_idx] = experience
self.transitions.append(experience)
self.update(tree_idx, p)
self.trans_idx += 1
if self.trans_idx >= self.capacity:
self.trans_idx = 0
self.num_samples = min(self.num_samples + 1, self.capacity)
def update(self, tree_idx, p):
diff = p - self.tree[tree_idx]
self.tree[tree_idx] = p
while tree_idx != 0:
tree_idx = (tree_idx - 1) // 2
self.tree[tree_idx] += diff
def get_leaf(self, value):
parent_idx = 0
while True:
childleft_idx = 2 * parent_idx + 1
childright_idx = childleft_idx + 1
if childleft_idx >= len(self.tree):
leaf_idx = parent_idx
break
else:
if value <= self.tree[childleft_idx]:
parent_idx = childleft_idx
else:
value -= self.tree[childleft_idx]
parent_idx = childright_idx
data_idx = leaf_idx - self.capacity + 1
return leaf_idx, self.tree[leaf_idx], self.transitions[data_idx]
#property
def total_p(self):
return self.tree[0] # the root
#property
def volume(self):
return self.num_samples # number of transistions stored
Here's an example where this SumTree object will be used:
def add(self, experience)
max_p = np.max(self.tree.tree[-self.tree.capacity:])
if max_p == 0:
max_p = 1.0
exp = self.Experience(*experience)
self.tree.add(max_p, exp)
where Experience is a named tuple and self.tree is a Sumtree instance, when I removed the last line the high disk usage disappears.
Can anyone help me with this?
I finally sort this out because each experience is a tuple of namedtuple and I'm creating another namedtuple Experience from it. Fixed by changing experience to a tuple of numpy arrays.

Categories

Resources