SciPy, hagræðing með skilyrðum

SciPy, hagræðing með skilyrðum

SciPy (borið fram sai pie) er stærðfræðipakki sem byggir á numpy sem inniheldur einnig C og Fortran bókasöfn. SciPy breytir gagnvirku Python-lotunni þinni í fullkomið gagnafræðiumhverfi eins og MATLAB, IDL, Octave, R eða SciLab.

Í þessari grein munum við skoða grunntækni stærðfræðilegrar forritunar - að leysa skilyrt hagræðingarvandamál fyrir stigstærðfall af nokkrum breytum með því að nota scipy.optimize pakkann. Óþvinguð hagræðingaralgrím hefur þegar verið rædd í síðasta greinin. Ítarlegri og uppfærðari hjálp um scipy aðgerðir er alltaf hægt að fá með help() skipuninni, Shift+Tab eða í opinber skjöl.

Inngangur

Sameiginlegt viðmót til að leysa bæði skilyrt og óþvinguð hagræðingarvandamál í scipy.optimize pakkanum er veitt af aðgerðinni minimize(). Hins vegar er vitað að það er engin alhliða aðferð til að leysa öll vandamál, þannig að val á fullnægjandi aðferð, eins og alltaf, fellur á herðar rannsakandans.
Viðeigandi hagræðingaralgrím er tilgreint með því að nota fallrök minimize(..., method="").
Til skilyrtrar hagræðingar á falli nokkurra breyta eru útfærslur á eftirfarandi aðferðum tiltækar:

  • trust-constr — leitaðu að staðbundnu lágmarki á sjálfstraustssvæðinu. Wiki grein, grein um Habré;
  • SLSQP — raðbundin fjórðungsforritun með takmörkunum, Newtonsk aðferð til að leysa Lagrange kerfið. Wiki grein.
  • TNC - Styttur Newton takmarkaður, takmarkaður fjöldi endurtekningar, góður fyrir ólínuleg föll með miklum fjölda óháðra breyta. Wiki grein.
  • L-BFGS-B — aðferð frá Broyden–Fletcher–Goldfarb–Shanno teyminu, útfærð með minni minnisnotkun vegna hlutahleðslu vigra úr Hessian fylkinu. Wiki grein, grein um Habré.
  • COBYLA — MARE bundin hagræðing með línulegri nálgun, takmörkuð hagræðing með línulegri nálgun (án hallaútreiknings). Wiki grein.

Það fer eftir valinni aðferð, skilyrði og takmarkanir til að leysa vandamálið eru settar á annan hátt:

  • flokks hlutur Bounds fyrir aðferðir L-BFGS-B, TNC, SLSQP, trust-constr;
  • listinn (min, max) fyrir sömu aðferðir L-BFGS-B, TNC, SLSQP, trust-constr;
  • hlutur eða lista yfir hluti LinearConstraint, NonlinearConstraint fyrir COBYLA, SLSQP, trust-constr aðferðir;
  • orðabók eða lista yfir orðabækur {'type':str, 'fun':callable, 'jac':callable,opt, 'args':sequence,opt} fyrir COBYLA, SLSQP aðferðir.

Yfirlit greinar:
1) Íhugaðu notkun skilyrts hagræðingaralgríms á traustasvæðinu (aðferð = "trust-constr") með takmörkunum sem eru tilgreindar sem hlutir Bounds, LinearConstraint, NonlinearConstraint ;
2) Íhugaðu raðbundna forritun með því að nota minnstu ferningsaðferðina (aðferð = "SLSQP") með takmörkunum sem tilgreindar eru í formi orðabókar {'type', 'fun', 'jac', 'args'};
3) Greindu dæmi um hagræðingu á framleiddum vörum með því að nota dæmi um vefstofu.

Skilyrt fínstillingaraðferð = "trust-constr"

Innleiðing aðferðarinnar trust-constr byggt á EQSQP fyrir vandamál með hömlur á formi jafnréttis og áfram TRIP vegna vandamála með þvingunum í formi ójöfnuðar. Báðar aðferðirnar eru útfærðar með reikniritum til að finna staðbundið lágmark á öryggissvæðinu og henta vel fyrir stór vandamál.

Stærðfræðileg mótun vandamálsins við að finna lágmark í almennu formi:

SciPy, hagræðing með skilyrðum

SciPy, hagræðing með skilyrðum

SciPy, hagræðing með skilyrðum

Fyrir strangar jafnréttisþvinganir eru neðri mörkin sett jöfn efri mörkunum SciPy, hagræðing með skilyrðum.
Fyrir einstefnu þvingun eru efri eða neðri mörkin sett np.inf með tilheyrandi merki.
Látum það vera nauðsynlegt að finna lágmark þekkts Rosenbrock falls af tveimur breytum:

SciPy, hagræðing með skilyrðum

Í þessu tilviki eru eftirfarandi takmarkanir settar á skilgreiningarsvið þess:

SciPy, hagræðing með skilyrðum

SciPy, hagræðing með skilyrðum

SciPy, hagræðing með skilyrðum

SciPy, hagræðing með skilyrðum

SciPy, hagræðing með skilyrðum

SciPy, hagræðing með skilyrðum

Í okkar tilviki er einstök lausn á þeim tímapunkti SciPy, hagræðing með skilyrðum, þar sem aðeins fyrsta og fjórða takmörkunin gildir.
Við skulum fara í gegnum takmarkanirnar frá botni til topps og skoða hvernig við getum skrifað þær í scipy.
Takmarkanir SciPy, hagræðing með skilyrðum и SciPy, hagræðing með skilyrðum við skulum skilgreina það með Bounds hlutnum.

from scipy.optimize import Bounds
bounds = Bounds ([0, -0.5], [1.0, 2.0])

Takmarkanir SciPy, hagræðing með skilyrðum и SciPy, hagræðing með skilyrðum Við skulum skrifa það á línulegu formi:

SciPy, hagræðing með skilyrðum

Við skulum skilgreina þessar skorður sem LinearConstraint hlut:

import numpy as np
from scipy.optimize import LinearConstraint
linear_constraint = LinearConstraint ([[1, 2], [2, 1]], [-np.inf, 1], [1, 1])

Og að lokum ólínulega þvingunin í fylkisformi:

SciPy, hagræðing með skilyrðum

Við skilgreinum Jacobian fylkið fyrir þessa þvingun og línulega samsetningu af Hessian fylkinu með handahófskenndum vektor SciPy, hagræðing með skilyrðum:

SciPy, hagræðing með skilyrðum

SciPy, hagræðing með skilyrðum

Nú getum við skilgreint ólínulega þvingun sem hlut NonlinearConstraint:

from scipy.optimize import NonlinearConstraint

def cons_f(x):
     return [x[0]**2 + x[1], x[0]**2 - x[1]]

def cons_J(x):
     return [[2*x[0], 1], [2*x[0], -1]]

def cons_H(x, v):
     return v[0]*np.array([[2, 0], [0, 0]]) + v[1]*np.array([[2, 0], [0, 0]])

nonlinear_constraint = NonlinearConstraint(cons_f, -np.inf, 1, jac=cons_J, hess=cons_H)

Ef stærðin er stór, er einnig hægt að tilgreina fylki í dreifðu formi:

from scipy.sparse import csc_matrix

def cons_H_sparse(x, v):
     return v[0]*csc_matrix([[2, 0], [0, 0]]) + v[1]*csc_matrix([[2, 0], [0, 0]])

nonlinear_constraint = NonlinearConstraint(cons_f, -np.inf, 1,
                                            jac=cons_J, hess=cons_H_sparse)

eða sem hlutur LinearOperator:

from scipy.sparse.linalg import LinearOperator

def cons_H_linear_operator(x, v):
    def matvec(p):
        return np.array([p[0]*2*(v[0]+v[1]), 0])
    return LinearOperator((2, 2), matvec=matvec)

nonlinear_constraint = NonlinearConstraint(cons_f, -np.inf, 1,
                                jac=cons_J, hess=cons_H_linear_operator)

Þegar Hessian fylkið er reiknað út SciPy, hagræðing með skilyrðum krefst mikillar fyrirhafnar, þú getur notað bekk HessianUpdateStrategy. Eftirfarandi aðferðir eru í boði: BFGS и SR1.

from scipy.optimize import BFGS

nonlinear_constraint = NonlinearConstraint(cons_f, -np.inf, 1, jac=cons_J, hess=BFGS())

Hessian er einnig hægt að reikna með því að nota endanlegan mismun:

nonlinear_constraint = NonlinearConstraint (cons_f, -np.inf, 1, jac = cons_J, hess = '2-point')

Jacobian fylki fyrir þvingun er einnig hægt að reikna með því að nota endanlegan mismun. Hins vegar, í þessu tilviki, er ekki hægt að reikna Hessian fylkið með því að nota endanlegan mismun. Hessian verður að vera skilgreint sem fall eða með því að nota HessianUpdateStrategy flokkinn.

nonlinear_constraint = NonlinearConstraint (cons_f, -np.inf, 1, jac = '2-point', hess = BFGS ())

Lausnin á hagræðingarvandanum lítur svona út:

from scipy.optimize import minimize
from scipy.optimize import rosen, rosen_der, rosen_hess, rosen_hess_prod

x0 = np.array([0.5, 0])
res = minimize(rosen, x0, method='trust-constr', jac=rosen_der, hess=rosen_hess,
                constraints=[linear_constraint, nonlinear_constraint],
                options={'verbose': 1}, bounds=bounds)
print(res.x)

`gtol` termination condition is satisfied.
Number of iterations: 12, function evaluations: 8, CG iterations: 7, optimality: 2.99e-09, constraint violation: 1.11e-16, execution time: 0.033 s.
[0.41494531 0.17010937]

Ef nauðsyn krefur er hægt að skilgreina fallið til að reikna út Hessian með því að nota LinearOperator flokkinn

def rosen_hess_linop(x):
    def matvec(p):
        return rosen_hess_prod(x, p)
    return LinearOperator((2, 2), matvec=matvec)

res = minimize(rosen, x0, method='trust-constr', jac=rosen_der, hess=rosen_hess_linop,
                 constraints=[linear_constraint, nonlinear_constraint],
                 options={'verbose': 1}, bounds=bounds)

print(res.x)

eða margfeldi Hessíunnar og handahófskenndan vektor í gegnum færibreytuna hessp:

res = minimize(rosen, x0, method='trust-constr', jac=rosen_der, hessp=rosen_hess_prod,
                constraints=[linear_constraint, nonlinear_constraint],
                options={'verbose': 1}, bounds=bounds)
print(res.x)

Að öðrum kosti er hægt að nálgast fyrstu og aðra afleiðu fallsins sem verið er að fínstilla. Til dæmis er hægt að nálgast hessíuna með því að nota fallið SR1 (hálf-Newtonsk nálgun). Hægt er að nálgast hallann með endanlegum mismun.

from scipy.optimize import SR1
res = minimize(rosen, x0, method='trust-constr',  jac="2-point", hess=SR1(),
               constraints=[linear_constraint, nonlinear_constraint],
               options={'verbose': 1}, bounds=bounds)
print(res.x)

Skilyrt fínstillingaraðferð = "SLSQP"

SLSQP aðferðin er hönnuð til að leysa vandamál við að lágmarka aðgerð á formi:

SciPy, hagræðing með skilyrðum

SciPy, hagræðing með skilyrðum

SciPy, hagræðing með skilyrðum

SciPy, hagræðing með skilyrðum

Hvar SciPy, hagræðing með skilyrðum и SciPy, hagræðing með skilyrðum — sett af vísitölum orðasambanda sem lýsa takmörkunum í formi jafnréttis eða ójöfnuðar. SciPy, hagræðing með skilyrðum — sett af neðri og efri mörkum fyrir skilgreiningarsvið fallsins.

Línulegum og ólínulegum takmörkunum er lýst í formi orðabóka með lyklum type, fun и jac.

ineq_cons = {'type': 'ineq',
             'fun': lambda x: np.array ([1 - x [0] - 2 * x [1],
                                          1 - x [0] ** 2 - x [1],
                                          1 - x [0] ** 2 + x [1]]),
             'jac': lambda x: np.array ([[- 1.0, -2.0],
                                          [-2 * x [0], -1.0],
                                          [-2 * x [0], 1.0]])
            }

eq_cons = {'type': 'eq',
           'fun': lambda x: np.array ([2 * x [0] + x [1] - 1]),
           'jac': lambda x: np.array ([2.0, 1.0])
          }

Leitin að lágmarki fer fram sem hér segir:

x0 = np.array([0.5, 0])
res = minimize(rosen, x0, method='SLSQP', jac=rosen_der,
               constraints=[eq_cons, ineq_cons], options={'ftol': 1e-9, 'disp': True},
               bounds=bounds)

print(res.x)

Optimization terminated successfully.    (Exit mode 0)
            Current function value: 0.34271757499419825
            Iterations: 4
            Function evaluations: 5
            Gradient evaluations: 4
[0.41494475 0.1701105 ]

Hagræðingardæmi

Í tengslum við umskipti yfir í fimmta tækniskipulagið skulum við skoða framleiðsluhagræðingu með því að nota dæmi um vefstofu, sem færir okkur litlar en stöðugar tekjur. Ímyndum okkur að við séum forstöðumaður eldhúss sem framleiðir þrjár tegundir af vörum:

  • x0 - að selja áfangasíður, frá 10 tr.
  • x1 - fyrirtækjavefsíður, frá 20 st.
  • x2 - netverslanir, frá 30 st.

Vingjarnlega vinnuhópurinn okkar samanstendur af fjórum yngri flokkum, tveimur miðjumönnum og einum eldri. Mánaðarlegur vinnutímasjóður þeirra:

  • júní: 4 * 150 = 600 чел * час,
  • miðja: 2 * 150 = 300 чел * час,
  • Senor: 150 чел * час.

Leyfðu fyrsta yngri tiltæka að eyða (0, 1, 2) klukkustundum í þróun og dreifingu á einni síðu af gerðinni (x10, x20, x30), miðjan - (7, 15, 20), eldri - (5, 10, 15 ) klukkustundir af besta tíma lífs þíns.

Eins og allir venjulegir leikstjórar viljum við hámarka mánaðarlegan hagnað. Fyrsta skrefið til að ná árangri er að skrifa niður hlutlæga aðgerðina value sem fjárhæð tekna af framleiðsluvörum á mánuði:

def value(x):
    return - 10*x[0] - 20*x[1] - 30*x[2]

Þetta er ekki villa; þegar leitað er að hámarki er markmiðsfallið lágmarkað með öfugu formerki.

Næsta skref er að banna starfsfólki okkar að vinna of mikið og setja upp takmarkanir á vinnutíma:

SciPy, hagræðing með skilyrðum

Hvað jafngildir:

SciPy, hagræðing með skilyrðum

ineq_cons = {'type': 'ineq',
             'fun': lambda x: np.array ([600 - 10 * x [0] - 20 * x [1] - 30 * x[2],
                                         300 - 7  * x [0] - 15 * x [1] - 20 * x[2],
                                         150 - 5  * x [0] - 10 * x [1] - 15 * x[2]])
            }

Formleg takmörkun er sú að framleiðsla vöru verður aðeins að vera jákvæð:

bnds = Bounds ([0, 0, 0], [np.inf, np.inf, np.inf])

Og að lokum, bjartasta forsendan er sú að vegna lágs verðs og hágæða sé stöðugt röð af ánægðum viðskiptavinum í röð fyrir okkur. Við getum valið mánaðarlegt framleiðslumagn sjálf, byggt á því að leysa þvingaða hagræðingarvandann með scipy.optimize:

x0 = np.array([10, 10, 10])
res = minimize(value, x0, method='SLSQP', constraints=ineq_cons, bounds=bnds)
print(res.x)

[7.85714286 5.71428571 3.57142857]

Námundum lauslega að heilum tölum og reiknum út mánaðarlegt álag róðra með ákjósanlegri dreifingu afurða x = (8, 6, 3) :

  • júní: 8 * 10 + 6 * 20 + 3 * 30 = 290 чел * час;
  • miðja: 8 * 7 + 6 * 15 + 3 * 20 = 206 чел * час;
  • Senor: 8 * 5 + 6 * 10 + 3 * 15 = 145 чел * час.

Ályktun: Til þess að leikstjórinn fái verðskuldað hámark sitt er ákjósanlegt að búa til 8 áfangasíður, 6 meðalstórar síður og 3 verslanir á mánuði. Í þessu tilviki verður eldri að plægja án þess að horfa upp frá vélinni, álag miðjanna verður um það bil 2/3, yngri minna en helmingur.

Ályktun

Greinin útlistar helstu aðferðir til að vinna með pakkann scipy.optimize, notað til að leysa skilyrt lágmarksvandamál. Persónulega nota ég scipy eingöngu í fræðilegum tilgangi og þess vegna er dæmið sem gefið er af svo kómískum toga.

Mikið af kenningum og sýndardæmum er til dæmis að finna í bók I.L. Akulich „Stærðfræðiforritun í dæmum og vandamálum“. Meira harðkjarna forrit scipy.optimize að byggja upp þrívíddarbyggingu úr safni mynda (grein um Habré) er hægt að skoða í scipy-matreiðslubók.

Aðaluppspretta upplýsinga er docs.scipy.orgþeir sem vilja leggja sitt af mörkum við þýðingu þessa og annarra hluta scipy Velkomin til GitHub.

Takk mephistophees vegna þátttöku í gerð útgáfunnar.

Heimild: www.habr.com

Bæta við athugasemd