Мотивация
Взгляните на следующую картинку.
Учитывая кривую красного, синего и зеленого. Я бы хотел найти в каждой точке оси x доминирующую кривую. Это показано как черная графа на картинке. Из свойств красной, зеленой и синей кривой (возрастающей и постоянной через некоторое время) это сводится к тому, чтобы найти доминирующую кривую с правой стороны, а затем двигаться в сторону левой стороны, находить все точки пересечения и обновлять доминирующие кривая.
Эта общая задача должна быть решена T раза. В этой проблеме есть один последний поворот. Синяя, зеленая и красная кривые следующей итерации строятся через доминирующее решение из предыдущей итерации плюс некоторые различные параметры. В качестве примера на рисунке выше: Решение - черная функция. Эта функция используется для создания новой синей, зеленой и красной кривой. Затем проблема снова начнется, чтобы найти доминирующую для этих новых кривых и т.д.
Вопрос в двух словах
На каждой итерации я начинаю с фиксированной правой стороны и оцениваю все три функции, чтобы увидеть, какая из них является доминирующей. Эти оценки занимают больше времени и больше по сравнению с итерацией. Мое ощущение состоит в том, что я не передаю оптимально старой доминирующей функции, чтобы построить новую синюю, зеленую и красную кривые. Причина. Я получил более раннюю версию ошибки максимальной глубины рекурсии. Другие части кода, в которых требуется значение текущей доминирующей функции (которая является существенной или зеленой, красной или синей кривой), также занимают больше времени и больше с итерацией.
В течение 5 итераций возрастает только оценка функций в одной точке с правой стороны:
Результаты были получены через
test = A(5, 120000, 100000)
И затем запустите
test.find_all_intersections()
>>> test.find_all_intersections()
iteration 4
to compute function values it took
0.0102479457855
iteration 3
to compute function values it took
0.0134601593018
iteration 2
to compute function values it took
0.0294270515442
iteration 1
to compute function values it took
0.109843969345
iteration 0
to compute function values it took
0.823768854141
Я хотел бы знать, почему это так, и если можно запрограммировать его более эффективно.
Подробное описание кода
Я быстро обобщаю наиболее важные функции. Полный код можно найти ниже. Если есть какие-либо другие вопросы относительно кода, я более чем счастлив разработать/уточнить.
-
Метод
u: для повторяющейся задачи создания новой партии зеленой, красной и синей кривой выше, нам нужен старый доминирующий.u- это инициализация, которая будет использоваться в самой первой итерации. -
Метод
_function_template: функция генерирует версии зеленой, синей и красной кривой, используя разные параметры. Он возвращает функция одного входа. -
Метод
eval: Это основная функция для генерации синей, зеленой и красной версий каждый раз. Для каждой итерации требуется три различных параметра:vfunction, который является доминирующей функцией предыдущего шага,mиs, которые представляют собой два параметра (флеоты), влияющие на форму результирующей кривой. Остальные параметры одинаковы для каждой итерации. В коде есть значения выборки дляmиsдля каждой итерации. Для более вызывающих: он должен аппроксимировать интеграл, гдеmиs- ожидаемое среднее и стандартное отклонение основного нормального распределения. Аппроксимация выполняется через узлы Гаусса-Эрмита/веса. -
Метод
find_all_intersections: это основной метод нахождения в каждая итерация является доминирующей. Он строит доминирующую функции через кусочную мудрую конкатенацию синего, зеленого и красного кривая. Это достигается с помощью функцииpiecewise.
Вот полный код
import numpy as np
import pandas as pd
from scipy.optimize import brentq
import multiprocessing as mp
import pathos as pt
import timeit
import math
class A(object):
def u(self, w):
_w = np.asarray(w).copy()
_w[_w >= 120000] = 120000
_p = np.maximum(0, 100000 - _w)
return _w - 1000*_p**2
def __init__(self, T, upper_bound, lower_bound):
self.T = T
self.upper_bound = upper_bound
self.lower_bound = lower_bound
def _function_template(self, *args):
def _f(x):
return self.evalv(x, *args)
return _f
def evalv(self, w, c, vfunction, g, m, s, gauss_weights, gauss_nodes):
_A = np.tile(1 + m + math.sqrt(2) * s * gauss_nodes, (np.size(w), 1))
_W = (_A.T * w).T
_W = gauss_weights * vfunction(np.ravel(_W)).reshape(np.size(w),
len(gauss_nodes))
evalue = g*1/math.sqrt(math.pi)*np.sum(_W, axis=1)
return c + evalue
def find_all_intersections(self):
# the hermite gauss weights and nodes for integration
# and additional paramters used for function generation
gauss = np.polynomial.hermite.hermgauss(10)
gauss_nodes = gauss[0]
gauss_weights = gauss[1]
r = np.asarray([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1.])
m = [[0.038063407778193614, 0.08475713587463352, 0.15420895520972322],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.03836174909668277, 0.08543620707856969, 0.15548297423808233],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038063407778193614, 0.08475713587463352, 0.15420895520972322],
[0.038063407778193614, 0.08475713587463352, 0.15420895520972322],
[0.03836174909668277, 0.08543620707856969, 0.15548297423808233],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038063407778193614, 0.08475713587463352, 0.15420895520972322],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.03836174909668277, 0.08543620707856969, 0.15548297423808233],
[0.038063407778193614, 0.08475713587463352, 0.15420895520972322],
[0.038063407778193614, 0.08475713587463352, 0.15420895520972322],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.03836174909668277, 0.08543620707856969, 0.15548297423808233],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624]]
s = [[0.01945441966324046, 0.04690600929081242, 0.200125178687699],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019529101011406914, 0.04708607140891122, 0.20089341636351565],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.01945441966324046, 0.04690600929081242, 0.200125178687699],
[0.01945441966324046, 0.04690600929081242, 0.200125178687699],
[0.019529101011406914, 0.04708607140891122, 0.20089341636351565],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.01945441966324046, 0.04690600929081242, 0.200125178687699],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019529101011406914, 0.04708607140891122, 0.20089341636351565],
[0.01945441966324046, 0.04690600929081242, 0.200125178687699],
[0.01945441966324046, 0.04690600929081242, 0.200125178687699],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019529101011406914, 0.04708607140891122, 0.20089341636351565],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142]]
self.solution = []
n_cpu = mp.cpu_count()
pool = pt.multiprocessing.ProcessPool(n_cpu)
# this function is used for multiprocessing
def call_f(f, x):
return f(x)
# this function takes differences for getting cross points
def _diff(f_dom, f_other):
def h(x):
return f_dom(x) - f_other(x)
return h
# finds the root of two function
def find_roots(F, u_bound, l_bound):
try:
sol = brentq(F, a=l_bound,
b=u_bound)
if np.absolute(sol - u_bound) > 1:
return sol
else:
return l_bound
except ValueError:
return l_bound
# piecewise function
def piecewise(l_comp, l_f):
def f(x):
_ind_f = np.digitize(x, l_comp) - 1
if np.isscalar(x):
return l_f[_ind_f](x)
else:
return np.asarray([l_f[_ind_f[i]](x[i])
for i in range(0, len(x))]).ravel()
return f
_u = self.u
for t in range(self.T-1, -1, -1):
print('iteration' + ' ' + str(t))
l_bound, u_bound = 0.5*self.lower_bound, self.upper_bound
l_ordered_functions = []
l_roots = []
l_solution = []
# build all function variations
l_functions = [self._function_template(0, _u, r[t], m[t][i], s[t][i],
gauss_weights, gauss_nodes)
for i in range(0, len(m[t]))]
# get the best solution for the upper bound on the very
# right hand side of wealth interval
array_functions = np.asarray(l_functions)
start_time = timeit.default_timer()
functions_values = pool.map(call_f, array_functions.tolist(),
len(m[t]) * [u_bound])
elapsed = timeit.default_timer() - start_time
print('to compute function values it took')
print(elapsed)
ind = np.argmax(functions_values)
cross_points = len(m[t]) * [u_bound]
l_roots.insert(0, u_bound)
max_m = m[t][ind]
l_solution.insert(0, max_m)
# move from the upper bound twoards the lower bound
# and find the dominating solution by exploring all cross
# points.
test = True
while test:
l_ordered_functions.insert(0, array_functions[ind])
current_max = l_ordered_functions[0]
l_c_max = len(m[t]) * [current_max]
l_u_cross = len(m[t]) * [cross_points[ind]]
# Find new cross points on the smaller interval
diff = pool.map(_diff, l_c_max, array_functions.tolist())
cross_points = pool.map(find_roots, diff,
l_u_cross, len(m[t]) * [l_bound])
# update the solution, cross points and current
# dominating function.
ind = np.argmax(cross_points)
l_roots.insert(0, cross_points[ind])
max_m = m[t][ind]
l_solution.insert(0, max_m)
if cross_points[ind] <= l_bound:
test = False
l_ordered_functions.insert(0, l_functions[0])
l_roots.insert(0, 0)
l_roots[-1] = np.inf
l_comp = l_roots[:]
l_f = l_ordered_functions[:]
# build piecewise function which is used for next
# iteration.
_u = piecewise(l_comp, l_f)
_sol = pd.DataFrame(data=l_solution,
index=np.asarray(l_roots)[0:-1])
self.solution.insert(0, _sol)
return self.solution
