I want to improve the performance of this code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy
import random
import math
random.seed(42)
class TrainModel:
def __init__(cls, scheduling_interval, trains):
cls.scheduling_interval = scheduling_interval
cls.trains = trains
class Train:
def __init__( cls, legs):
cls.legs = legs
class Leg: # Teilfahrt
def __init__(cls, travel_time, earliest_departure_time, latest_departure_time,
power_profile):
cls.travel_time = travel_time # Fahrzeit
cls.earliest_departure_time = earliest_departure_time # Früheste Abfraht
cls.latest_departure_time = latest_departure_time # Späteste Abfahrt
cls.power_profile = power_profile # Leistungsprofil
# Test model
scheduling_interval = 60*24 # one day in minutes
train_number = 10
trains = []
for i in xrange(train_number):
legs = []
leg_number = random.randint(5,10)
for j in xrange(leg_number):
travel_time = random.randint(5,40)
earliest_departure_time = random.randint(0,scheduling_interval-100)
latest_departure_time = earliest_departure_time+7
power_profile = numpy.random.rand(travel_time*60+1)
legs.append(Leg(travel_time, earliest_departure_time, latest_departure_time, power_profile))
train = Train(legs)
trains.append(train)
train_model = TrainModel(scheduling_interval, trains)
def calc_middle(t, profile):
x_min = int(min((901-1)*(t+1), len(profile)-1))
sum_m = sum(a for a in profile[(901-1)*t+1:x_min] if a > 0)
sum_m += max(0.5 * profile[(901-1)*t], 0)
sum_m += max(0.5 * profile[x_min], 0)
return sum_m
storage = {}
# Slow loops profiling shows that most of the time is spend in the sum function of calc_middle
number_of_middle_intervals = int(math.ceil((train_model.scheduling_interval*60+1)/901))
for t in xrange(len(train_model.trains)):
for r in xrange(len(train_model.trains[t].legs)):
current_train = train_model.trains[t].legs[r]
for j in xrange(current_train.latest_departure_time - current_train.earliest_departure_time + 1):
full_profile = numpy.zeros(train_model.scheduling_interval*60+1)
dim_start = (current_train.earliest_departure_time+j)*60
dim_end = (current_train.earliest_departure_time+j+current_train.travel_time)*60+1
full_profile[dim_start:dim_end] = full_profile[dim_start:dim_end] + current_train.power_profile.clip(0)
power_value_per_middle_intervall = []
for i in xrange(number_of_middle_intervals):
x_min = int(min((901-1)*(i+1), len(full_profile)-1))
x_start = (901-1)*i
if x_min < dim_start or x_start > dim_end:
tmp_sum = 0
else:
tmp_sum = calc_middle(i, full_profile)
power_value = tmp_sum
power_value_per_middle_intervall.append(power_value)
storage[t,r,j] = power_value_per_middle_intervall
The loops does the following:
It loops through all trains and all legs of each train. Then for each departure time of the current leg it creates a big array full_profile
, which will hold for all time points in the scheduling interval the power consumption. It will put the small array current_train.power_profile
at the specific position.
Then it needs to calculate the middle values of 901 consecutive points, by the formula of calc_middle. (this means sum up all the 899 inner points and add 0.5 times the other outer 2 points, the x_min
is needed in case the last interval is shorter than 901 points). This needs to be done for all middle intervals, but you can improve the computation with the condition if x_min < dim_start or x_start > dim_end:
because you know that the middle interval will be zero then.
The next departure time of the same leg, will have the position, where the small array is inserted be shifted by 60 indexes, this means that the middle intervals which are affected might change. In general the big array is much bigger than the small array.
Note the big array is always scheduling_interval*60+1
big and the small array travel_time*60+1
.
EDIT
I included now a sample test model. I wrote it that you can vary the model size by increasing the train_number
. The parameters are all realistic, except the train_number
is now 10, but it can be up to 24000 in general.
train_model
so that A) your code works and B) we have a clue at what the input looks like. \$\endgroup\$