Search code examples
pythonoptimizationscipy-optimizequad

Problem with optimizing a value function featuring integrals and fractional powers using Scipy quad and optimize


I am trying to run a dynamic optimization with Scipy, that features a definite integral with fixed bounds. However, the optimization fails to send back any value for my objective function, and report two issues (detailed report below) :

  • Roundoff error in the integral (which, if I understand correctly, should occur in case of a diverging integral)
  • Invalid value encountered in scalar power (which should only concern attempts to apply a fractionnal power to a negative number)

My code is :

import numpy as np
from scipy.integrate import quad
from scipy.stats import truncpareto
from scipy.optimize import minimize, Bounds, NonlinearConstraint

φ_min = .7                  # ACAP minimale
φ_max = 1.7                 # ACAP maximale
p = 1                       # Prix du bien manufacturier
α = 0.036                   # Part de l'énergie dans la production
ν =  0.27                   # Part du capital dans la production
η = 0.6                     # Part du travail dans la production
ρ = 1- α - η - ν            # Paramètre de rendements d'échelle décroissants
ω = 300                     # Paramètre d'échelle de l'apprentissage
γ = 0.271                   # Paramètre d'apprentissage
λ = 8                       # Productivité verte maximal
L = 192                     # Travail disponible
J = 1                       # Nombre de firmes 
δ = 0.1                     # Taux de dépréciation
a = 2.                      # Paramètre de forme (shape) de la loi de Pareto
Ad = 5                      # Productivité de la technologie brune
Pec = 130                   # Prix de l'énergie bas carbone
Ped = 35                    # Prix de l'énergie fossile
β = 0.03                    # Taux d'actualisation intertemporel
CO2 = 0.4                   # Facteur d'émission de l'énergie fossile
H0 = 5                      # Expérience initiale verte
S0 = 100                    # Budget carbone initial
T = 10                      #Horizon temporel


def Integrande(phi : float ,H : float) -> float :
    
    return (((phi*(ω**γ)+(λ*(H**γ)))/((H**γ)+(ω**γ)))**(1/ρ))*truncpareto.pdf(phi, c = φ_max/φ_min, scale = φ_min, b = a)

def EAc(H: float) -> float :

    return quad(Integrande, φ_min, φ_max, args=(H))[0]


def Zt(H : float, Tax : float, Sub : float) -> float :
    
    return ((Ad/((Ped+Tax)**α))**(1/ρ))+( EAc(H)/((1-Sub)**(ν/ρ)*Pec**(α/ρ)))
    

def Production(Tax : float, Sub : float, H : float) -> float:
    #c = (p**(α+ν)*(α**α)*(((β*ν)/δ)**ν)*L**η*(J**(-η/ρ)))**(1/(ρ+η))*
    
    return (Zt(H, Tax, Sub)**(ρ/(ρ+η)))*J

'transition dynamics'
def transition(States : np.ndarray , Controls : np.ndarray) -> float:
    H1, S1 = States
    Tax1, Sub1 = Controls
    # Budget carbone
    S_Next = S1 - CO2 * Zt(H1, Controls[0], Controls[1])**(-η/(ρ+η))*(Ad/((Ped + Controls[0])**(α+ρ)))**(1/ρ)*J #Fonction de conso d'énergie sale
    # Expérience verte
    H_Next = States[1] + (Zt(States[0], Controls[0], Controls[1])**(-η/(ρ+η)))*(EAc(Controls[1])/((1-Controls[1])**(ν/ρ)*Pec**(α/ρ)))*J
    
    return S_Next, H_Next


'Constraint'
def contrainte(Control_seq : np.ndarray) -> float : 
    States = np.array([S0, H0])
    Control_seq_reshaped = np.reshape(Control_seq,(T,2))
    for Controls in Control_seq_reshaped:
        BudgetC = transition(States,Controls)[0]
        States = transition(States, Controls)
    
    return BudgetC

'Optimization problem with objective function'
def Problème_opti(Control_seq : np.ndarray) -> float :
    Prod_totale = 0
    states = np.array([S0, H0])
    Control_seq_reshaped = np.reshape(Control_seq,(len(Control_seq)//2,2))
    t = 0
    
    for Controls in Control_seq_reshaped:
        Tax, Sub = Controls
        Prod_totale = (1-β)**t * Production(Tax, Sub, states[1])
        states = transition(states, Controls)
        t = t+1
    
    return -Prod_totale
    

def main() -> None :    
    result = minimize(
        fun = Problème_opti,
        x0 = np.zeros(2*T),
        constraints = NonlinearConstraint(contrainte, lb = 0, ub = np.inf),
        method = 'SLSQP'
        )
    
    return result.x, result.fun

print('optimal policy : ',main()[0])
print('Production totale', main()[1])

The console reports

c:\users\gal\dropbox\thèse\code\modèle dynamique v3.py:51: RuntimeWarning: invalid value encountered in scalar power
  return ((Ad/((Ped+Tax)**α))**(1/ρ))+( EAc(H)/((1-Sub)**(ν/ρ)*Pec**(α/ρ)))
c:\users\gal\dropbox\thèse\code\modèle dynamique v3.py:66: RuntimeWarning: invalid value encountered in scalar power
  H_Next = States[1] + (Zt(States[0], Controls[0], Controls[1])**(-η/(ρ+η)))*(EAc(Controls[1])/((1-Controls[1])**(ν/ρ)*Pec**(α/ρ)))*J
c:\users\gal\dropbox\thèse\code\modèle dynamique v3.py:46: IntegrationWarning: The occurrence of roundoff error is detected, which prevents 
  the requested tolerance from being achieved.  The error may be 
  underestimated.
  return quad(Integrande, φ_min, φ_max, args=(H))[0]
c:\users\gal\dropbox\thèse\code\modèle dynamique v3.py:64: RuntimeWarning: invalid value encountered in scalar power
  S_Next = S1 - CO2 * Zt(H1, Controls[0], Controls[1])**(-η/(ρ+η))*(Ad/((Ped + Controls[0])**(α+ρ)))**(1/ρ)*J #Fonction de conso d'énergie sale

and as final value of my objective function, the program reports 'nan'

I ran the objective function ('Problème-opti') with an arbitrary argument, by calling Problème_opti(np.array([1,2,3,4,5,6,7,8,9,1])) (and commenting away the "main()" section) and obtained a rather similar feedback :

c:\users\gal\dropbox\thèse\code\untitled1.py:47: RuntimeWarning: invalid value encountered in power
  return ((Ad/((Ped+Tax)**α))**(1/ρ))+( EAc(H)/((1-Sub)**(ν/ρ)*Pec**(α/ρ)))
c:\users\gal\dropbox\thèse\code\untitled1.py:63: RuntimeWarning: invalid value encountered in power
  H_Next = States[1] + (Zt(States[0], Controls[0], Controls[1])**(-η/(ρ+η)))*(EAc(Controls[1])/((1-Controls[1])**(ν/ρ)*Pec**(α/ρ)))
c:\users\gal\dropbox\thèse\code\untitled1.py:42: IntegrationWarning: The occurrence of roundoff error is detected, which prevents 
  the requested tolerance from being achieved.  The error may be 
  underestimated.
  return quad(Integrande, φ_min, φ_max, args=(H))[0]
c:\users\gal\dropbox\thèse\code\untitled1.py:63: RuntimeWarning: divide by zero encountered in scalar divide
  H_Next = States[1] + (Zt(States[0], Controls[0], Controls[1])**(-η/(ρ+η)))*(EAc(Controls[1])/((1-Controls[1])**(ν/ρ)*Pec**(α/ρ)))

I am rather confused, and I cannot see where I get this wrong. can you help me with figuring it out ?


Solution

  • It turns out one of the control variable should take its values between 0 and 1. The minimize function does not include such a restriction. Now I need to define a specific bound for it.