Ottimizzazione multi-obiettivo con Python: soluzioni unsupported, metodo ε-constraint e Knee Point — Parte 2

Cerca:

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors
Frontiera di Pareto_ scelte e compromessi

Un’azienda può aumentare i profitti accettando più emissioni, una startup può trovarsi davanti a un portafoglio di progetti efficiente che nessuna combinazione lineare dei suoi obiettivi riesce a selezionare e un data center può individuare un compromesso tra costo dell’energia e decarbonizzazione senza avere ancora elementi sufficienti per decidere se quella sia davvero la scelta migliore.

Sono problemi diversi, ma hanno una difficoltà in comune: trovare una soluzione ottima non basta, se il metodo utilizzato esclude alternative valide o se il modello non rappresenta le priorità reali dell’impresa.

Nella prima parte dedicata alla frontiera di Pareto, alla dominanza e al Knee Point con Python abbiamo visto come distinguere le soluzioni efficienti da quelle dominate, costruendo una rappresentazione delle alternative che consenta di confrontare obiettivi in conflitto. Ora facciamo un passo avanti, perché il problema diventa più interessante quando le decisioni sono indivisibili, i compromessi non possono essere recuperati attraverso una semplice somma pesata oppure la forma della frontiera influenza il risultato di un criterio geometrico.

Partiremo da un problema produttivo in cui profitto ed emissioni hanno un significato economico preciso, passeremo alla selezione di un portafoglio di progetti deep-tech per capire perché alcune soluzioni Pareto-efficienti non possono emergere da una funzione obiettivo pesata e arriveremo a un modello energetico nel quale il Knee Point aiuta a individuare una possibile zona di compromesso.

Ogni caso sarà sviluppato con formule e codice Python, ma l’attenzione non si fermerà al risultato numerico. Vedremo quali ipotesi lo sostengono, quali alternative possono essere escluse dal metodo scelto e quali informazioni servono prima di trasformare un risultato matematico in una decisione aziendale.

Pubblicità

Produzione metalmeccanica: profitto ed emissioni in conflitto

Un’azienda metalmeccanica che produce componenti per il settore automotive deve decidere come distribuire la produzione tra due linee. Aumentare i volumi può migliorare il risultato economico, ma la composizione del mix produttivo influenza anche le emissioni generate, perciò la direzione non può limitarsi a massimizzare il profitto senza valutare il costo ambientale delle diverse configurazioni.

Indichiamo con [math]x[/math] e [math]y[/math] le centinaia di unità prodotte ogni settimana sulle due linee. La capacità produttiva, la disponibilità di materie prime e i limiti operativi sono rappresentati dal sistema:

[math]\displaystyle \begin{cases}
x+y\leq10\\
2x+y\leq16\\
x\leq8\\
x,y\geq0
\end{cases}[/math]

L’azienda deve perseguire due obiettivi, che esprimiamo attraverso le funzioni:

[math]\max\pi(x,y)=5x+4y[/math]

[math]\min E(x,y)=2x+3y[/math]

Il profitto [math]\pi[/math] è espresso in migliaia di euro settimanali, mentre [math]E[/math] rappresenta le tonnellate di CO₂ associate alla produzione settimanale, secondo coefficienti ipotetici definiti per questo esercizio.

Il conflitto tra i due obiettivi dipende dal mix produttivo: la seconda linea genera più emissioni per unità di produzione, mentre la prima utilizza maggiormente la risorsa limitata rappresentata dal secondo vincolo. Non basta quindi aumentare o ridurre indistintamente i volumi, perché una diversa distribuzione della produzione può modificare sia il risultato economico sia l’impatto ambientale.

La regione ammissibile e i suoi vertici

Poiché i vincoli sono lineari, la regione ammissibile è un poligono convesso. Per individuarne i vertici occorre calcolare le intersezioni tra i vincoli e conservare soltanto quelle che rispettano tutte le condizioni operative.

I vertici ammissibili sono:

[math]\displaystyle (0,0),\quad(8,0),\quad(6,4),\quad(0,10)[/math]

L’intersezione tra le rette [math]x+y=10[/math] e [math]2x+y=16[/math] si ottiene sottraendo la prima equazione dalla seconda, da cui [math]x=6[/math] e, sostituendo, [math]y=4[/math]. L’intersezione [math](10,0)[/math], invece, non è ammissibile perché viola il vincolo [math]2x+y\leq16[/math], mentre il punto [math](8,0)[/math] rispetta tutti i vincoli ed è quindi un vertice valido.

Calcolando profitto ed emissioni in corrispondenza dei quattro vertici, otteniamo:

Piano produttivo [math](x,y)[/math] Profitto (k€) Emissioni (t CO₂)
(0,0) 0 0
(8,0) 40 16
(6,4) 46 24
(0,10) 40 30

Il piano [math](6,4)[/math] garantisce il profitto massimo, pari a 46.000 euro settimanali, ma genera più emissioni del piano [math](8,0)[/math], che si ferma a 40.000 euro. Il piano [math](0,10)[/math], invece, produce lo stesso profitto di [math](8,0)[/math] con emissioni decisamente superiori, circostanza che lo rende meno interessante sotto entrambi i criteri di confronto.

Questa prima valutazione evidenzia una distinzione fondamentale: rispettare i vincoli produttivi significa essere ammissibili, mentre appartenere alla frontiera di Pareto significa non essere migliorabili su un obiettivo senza peggiorare almeno uno degli altri. Un piano tecnicamente realizzabile può quindi essere escluso dalle alternative efficienti prima ancora di discutere quale soluzione preferisca il management.

La frontiera di Pareto nello spazio degli obiettivi

Per studiare il compromesso, rappresentiamo ogni piano nello spazio [math](E,\pi)[/math], dove le emissioni sono sull’asse orizzontale e il profitto su quello verticale. I quattro vertici diventano:

[math]\displaystyle (0,0),\quad(16,40),\quad(24,46),\quad(30,40)[/math]

Il punto [math](30,40)[/math], corrispondente al piano produttivo [math](0,10)[/math], è dominato da [math](16,40)[/math], perché quest’ultimo garantisce lo stesso profitto con 14 tonnellate di CO₂ in meno. Il piano [math](0,10)[/math] può quindi essere escluso dalla frontiera efficiente senza alcuna perdita per il decisore.

Gli altri tre vertici sono non dominati: l’origine minimizza le emissioni, il piano [math](8,0)[/math] offre un profitto di 40.000 euro con emissioni relativamente contenute e il piano [math](6,4)[/math] massimizza il risultato economico.

La frontiera continua non è però composta soltanto da questi tre punti, perché le variabili produttive possono assumere valori reali. Comprende anche i segmenti che collegano i vertici efficienti, lungo i quali ogni miglioramento del profitto richiede un aumento delle emissioni.

Sul segmento che unisce [math](0,0)[/math] a [math](16,40)[/math], la relazione tra profitto ed emissioni è:

[math]\pi=2{,}5E[/math]

Sul segmento che unisce [math](16,40)[/math] a [math](24,46)[/math], invece, vale:

[math]\pi=28+0{,}75E[/math]

Le pendenze esprimono il rendimento economico marginale associato alle emissioni: nel primo tratto, un aumento unitario delle emissioni è associato a 2.500 euro di profitto aggiuntivo, mentre nel secondo il guadagno scende a 750 euro. La convenienza economica di un aumento delle emissioni non è quindi costante lungo la frontiera.

Per la direzione aziendale, questo significa che la decisione non dovrebbe limitarsi al confronto tra profitto massimo e profitto minimo, perché anche il beneficio incrementale delle configurazioni intermedie può cambiare in modo sostanziale.

Quando conviene usare una somma pesata

Un modo per tradurre le preferenze aziendali in una regola operativa consiste nel massimizzare una funzione che sottragga al profitto una penalità proporzionale alle emissioni:

[math]\max\bigl[\pi-\lambda E\bigr][/math]

Il parametro [math]\lambda[/math] rappresenta il valore attribuito alla riduzione delle emissioni ed è espresso in migliaia di euro per tonnellata di CO₂. Con [math]\lambda=0[/math], l’azienda considera soltanto il profitto; aumentando [math]\lambda[/math], rende progressivamente più costose le emissioni nella funzione obiettivo.

Confrontando le pendenze dei due segmenti efficienti, otteniamo tre situazioni:

  • per [math]0\leq\lambda<0{,}75[/math], è ottimo il piano [math](6,4)[/math], che massimizza il profitto;
  • per [math]0{,}75<\lambda<2{,}5[/math], è ottimo il piano [math](8,0)[/math];
  • per [math]\lambda>2{,}5[/math], diventa ottima la produzione nulla.

In corrispondenza di [math]\lambda=0{,}75[/math], tutti i punti del segmento tra [math](8,0)[/math] e [math](6,4)[/math] sono ottimi per la funzione pesata, mentre con [math]\lambda=2{,}5[/math] sono ottimi tutti i punti del segmento tra [math](0,0)[/math] e [math](8,0)[/math].

I pesi non sono quindi semplici parametri tecnici da scegliere senza una motivazione, perché esprimono una preferenza economica che deve essere giustificata attraverso i costi ambientali, i vincoli normativi e le priorità dell’impresa.

Verifica con Python

Il codice seguente costruisce la regione ammissibile, calcola i vertici e confronta i risultati della funzione pesata per diversi valori di [math]\lambda[/math]. Le funzioni necessarie sono definite nel blocco, così da poterlo eseguire autonomamente in Colab o in un ambiente Python con NumPy installato.

import numpy as np
from itertools import combinations

def enumerate_vertices(A, b, tol=1e-9):
    """Enumera i vertici di A @ x <= b, con x >= 0."""
    A2 = np.vstack([A, [[-1, 0], [0, -1]]])
    b2 = np.concatenate([b, [0, 0]])
    vertices = set()

    for i, j in combinations(range(len(b2)), 2):
        M = A2[[i, j]]

        if abs(np.linalg.det(M)) < 1e-12:
            continue

        x = np.linalg.solve(M, b2[[i, j]])

        if np.all(A2 @ x <= b2 + tol):
            coords = tuple(
                0.0 if abs(float(v)) < tol
                else round(float(v), 9)
                for v in x
            )
            vertices.add(coords)

    return sorted(vertices)

def profit(v):
    return 5 * v[0] + 4 * v[1]

def emissions(v):
    return 2 * v[0] + 3 * v[1]

def weighted_sum_scan(vertices, lambdas, tol=1e-9):
    """Massimizza profitto - lambda * emissioni sui vertici."""
    results = []

    for lam in lambdas:
        scores = [
            (profit(v) - lam * emissions(v), v)
            for v in vertices
        ]

        best = max(score for score, _ in scores)

        optima = [
            v for score, v in scores
            if abs(score - best) < tol
        ]

        results.append((lam, optima))

    return results

A = np.array([
    [1, 1],
    [2, 1],
    [1, 0]
])
b = np.array([10, 16, 8])

vertices = enumerate_vertices(A, b)

print("Vertici ammissibili:")
for v in vertices:
    print(
        f"{v}: profitto={profit(v):.1f} k€, "
        f"emissioni={emissions(v):.1f} t"
    )

print("\nRisultati della funzione pesata:")
lambdas = [0, 0.5, 0.75, 1.5, 2.5, 3]

for lam, optima in weighted_sum_scan(vertices, lambdas):
    print(f"lambda={lam}: ottimi={optima}")

L’output sui vertici ammissibili è il seguente:

Vertici ammissibili:
(0.0, 0.0): profitto=0.0 k€, emissioni=0.0 t
(0.0, 10.0): profitto=40.0 k€, emissioni=30.0 t
(6.0, 4.0): profitto=46.0 k€, emissioni=24.0 t
(8.0, 0.0): profitto=40.0 k€, emissioni=16.0 t

Il programma conferma i valori ottenuti analiticamente e mostra che la soluzione con profitto massimo è [math](6,4)[/math], mentre il piano [math](0,10)[/math] resta ammissibile ma inefficiente.

Forse potrebbe interessarti anche:  Programmazione Lineare Multiobiettivo (PLMO): Soluzione Completa con Determinazione Efficienza Pareto e Regione Ammissibile

La scansione dei pesi produce questi risultati:

lambda=0: ottimi=[(6.0, 4.0)]
lambda=0.5: ottimi=[(6.0, 4.0)]
lambda=0.75: ottimi=[(6.0, 4.0), (8.0, 0.0)]
lambda=1.5: ottimi=[(8.0, 0.0)]
lambda=2.5: ottimi=[(0.0, 0.0), (8.0, 0.0)]
lambda=3: ottimi=[(0.0, 0.0)]

Quando il peso è pari a 0,75 oppure a 2,5, il codice restituisce soltanto i vertici ottimi, mentre nella formulazione continua tutti i punti del segmento pertinente sono ottimi, perché la funzione obiettivo rimane costante lungo quel tratto. Gli estremi stampati non rappresentano quindi l’intero insieme delle soluzioni ottime in corrispondenza delle soglie critiche.

La verifica numerica è utile per controllare i calcoli e comprendere come cambino gli ottimi al variare dei pesi, ma non sostituisce l’analisi della frontiera continua, soprattutto quando il decisore vuole confrontare tutte le configurazioni produttive possibili.

Che cosa significa per la direzione aziendale

Il risultato più importante non è semplicemente che il piano [math](6,4)[/math] garantisca il profitto massimo, ma che il rendimento economico marginale associato alle emissioni cambi lungo la frontiera. Se la direzione attribuisce alle emissioni un costo implicito sufficientemente elevato, la scelta può spostarsi verso il piano [math](8,0)[/math] e, con una penalità ancora maggiore, fino all’interruzione della produzione.

Nella pratica, prima di adottare questa funzione obiettivo occorrerebbe stimare un valore economico credibile per le emissioni, considerando eventuali costi regolatori, investimenti di abbattimento, contratti di fornitura e obiettivi di sostenibilità. La somma pesata aiuta a tradurre queste preferenze in una regola di scelta, ma non stabilisce autonomamente quanto debba valere una tonnellata di CO₂ per l’azienda.

Il metodo è adatto a questo caso perché il problema è lineare e la regione ammissibile è convessa; il quadro cambia, invece, quando le decisioni sono discrete e l’insieme delle alternative non presenta più la stessa struttura geometrica.

Pubblicità

Selezione del portafoglio deep-tech: quando la somma pesata perde una soluzione efficiente

Una startup tecnologica dispone di un budget massimo di un milione di euro, suddiviso in dieci unità da 100.000 euro ciascuna. Il comitato direttivo deve selezionare alcuni progetti interni, senza possibilità di finanziarli parzialmente, cercando di massimizzare sia il valore economico atteso sia l’impatto strategico.

I progetti sono descritti dalla tabella seguente, dove i costi sono espressi in unità da 100.000 euro, l’NPV in milioni di euro e l’impatto strategico su una scala ipotetica da 0 a 10.

Progetto Costo (unità) NPV (M€) Impatto
P1 4 5 5
P3 6 9 2
P4 6 2 9
P5 3 4 4

Le variabili decisionali sono binarie: ogni progetto viene finanziato oppure escluso. Indicando con [math]z_i\in{0,1}[/math] la decisione relativa al progetto [math]i[/math], il vincolo di budget è:

[math]4z_1+6z_3+6z_4+3z_5\leq10[/math]

I due obiettivi sono:

[math]\max \text{NPV}=\sum_i \text{NPV}_i z_i[/math]

[math]\max I=\sum_i I_i z_i[/math]

dove [math]I[/math] rappresenta la somma dei punteggi di impatto dei progetti selezionati.

Per semplicità, assumiamo che NPV e impatto siano additivi e che non esistano dipendenze o sinergie tra i progetti, così che il valore del portafoglio possa essere calcolato sommando i contributi delle singole iniziative. In un’applicazione reale, questa ipotesi dovrebbe essere verificata, perché la complementarità tecnologica, i costi condivisi e i rischi correlati possono modificare sensibilmente la convenienza di una combinazione.

Enumerare le combinazioni ammissibili

A differenza del caso produttivo precedente, qui non possiamo spostarci liberamente lungo un segmento tra due soluzioni, perché i progetti sono indivisibili e le combinazioni ammissibili formano un insieme finito di punti.

Le combinazioni singole hanno profili [math](\text{NPV},I)[/math] pari a [math](5,5)[/math], [math](9,2)[/math], [math](2,9)[/math] e [math](4,4)[/math]. Tra le coppie, rispettano il budget le seguenti:

Portafoglio Costo NPV Impatto
P1 + P3 10 14 7
P1 + P4 10 7 14
P1 + P5 7 9 9
P3 + P5 9 13 6
P4 + P5 9 6 13

La coppia P3 + P4 costa 12 unità e supera il budget, mentre tutte le combinazioni di tre progetti richiedono almeno 13 unità. Includiamo anche il portafoglio vuoto, con costo, NPV e impatto pari a zero.

Dopo aver eliminato i portafogli dominati, la frontiera di Pareto è composta da tre soluzioni:

[math]\displaystyle (7,14),\quad(9,9),\quad(14,7)[/math]

La prima privilegia l’impatto strategico, la terza massimizza il valore economico atteso e la soluzione intermedia distribuisce le risorse tra progetti con caratteristiche diverse. Tutte e tre sono efficienti, perché nessun altro portafoglio ammissibile migliora almeno uno dei due obiettivi senza peggiorare l’altro.

Il portafoglio [math](9,9)[/math] non è il migliore in termini di NPV né il migliore in termini di impatto, ma questo non lo rende inferiore alle altre soluzioni: il suo valore sta proprio nel bilanciamento tra i due criteri.

La soluzione unsupported: il limite della somma pesata

Un comitato potrebbe assegnare un peso [math]\lambda[/math] al valore economico e [math]1-\lambda[/math] all’impatto, massimizzando:

[math]S=\lambda\,\text{NPV}+(1-\lambda)I,\quad 0\leq\lambda\leq1[/math]

Questa formulazione sembra naturale, perché permette di aumentare o ridurre l’importanza relativa dei due obiettivi. Nel caso in esame, tuttavia, presenta un limite importante: non esiste alcun valore di [math]\lambda[/math] che renda il portafoglio [math](9,9)[/math] ottimo rispetto all’intero insieme ammissibile.

Perché [math](9,9)[/math] sia almeno altrettanto conveniente di [math](7,14)[/math], deve valere:

[math]9\geq7\lambda+14(1-\lambda)[/math]

da cui:

[math]\displaystyle \lambda\geq\frac57[/math]

Perché lo stesso portafoglio sia almeno altrettanto conveniente di [math](14,7)[/math], invece, occorre:

[math]9\geq14\lambda+7(1-\lambda)[/math]

da cui:

[math]\displaystyle \lambda\leq\frac27[/math]

Le due condizioni sono incompatibili, perché non esiste un peso che sia contemporaneamente maggiore o uguale a [math]5/7[/math] e minore o uguale a [math]2/7[/math]. Il portafoglio [math](9,9)[/math] non può quindi essere selezionato come ottimo dalla funzione pesata.

Questa conclusione non significa che il portafoglio sia inefficiente, ma che è una soluzione unsupported, cioè una soluzione Pareto-efficiente che non può essere resa ottima da alcuna combinazione lineare dei due obiettivi con pesi non negativi.

Il motivo risiede nella geometria dell’insieme discreto: la frontiera presenta un punto efficiente che non si trova sul tratto esposto selezionato da una retta di supporto. Cambiare i pesi non basta a recuperarlo, perché il limite non dipende da una scelta poco accurata dei parametri, ma dalla struttura delle alternative disponibili.

Per un comitato aziendale, questa è una distinzione sostanziale. Se si presume che ogni compromesso efficiente possa essere recuperato attraverso una somma pesata, si rischia di ignorare portafogli che bilanciano in modo utile redditività e impatto strategico.

Il metodo ε-constraint: fissare una priorità esplicita

Un’alternativa consiste nel fissare una soglia minima per un obiettivo e ottimizzare l’altro. Se il comitato stabilisce che il valore economico atteso non possa scendere sotto 9 milioni di euro, il problema diventa:

[math]\displaystyle \begin{cases}
\max I\\
\text{NPV}\geq9\\
\text{Costo}\leq10
\end{cases}[/math]

Tra i portafogli che rispettano entrambe le condizioni, [math](9,9)[/math] offre l’impatto più alto. La soluzione P1 + P5 viene quindi individuata senza dover trovare un sistema di pesi che, in questo caso, non esiste.

Il metodo ε-constraint consente di esplorare la frontiera fissando progressivamente soglie diverse per un obiettivo, mentre l’altro viene ottimizzato. La soglia non è neutrale, perché dipende dalle priorità aziendali e dalla qualità delle stime economiche, ma rende esplicito il requisito che il decisore vuole proteggere.

Nel nostro esempio, la soglia di 9 milioni non è un risultato prodotto dall’algoritmo, bensì una condizione scelta dal comitato. L’ottimizzazione risponde a una domanda precisa: tra i portafogli che garantiscono almeno quel rendimento atteso, quale offre l’impatto maggiore?

Codice Python completo per il portafoglio

Il programma seguente genera tutti i portafogli ammissibili, individua la frontiera di Pareto e applica sia la somma pesata sia il metodo ε-constraint. Per quattro progetti, l’enumerazione esaustiva permette di verificare ogni combinazione e di confrontare direttamente i risultati.

from itertools import product

projects = {
    "P1": {"cost": 4, "npv": 5, "imp": 5},
    "P3": {"cost": 6, "npv": 9, "imp": 2},
    "P4": {"cost": 6, "npv": 2, "imp": 9},
    "P5": {"cost": 3, "npv": 4, "imp": 4},
}

BUDGET = 10

def feasible_portfolios(projects, budget):
    names = list(projects)
    portfolios = []

    for choices in product([0, 1], repeat=len(names)):
        selected = [
            names[i] for i, z in enumerate(choices) if z
        ]

        cost = sum(projects[p]["cost"] for p in selected)

        if cost > budget:
            continue

        portfolios.append({
            "projects": selected,
            "cost": cost,
            "npv": sum(
                projects[p]["npv"] for p in selected
            ),
            "imp": sum(
                projects[p]["imp"] for p in selected
            ),
        })

    return portfolios

def pareto_max(portfolios):
    front = []

    for p in portfolios:
        dominated = any(
            q["npv"] >= p["npv"]
            and q["imp"] >= p["imp"]
            and (
                q["npv"] > p["npv"]
                or q["imp"] > p["imp"]
            )
            for q in portfolios
        )

        if not dominated:
            front.append(p)

    return sorted(front, key=lambda p: p["npv"])

def weighted_sum(portfolios, lam):
    """Massimizza lambda * NPV + (1-lambda) * impatto."""
    return max(
        portfolios,
        key=lambda p:
            lam * p["npv"] + (1 - lam) * p["imp"]
    )

def epsilon_constraint(portfolios, eps_npv):
    """Massimizza l'impatto imponendo NPV >= eps_npv."""
    candidates = [
        p for p in portfolios if p["npv"] >= eps_npv
    ]

    if not candidates:
        return None

    return max(candidates, key=lambda p: p["imp"])

feasible = feasible_portfolios(projects, BUDGET)
front = pareto_max(feasible)

print("Frontiera di Pareto:")
for p in front:
    print(
        p["projects"],
        f'Costo={p["cost"]}',
        f'NPV={p["npv"]}',
        f'Impatto={p["imp"]}'
    )

print("\nSoluzione con epsilon-constraint:")
print(epsilon_constraint(feasible, 9))

print("\nSoluzioni della somma pesata:")
for lam in [0, 0.25, 0.5, 0.75, 1]:
    print(lam, weighted_sum(feasible, lam))

L’output della frontiera è:

Frontiera di Pareto:
['P1', 'P4'] Costo=10 NPV=7 Impatto=14
['P1', 'P5'] Costo=7 NPV=9 Impatto=9
['P1', 'P3'] Costo=10 NPV=14 Impatto=7

Il risultato conferma che il portafoglio P1 + P5 appartiene alla frontiera, anche se non massimizza nessuno dei due obiettivi separatamente. Dal punto di vista aziendale, rimane un’alternativa valida perché non esiste una combinazione ammissibile che ne migliori simultaneamente NPV e impatto.

Forse potrebbe interessarti anche:  Ottimizzazione Multi-Obiettivo: Come Bilanciare Profitto, Rischio e Sostenibilità con Python

Il metodo ε-constraint restituisce:

Soluzione con epsilon-constraint:
{'projects': ['P1', 'P5'], 'cost': 7, 'npv': 9, 'imp': 9}

La soluzione utilizza sette delle dieci unità di budget disponibili, garantisce l’NPV minimo richiesto e massimizza l’impatto tra i portafogli che rispettano la soglia. Le tre unità residue non sono necessariamente un errore: indicano che, con i progetti e i vincoli disponibili, spendere tutto il budget non è indispensabile per ottenere il miglior impatto compatibile con il rendimento richiesto.

La scansione di alcuni pesi produce invece:

Soluzioni della somma pesata:
0 {'projects': ['P1', 'P4'], 'cost': 10, 'npv': 7, 'imp': 14}
0.25 {'projects': ['P1', 'P4'], 'cost': 10, 'npv': 7, 'imp': 14}
0.5 {'projects': ['P1', 'P4'], 'cost': 10, 'npv': 7, 'imp': 14}
0.75 {'projects': ['P1', 'P3'], 'cost': 10, 'npv': 14, 'imp': 7}
1 {'projects': ['P1', 'P3'], 'cost': 10, 'npv': 14, 'imp': 7}

Il codice mostra come i pesi estremi selezionino portafogli che privilegiano rispettivamente l’impatto o l’NPV, mentre il valore intermedio pari a 0,5 presenta un pareggio tra i due portafogli estremi. La funzione max restituisce il primo portafoglio che raggiunge il massimo nell’ordine di enumerazione, perciò l’output mostra una sola soluzione in caso di parità.

Questa scansione, da sola, non dimostra che il portafoglio [math](9,9)[/math] sia irraggiungibile con qualsiasi peso: tale conclusione deriva dalle due disuguaglianze dimostrate analiticamente. Il codice serve a controllare il comportamento della funzione obiettivo per alcuni valori esemplificativi, mentre la prova matematica stabilisce il limite generale.

L’enumerazione esaustiva è comoda per quattro progetti, perché permette di verificare direttamente tutte le combinazioni. Con [math]n[/math] progetti binari, tuttavia, il numero di sottoinsiemi cresce come [math]2^n[/math]; con 30 progetti si arriva a oltre un miliardo di combinazioni, rendendo necessarie strategie più efficienti, come la programmazione dinamica per problemi di tipo knapsack, i modelli di programmazione intera o algoritmi multi-obiettivo dedicati.

Che cosa cambia se il budget aumenta

Se il budget passa da 10 a 12 unità, diventa ammissibile anche il portafoglio P3 + P4, con costo 12, NPV pari a 11 e impatto pari a 11. Questa combinazione domina [math](9,9)[/math], perché migliora entrambi gli obiettivi.

Il cambiamento mostra che la frontiera dipende dall’insieme delle decisioni ammissibili: una soluzione efficiente con un certo budget può diventare dominata quando vengono rese disponibili nuove combinazioni. Non viene cancellata la sua validità per il problema originario, ma cambia il problema decisionale che l’azienda deve risolvere.

Dal punto di vista manageriale, non basta chiedersi quali progetti siano interessanti singolarmente, perché il valore di un portafoglio dipende dalla combinazione delle iniziative, dal vincolo finanziario e dai criteri utilizzati per confrontarle. Un aumento del budget può migliorare le possibilità di scelta, ma non garantisce che ogni progetto aggiuntivo sia utile o che tutte le combinazioni più costose siano preferibili.

Pubblicità

Mix energetico di un data center: trovare un Knee Point che abbia senso economico

Un data center di grandi dimensioni deve decidere quale quota dell’energia acquistare da fonti rinnovabili. Aumentare tale quota può richiedere contratti più costosi, investimenti per integrare fonti intermittenti o sistemi di accumulo, mentre riduce le emissioni associate all’elettricità utilizzata.

Per studiare questo compromesso, definiamo [math]x\in[0,1][/math] come quota di approvvigionamento da rinnovabili e utilizziamo un modello economico ipotetico:

[math]C(x)=80+60(1-x)^2+80x^2[/math]

[math]E(x)=400(1-x)[/math]

Il costo [math]C[/math] è espresso in euro per MWh e le emissioni [math]E[/math] in chilogrammi di CO₂ per MWh. Il termine [math]60(1-x)^2[/math] rappresenta una componente di costo associata alla dipendenza dall’approvvigionamento non rinnovabile, mentre [math]80x^2[/math] rappresenta un costo crescente di integrazione delle rinnovabili.

Le funzioni sono state costruite per finalità didattiche e non costituiscono una stima empirica del costo energetico di un data center reale. Questa precisazione è importante perché i risultati numerici dipendono dalla forma delle funzioni: un modello in cui il costo diminuisse sempre con la quota rinnovabile potrebbe condurre a una soluzione completamente diversa.

Individuare l’intervallo Pareto-efficiente

Derivando la funzione di costo rispetto a [math]x[/math], otteniamo:

[math]C'(x)=-120(1-x)+160x=-120+280x[/math]

Il costo minimo si verifica quando [math]C'(x)=0[/math], quindi:

[math]\displaystyle x_{\min}=\frac{3}{7}\approx42{,}86\%[/math]

Per quote inferiori a questa soglia, aumentare la componente rinnovabile riduce sia il costo sia le emissioni, rendendo le soluzioni precedenti dominate. La frontiera di Pareto si trova quindi nell’intervallo:

[math]\displaystyle \frac37\leq x\leq1[/math]

Agli estremi della frontiera abbiamo due situazioni differenti: con [math]x=3/7[/math], il costo è minimo e vale circa 114,29 €/MWh, ma le emissioni sono circa 228,57 kgCO₂/MWh; con [math]x=1[/math], le emissioni operative del modello sono nulle, ma il costo sale a 160 €/MWh.

Nessuno dei due estremi domina l’altro, perché il primo minimizza il costo e il secondo minimizza le emissioni. Le soluzioni intermedie rappresentano alternative efficienti tra cui scegliere in funzione delle priorità aziendali.

È importante osservare che il modello non considera tutte le emissioni del ciclo di vita dell’energia, ma rappresenta le emissioni associate alla quota non rinnovabile secondo una relazione lineare ipotizzata. Il valore zero raggiunto a [math]x=1[/math] è quindi un risultato interno al modello, non una dichiarazione che l’approvvigionamento reale sia privo di qualsiasi impatto ambientale.

Esprimere il costo in funzione delle emissioni

Poiché [math]E=400(1-x)[/math], possiamo ricavare:

[math]\displaystyle x=1-\frac{E}{400}[/math]

Sostituendo nella funzione di costo otteniamo:

[math]C(E)=160-0{,}4E+0{,}000875E^2[/math]

Questa espressione descrive il costo lungo la frontiera efficiente, con [math]E[/math] compreso tra zero e circa 228,57 kgCO₂/MWh. La curva mostra che, una volta superato il minimo economico, ridurre ulteriormente le emissioni comporta un aumento del costo, ma il costo marginale della decarbonizzazione non rimane costante.

Questa relazione consente di ragionare sul compromesso senza concentrarsi esclusivamente sulla quota rinnovabile: il decisore può confrontare direttamente il costo associato a diversi livelli di emissioni e valutare quanto dovrebbe spendere per raggiungere un obiettivo ambientale più ambizioso.

Calcolare il Knee Point

Per identificare un possibile punto di compromesso adottiamo un criterio geometrico preciso: cerchiamo il punto della frontiera che presenta la massima distanza verticale dalla corda che collega i due estremi efficienti.

La retta tra [math](0,160)[/math] e [math](1600/7,800/7)[/math], nello spazio [math](E,C)[/math], è:

[math]C_{\text{corda}}(E)=160-0{,}2E[/math]

Poiché la curva del costo si trova al di sotto della corda nell’intervallo considerato, definiamo la distanza verticale positiva come:

[math]d(E)=C_{\text{corda}}(E)-C(E)[/math]

Sostituendo la funzione di costo otteniamo:

[math]d(E)=0{,}2E-0{,}000875E^2[/math]

La derivata è:

[math]d'(E)=0{,}2-0{,}00175E[/math]

Ponendo la derivata uguale a zero:

[math]\displaystyle E^*=\frac{0{,}2}{0{,}00175}=\frac{800}{7}\approx114{,}29[/math]

La seconda derivata è negativa, quindi la distanza raggiunge un massimo in questo punto. La quota rinnovabile corrispondente è:

[math]\displaystyle x^*=1-\frac{E^*}{400}=\frac57\approx71{,}43\%[/math]

Il costo risulta:

[math]\displaystyle C(x^*)=\frac{880}{7}\approx125{,}71\ \text{€/MWh}[/math]

Il criterio geometrico individua quindi una quota rinnovabile di circa il 71,4%, con emissioni di circa 114,3 kgCO₂/MWh e un costo di circa 125,71 €/MWh.

Il criterio adottato è la massima distanza verticale dalla corda, mentre in alcuni algoritmi il Knee Point viene individuato attraverso la distanza perpendicolare dalla retta che unisce gli estremi. I due criteri non coincidono in generale, ma in questo specifico esempio conducono allo stesso punto, perché la distanza perpendicolare è proporzionale alla distanza verticale rispetto alla stessa retta.

Il risultato geometrico non dimostra che il 71,4% sia necessariamente la scelta migliore per l’azienda: il Knee è un candidato al compromesso, da valutare insieme ai prezzi dell’energia, ai vincoli di rete, agli obiettivi ambientali e ai rischi di approvvigionamento. Una direzione che debba rispettare un limite massimo alle emissioni o un budget rigido potrebbe preferire un altro punto della frontiera.

Verifica con Python

Il programma seguente costruisce la frontiera efficiente, calcola il Knee Point attraverso una griglia sufficientemente fitta e restituisce il risultato dell’ottimizzazione con una carbon tax. La griglia fornisce un’approssimazione numerica del punto geometrico, mentre le formule analitiche precedenti ne forniscono il valore esatto.

import numpy as np

def cost(x):
    return 80 + 60 * (1 - x)**2 + 80 * x**2

def emissions(x):
    return 400 * (1 - x)

# Intervallo Pareto-efficiente
x_min = 3 / 7
x = np.linspace(x_min, 1, 10001)

E = emissions(x)
C = cost(x)

# Estremi della frontiera nello spazio (E, C)
p0 = np.array([E[-1], C[-1]])
p1 = np.array([E[0], C[0]])

v = p1 - p0
points = np.column_stack([E, C])

# Distanza perpendicolare dalla retta della corda
cross = (
    v[0] * (points[:, 1] - p0[1])
    - v[1] * (points[:, 0] - p0[0])
)
distance = np.abs(cross) / np.linalg.norm(v)

i = np.argmax(distance)

print("Knee Point numerico")
print(f"Quota rinnovabile: {100*x[i]:.2f}%")
print(f"Emissioni: {E[i]:.2f} kgCO2/MWh")
print(f"Costo: {C[i]:.2f} €/MWh")

def optimize_with_carbon_tax(p):
    """
    p è espresso in €/kgCO2.
    Restituisce la soluzione analitica vincolata a [0, 1].
    """
    x_star = np.clip((120 + 400*p) / 280, 0, 1)

    return {
        "x": x_star,
        "costo": cost(x_star),
        "emissioni": emissions(x_star),
        "obiettivo": cost(x_star) + p * emissions(x_star)
    }

for p in [0, 0.05, 0.20, 0.40, 0.50]:
    result = optimize_with_carbon_tax(p)

    print(
        f"\nCarbon tax = {p:.2f} €/kgCO2"
        f"\nQuota rinnovabile = {100*result['x']:.2f}%"
        f"\nCosto = {result['costo']:.2f} €/MWh"
        f"\nEmissioni = {result['emissioni']:.2f} kgCO2/MWh"
    )

L’output del Knee Point è:

Knee Point numerico
Quota rinnovabile: 71.43%
Emissioni: 114.29 kgCO2/MWh
Costo: 125.71 €/MWh

Il risultato numerico coincide, entro la precisione di stampa, con quello ricavato analiticamente. La griglia contiene 10.001 punti e permette di approssimare il massimo della distanza con una precisione adeguata all’esempio, ma il valore esatto si ottiene direttamente dalla derivazione della funzione.

Forse potrebbe interessarti anche:  Catene di Markov in Python: Modellare Funnel, Retention e Churn oltre le classiche dashboard

Per la carbon tax, il programma restituisce i seguenti risultati:

Carbon tax = 0.00 €/kgCO2
Quota rinnovabile = 42.86%
Costo = 114.29 €/MWh
Emissioni = 228.57 kgCO2/MWh

Carbon tax = 0.05 €/kgCO2
Quota rinnovabile = 50.00%
Costo = 115.00 €/MWh
Emissioni = 200.00 kgCO2/MWh

Carbon tax = 0.20 €/kgCO2
Quota rinnovabile = 71.43%
Costo = 125.71 €/MWh
Emissioni = 114.29 kgCO2/MWh

Carbon tax = 0.40 €/kgCO2
Quota rinnovabile = 100.00%
Costo = 160.00 €/MWh
Emissioni = 0.00 kgCO2/MWh

Carbon tax = 0.50 €/kgCO2
Quota rinnovabile = 100.00%
Costo = 160.00 €/MWh
Emissioni = 0.00 kgCO2/MWh

L’output evidenzia che la quota ottimale di rinnovabili cresce con il valore economico attribuito alle emissioni: senza carbon tax, il modello sceglie il minimo di costo; con una tassa di 0,20 €/kgCO₂, la soluzione coincide con il Knee Point; da 0,40 €/kgCO₂ in poi, la soluzione ottimale raggiunge il limite superiore del 100%.

Il codice utilizza una formula analitica e applica il vincolo sull’intervallo [math][0,1][/math], evitando di affidare la soluzione a un algoritmo numerico che potrebbe restituire valori leggermente interni agli estremi quando il vero ottimo si trova sul bordo dell’intervallo.

Come interpretare la carbon tax

Introduciamo una tassa [math]p[/math], espressa in euro per chilogrammo di CO₂, e minimizziamo il costo complessivo:

[math]C_p(x)=C(x)+400p(1-x)[/math]

Derivando rispetto a [math]x[/math], otteniamo:

[math]C_p'(x)=-120+280x-400p[/math]

L’ottimo interno soddisfa quindi:

[math]\displaystyle x^*=\frac{120+400p}{280}[/math]

con il risultato limitato all’intervallo [math][0,1][/math]. Quando [math]p=0[/math], l’azienda sceglie la quota economicamente più conveniente, pari a [math]3/7[/math]. All’aumentare della tassa, la quota ottimale di rinnovabili cresce fino a raggiungere il 100% quando [math]p\geq0{,}40[/math] €/kgCO₂.

La carbon tax introduce quindi una regola economica esplicita che modifica la convenienza relativa delle alternative, mentre il Knee Point è un criterio geometrico che individua una particolare posizione sulla frontiera. I due risultati possono coincidere, come accade con [math]p=0{,}20[/math] in questo modello, ma non esiste una ragione generale per cui debbano farlo.

In un’applicazione reale, il parametro [math]p[/math] dovrebbe essere giustificato attraverso uno scenario economico o regolatorio coerente con il problema. Il modello non include contratti pluriennali, profili orari di consumo, costi di rete, accumulo o differenze tra le fonti rinnovabili, perciò il valore numerico ottenuto serve a illustrare il meccanismo di scelta e non costituisce una raccomandazione energetica per un data center specifico.

Dalle soluzioni matematiche alle decisioni aziendali

I tre casi mostrano perché la scelta del metodo di ottimizzazione conta quanto il calcolo della soluzione. Nel problema metalmeccanico, la regione ammissibile è convessa e la funzione pesata permette di individuare i compromessi efficienti, purché i pesi siano interpretati correttamente. Nel portafoglio deep-tech, invece, le decisioni indivisibili producono un insieme discreto e non convesso, nel quale alcune soluzioni Pareto-efficienti non possono emergere da una semplice somma pesata. Nel caso energetico, infine, il Knee Point acquista significato soltanto dopo aver costruito un modello che presenti un autentico conflitto tra costo ed emissioni.

Queste differenze hanno conseguenze pratiche. Se un piano produttivo è dominato, non c’è motivo di conservarlo tra le alternative finali; se un portafoglio efficiente non è supportato, cambiare i pesi potrebbe non bastare a recuperarlo; se un Knee Point deriva da funzioni ipotetiche, non è possibile trasformarlo automaticamente in una raccomandazione operativa.

Prima di utilizzare una frontiera di Pareto in un processo decisionale, conviene verificare che gli obiettivi siano definiti con unità di misura coerenti, che i vincoli rappresentino condizioni operative reali e che il metodo utilizzato sia adatto alla struttura continua o discreta del problema. Occorre poi analizzare quanto la soluzione dipenda dai parametri, quali alternative vengano escluse e quali informazioni potrebbero cambiare la scelta.

Nel caso produttivo, servono margini unitari aggiornati, emissioni misurate, capacità degli impianti, disponibilità di materie prime, costi di avviamento e vincoli di domanda, perché questi dati permettono di capire se la frontiera teorica rappresenti davvero le possibilità dell’azienda.

Nel portafoglio deep-tech, servono stime documentate dell’NPV, probabilità di successo, tempi di realizzazione, fabbisogni finanziari, dipendenze tra progetti e criteri trasparenti per misurare l’impatto. Il comitato deve inoltre chiarire se l’impatto sia un obiettivo da massimizzare, una soglia minima da rispettare oppure un criterio subordinato alla redditività, perché questa scelta può cambiare la formulazione matematica.

Per il data center, occorrono curve di costo basate su tariffe e contratti reali, profili orari di consumo, fattori di emissione coerenti con il metodo di rendicontazione, disponibilità effettiva delle fonti rinnovabili, costi di accumulo e requisiti di continuità del servizio. Solo dopo aver introdotto questi elementi si può valutare se il Knee rimanga interessante anche quando il modello si avvicina alle condizioni operative.

In tutti e tre i casi, l’analisi di sensibilità è fondamentale: se aumentano i costi produttivi, se un progetto rende meno del previsto o se cambia il prezzo dell’energia, la soluzione che appariva migliore potrebbe perdere attrattiva. Una decisione robusta deve quindi essere valutata anche rispetto a scenari alternativi, soprattutto quando i dati di partenza sono incerti.

La frontiera di Pareto non sostituisce il giudizio manageriale, ma rende più trasparente il rapporto tra obiettivi concorrenti. La sua utilità consiste nel mostrare quali alternative meritino di essere discusse, quali siano già inferiori ad altre e quali preferenze debbano essere esplicitate prima di scegliere.

Il punto decisivo non è trovare un numero da presentare in una riunione, ma capire che cosa quel numero rappresenti, quali ipotesi lo sostengano e quali condizioni potrebbero renderlo inadatto. Un modello di ottimizzazione diventa uno strumento decisionale credibile quando permette di distinguere ciò che deriva dai dati, ciò che dipende dalle ipotesi e ciò che richiede una scelta strategica.

Domande per verificare la comprensione

Perché il piano produttivo (0,10) non appartiene alla frontiera di Pareto?

Il piano [math](0,10)[/math] genera un profitto di 40.000 euro e 30 tonnellate di emissioni, mentre il piano [math](8,0)[/math] garantisce lo stesso profitto con 16 tonnellate di emissioni. Poiché il secondo piano mantiene invariato il risultato economico e riduce le emissioni, domina il primo, che deve quindi essere escluso dalla frontiera efficiente.

Perché il portafoglio (9,9) è efficiente, ma non può essere selezionato da alcuna somma pesata?

Il portafoglio [math](9,9)[/math] non è dominato da nessuna combinazione ammissibile, ma le condizioni necessarie perché sia almeno altrettanto conveniente dei due estremi della frontiera sono incompatibili: dovrebbe valere contemporaneamente [math]\lambda\geq5/7[/math] e [math]\lambda\leq2/7[/math]. Il metodo ε-constraint consente invece di recuperarlo imponendo [math]\text{NPV}\geq9[/math] e massimizzando l’impatto.

Perché il Knee Point del modello energetico non è automaticamente la scelta migliore?

Il Knee Point al 71,43% di energia rinnovabile deriva da un criterio geometrico applicato a funzioni di costo ed emissioni ipotetiche. Il risultato identifica un possibile compromesso, ma la scelta aziendale deve considerare anche tariffe reali, disponibilità energetica, vincoli di continuità, obiettivi ambientali e condizioni operative che il modello non rappresenta.

Quali informazioni servono per utilizzare questi modelli in azienda?

Servono dati attendibili sugli obiettivi e sui vincoli, una verifica delle ipotesi matematiche, criteri decisionali espliciti e un’analisi di sensibilità sui parametri che possono cambiare la scelta. La qualità della decisione dipende infatti non soltanto dal metodo di ottimizzazione, ma anche dalla corrispondenza tra il modello e il problema reale che si vuole governare.

📚 Frontiera di Pareto, ottimizzazione multi-obiettivo e decisioni con Python

La frontiera di Pareto permette di distinguere le alternative efficienti da quelle dominate, ma individuare i compromessi non basta sempre per scegliere una soluzione. Questi articoli approfondiscono i metodi matematici, gli strumenti Python e le applicazioni pratiche che aiutano a valutare le alternative e a motivare una decisione.

 

Pubblicità