Конфликт между векторизацией / трансляцией и решением ODE с solve_ivp

1

Используя массив 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
Теги:
numpy
scipy

1 ответ

0
Лучший ответ

Я вижу две проблемы:

Во-первых, у вас есть начальное условие для установки 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]. Это возвращает последний столбец массива решений (т.е. все фенотипы устойчивого состояния).

  • 0
    Спасибо, Уоррен Векессер, за этот отличный ответ!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню