Используя массив NumPy и векторизацию, я пытаюсь создать популяцию n
разных лиц, каждый из которых имеет три свойства: alpha
, beta
и phenotype
(phenotype
рассчитывается как установившееся состояние дифференциального уравнения, которое включает alpha
и beta
). Поэтому я хочу, чтобы у каждого человека был свой фенотип.
Однако мой код создает одинаковый фенотип для каждого человека. Более того, это нежелательное поведение возникает только в том случае, если в solve_ivp
y0
имеется ровно n
записей (которые здесь [0, 1]
) - в противном случае возникает ошибка вещания:
ValueError: operands could not be broadcast together with shapes (2,) (3,)
Здесь код:
import numpy as np
from scipy.integrate import solve_ivp
def create_population(n):
"""creates a population of n individuals"""
pop = np.zeros(n, dtype=[('alpha','<f8'),('beta','<f8'),('phenotype','<f8')])
pop['alpha'] = np.random.randn(n)
pop['beta'] = np.random.randn(n) + 5
def phenotype(n):
"""creates the phenotype"""
def pheno_ode(t_ode, y):
"""defines the ode for the phenotype"""
dydt = 0.123 - y + pop['alpha'] * (y ** pop['beta'] / (1 + y ** pop['beta']))
return dydt
t_end = 1e06
sol = solve_ivp(pheno_ode, [0, t_end], [0, 1], method='BDF')
return sol.y[0][-1] # last entry is assumed to be the steady state
pop['phenotype'] = phenotype(n)
return pop
popul = create_population(3)
print(popul)
Напротив, если фенотип рассчитывается из alpha
и beta
через "простое" уравнение, то векторизация отлично работает:
def phenotype(n):
"""creates the phenotype"""
phenotype_simple = 2 * pop['alpha'] + pop['beta']
return phenotype_simple
Я вижу две проблемы:
Во-первых, у вас есть начальное условие для установки ODE на [0, 1]
. solve_ivp
размер векторного решения для solve_ivp
2, независимо от значения n
. Однако массивы pop['alpha']
и pop['beta']
имеют длину n
, а в вашем скрипте вы вызываете create_population
с n
установленным в 3. Таким образом, у вас есть несоответствие в фигурах массива в формуле для dydt
: y
имеет длину 2, но pop['alpha']
и pop['beta']
имеют длину 3. Это вызывает ошибку, которую вы видите.
Вы можете исправить это, используя, скажем, np.ones(n)
вместо [0, 1]
в качестве начального условия в своем вызове solve_ivp
.
Вторая проблема заключается в заявлении return sol.y[0][-1]
в функции phenotype(n)
. sol.y
имеет форму (n, num_points)
, где num_points
- количество точек, вычисляемых командой solve_ivp
. Итак, sol.y[0]
- это только первая компонента решения, а sol.y[0][-1]
- последнее значение решения для первой компоненты. Это скаляр, поэтому, когда вы выполняете pop['phenotype'] = phenotype(n)
, вы назначаете одинаковое значение (устойчивое состояние первого компонента) всем фенотипам.
Оператор return должен return sol.y[:, -1]
. Это возвращает последний столбец массива решений (т.е. все фенотипы устойчивого состояния).