DecInfer-06-Value-Information : Valeur de l’Information

Serie : Programmation Probabiliste avec Infer.NET (6/10)
Duree estimee : 45 minutes
Prerequis : DecInfer-01 et 03 (Utilite), DecInfer-04 (Attributs), DecInfer-05 (Reseaux de decision)


Objectifs

  • Calculer la valeur de l’information parfaite (EVPI)
  • Calculer la valeur de l’information d’echantillon (EVSI)
  • Appliquer a des problemes classiques : droits de forage, chasse au tresor
  • Comprendre quand l’information a de la valeur

1. Information et Reduction d’Incertitude

Intuition

L’information a de la valeur quand elle change notre decision.

Si vous allez acheter un parapluie de toute facon, connaitre la meteo n’a pas de valeur. Mais si vous hesitez, cette information peut etre precieuse.

Question fondamentale

Combien devrait-on payer pour obtenir une information avant de decider ?

Types d’information

Type Description Exemple
Parfaite Connaitre l’etat du monde avec certitude Oracle parfait
Imparfaite Signal bruite, test avec erreurs Test medical
Nulle Pas d’information Statu quo
// Installation Infer.NET
#r "nuget: Microsoft.ML.Probabilistic"
#r "nuget: Microsoft.ML.Probabilistic.Compiler"

using Microsoft.ML.Probabilistic;
using Microsoft.ML.Probabilistic.Distributions;
using Microsoft.ML.Probabilistic.Models;
using Microsoft.ML.Probabilistic.Algorithms;

Console.WriteLine("Infer.NET charge !");
Installed Packages
  • Microsoft.ML.Probabilistic, 0.4.2504.701
  • Microsoft.ML.Probabilistic.Compiler, 0.4.2504.701
Infer.NET charge !

Chargement du helper de visualisation des graphes de facteurs.

// Chargement du helper pour visualiser les factor graphs Infer.NET
#load "../../Infer/FactorGraphHelper.cs"

// Active la generation des factor graphs pour les modeles Infer.NET
bool ShowFactorGraph = true;

Console.WriteLine("FactorGraphHelper charge - ShowFactorGraph = " + ShowFactorGraph);
FactorGraphHelper charge - ShowFactorGraph = True

Environnement charge

Infer.NET est maintenant disponible pour : - La modelisation bayesienne des etats incertains - Le calcul automatique des posterieurs après observation de signaux - L’integration avec les calculs de valeur de l’information

Rappel : Dans ce notebook, nous combinerons des calculs explicites de VoI (pour la pedagogie) avec des demonstrations Infer.NET (pour montrer l’automatisation).

2. Valeur de l’Information Parfaite (EVPI)

Definition

\[\text{EVPI} = E[\max_a U(a|\omega)] - \max_a E[U(a)]\]

Ou : - Le premier terme est l’utilite esperee avec information parfaite (on connait l’etat ω avant de decider) - Le second terme est l’utilite esperee sans information (on decide sur la base du prior)

Interpretation intuitive

EVPI = Combien paierait un agent rationnel pour un oracle parfait ?

L’EVPI mesure le cout de l’incertitude. C’est la perte moyenne due au fait de ne pas connaitre l’etat du monde au moment de decider.

Différence conceptuelle : EVPI vs EVSI

Aspect EVPI EVSI
Information Parfaite (oracle) Imparfaite (test)
Connaissance Etat ω certain Signal bruite de ω
Borne Maximum théorique ≤ EVPI
Pratique Irrealiste Applicable

L’EVPI est une borne superieure : aucune information imparfaite ne peut valoir plus qu’une information parfaite. Le ratio EVSI/EVPI mesure l’efficacite d’un test.

Calcul pratique

  1. Sans information : calculer max_a E[U(a)]
    • Pour chaque action a, calculer E[U(a)] = Σ_ω P(ω) × U(a,ω)
    • Choisir l’action avec E[U] maximal
  2. Avec information parfaite : calculer E[max_a U(a|ω)]
    • Pour chaque etat ω, trouver la meilleure action : max_a U(a,ω)
    • Faire la moyenne ponderee : Σ_ω P(ω) × max_a U(a,ω)
// Calcul de l'EVPI : Exemple simple

// Scenario : Parapluie
// Etats : Pluie (P), Soleil (S)
// Actions : Prendre parapluie (U), Ne pas prendre (N)

double pPluie = 0.3;

// Matrice d'utilite U(action, etat)
var utilites = new Dictionary<(string action, string etat), double>
{
    { ("U", "P"), 70 },  // Parapluie + Pluie : protege mais encombrant
    { ("U", "S"), 60 },  // Parapluie + Soleil : encombrement inutile
    { ("N", "P"), 20 },  // Pas parapluie + Pluie : trempe !
    { ("N", "S"), 100 }, // Pas parapluie + Soleil : parfait
};

// Sans information : calculer E[U(a)] pour chaque action
double EU_U = pPluie * utilites[("U", "P")] + (1 - pPluie) * utilites[("U", "S")];
double EU_N = pPluie * utilites[("N", "P")] + (1 - pPluie) * utilites[("N", "S")];

double maxEU_sansInfo = Math.Max(EU_U, EU_N);
string bestAction_sansInfo = EU_U > EU_N ? "Prendre parapluie" : "Ne pas prendre";

Console.WriteLine("=== Sans Information ===");
Console.WriteLine($"P(pluie) = {pPluie:P0}\n");
Console.WriteLine($"E[U(prendre parapluie)] = {EU_U:F1}");
Console.WriteLine($"E[U(ne pas prendre)] = {EU_N:F1}");
Console.WriteLine($"=> Decision : {bestAction_sansInfo}");
Console.WriteLine($"=> E[U*] = {maxEU_sansInfo:F1}\n");

// Avec information parfaite
// Si pluie certaine : max(U(U,P), U(N,P)) = max(70, 20) = 70
// Si soleil certain : max(U(U,S), U(N,S)) = max(60, 100) = 100
double maxU_pluie = Math.Max(utilites[("U", "P")], utilites[("N", "P")]);
double maxU_soleil = Math.Max(utilites[("U", "S")], utilites[("N", "S")]);

double EU_avecInfo = pPluie * maxU_pluie + (1 - pPluie) * maxU_soleil;

Console.WriteLine("=== Avec Information Parfaite ===");
Console.WriteLine($"Si Pluie : action optimale donne U = {maxU_pluie}");
Console.WriteLine($"Si Soleil : action optimale donne U = {maxU_soleil}");
Console.WriteLine($"=> E[U* | info parfaite] = {EU_avecInfo:F1}\n");

double EVPI = EU_avecInfo - maxEU_sansInfo;
Console.WriteLine($"=== EVPI = {EVPI:F1} ===");
Console.WriteLine($"L'agent paierait jusqu'a {EVPI:F1} points pour connaitre la meteo a l'avance.");
=== Sans Information ===
P(pluie) = 30 %

E[U(prendre parapluie)] = 63,0
E[U(ne pas prendre)] = 76,0
=> Decision : Ne pas prendre
=> E[U*] = 76,0

=== Avec Information Parfaite ===
Si Pluie : action optimale donne U = 70
Si Soleil : action optimale donne U = 100
=> E[U* | info parfaite] = 91,0

=== EVPI = 15,0 ===
L'agent paierait jusqu'a 15,0 points pour connaitre la meteo a l'avance.

Interpretation des résultats — EVPI du scénario parapluie

Analyse du scénario parapluie :

  • Sans information : La decision optimale est “Ne pas prendre le parapluie” car E[U] = 76 > 63. L’agent parie sur le beau temps (70% de chance).

  • Avec information parfaite : Si on savait avec certitude la meteo, on prendrait toujours la bonne decision :

    • Pluie (30%) → Parapluie → U = 70
    • Soleil (70%) → Pas de parapluie → U = 100
    • E[U|info parfaite] = 0.3×70 + 0.7×100 = 91
  • EVPI = 15 : C’est le cout de l’incertitude. L’agent rationnel paierait jusqu’a 15 points d’utilite pour un oracle meteorologique parfait.

Point cle : L’EVPI n’est pas nul car la decision optimale change selon l’etat du monde. Si on prenait toujours le parapluie quel que soit le temps, l’information n’aurait aucune valeur.

Exercice 1 : Investissement Immobilier - Calcul de l’EVPI

Contexte : Vous envisagez d’acheter un appartement pour le louer. L’etat du marche locatif est incertain : il peut etre porteur (bullish) ou defavorable (bearish).

Données :

Paramètre Valeur
P(marche porteur) 60%
Achat + marche porteur U = 150 (loyers eleves)
Achat + marche defavorable U = 40 (vacance, loyers bas)
Obligations (sans risque) U = 80

Objectif : Calculer l’EVPI et determiner si un oracle parfait valant 25 points d’utilite merite d’etre achete.

Étapes : 1. Calculer E[U(achat)] et E[U(obligations)] sans information 2. Identifier la decision optimale sans information 3. Calculer E[U] avec information parfaite (meilleure action par etat) 4. Calculer l’EVPI 5. Conclure : un oracle coutant 25 points, faut-il l’acheter ?

Indices : - Indice 1 : Sans information, E[U(a)] = P(porteur) * U(a, porteur) + P(defavorable) * U(a, defavorable) - Indice 2 : Avec information parfaite, pour chaque etat, on choisit la meilleure action - Indice 3 : EVPI = E[max U|omega] - max E[U]

// Exercice : Investissement Immobilier - Calcul de l'EVPI

double pPorteur = 0.60;
double U_achat_porteur = 150;   // Achat + marche porteur
double U_achat_defavorable = 40; // Achat + marche defavorable
double U_obligations = 80;       // Obligations (sans risque)

Console.WriteLine("=== Exercice : Investissement Immobilier ===\n");
Console.WriteLine($"P(marche porteur) = {pPorteur:P0}");

// TODO 1 : Calculer E[U(achat)] et E[U(obligations)]
// Indice : E[U(action)] = P(porteur) * U(action, porteur) + P(defavorable) * U(action, defavorable)
double EU_achat = 0;   // TODO etudiant
double EU_oblig = 0;   // TODO etudiant

// TODO 2 : Determiner la decision optimale sans information
// Indice : Comparer EU_achat et EU_oblig
string meilleureAction = "";  // TODO etudiant
double maxEU_sansInfo = 0;    // TODO etudiant

// TODO 3 : Calculer E[U] avec information parfaite
// Etape a : Pour chaque etat, choisir la meilleure action
// Indice : maxU_porteur = max(U_achat_porteur, U_obligations)
double maxU_porteur = 0;      // TODO etudiant
double maxU_defavorable = 0;  // TODO etudiant : max(U_achat_defavorable, U_obligations)
// Etape b : Esperance ponderee
double EU_infoParfaite = 0;   // TODO etudiant : P(porteur) * maxU_porteur + P(defav) * maxU_defavorable

// TODO 4 : Calculer l'EVPI
// Indice : EVPI = EU_infoParfaite - maxEU_sansInfo
double EVPI_immo = 0;  // TODO etudiant

// TODO 5 : L'oracle vaut-il 25 points ?
double coutOracle = 25;
// Indice : comparer EVPI_immo avec coutOracle et afficher la conclusion

Console.WriteLine("Exercice a completer");
=== Exercice : Investissement Immobilier ===

P(marche porteur) = 60 %
Exercice a completer

3. Exemple 1 : Droits de Forage Petrolier

Scénario

Une compagnie petroliere possede des droits de forage sur un terrain.

  • Incertitude : Presence de petrole (oui/non)
  • Decision : Forer ou vendre les droits
  • Information possible : Test sismique (imparfait)

Données

Paramètre Valeur
P(petrole) 30%
Cout forage 500k
Revenu si petrole 2M
Prix vente droits 200k
Cout test sismique 50k
Sensibilite test 90%
Specificite test 80%
// Droits de forage petrolier

double pPetrole = 0.30;
double coutForage = 500_000;
double revenuPetrole = 2_000_000;
double prixVente = 200_000;
double coutTest = 50_000;
double sensibiliteTest = 0.90; // P(test+|petrole)
double specificiteTest = 0.80; // P(test-|pas petrole)

// Profits nets
double profit_forer_petrole = revenuPetrole - coutForage;  // 1.5M
double profit_forer_pasPetrole = -coutForage;              // -500k
double profit_vendre = prixVente;                          // 200k

Console.WriteLine("=== Droits de Forage Petrolier ===\n");

// 1. Sans information
double EU_forer = pPetrole * profit_forer_petrole + (1 - pPetrole) * profit_forer_pasPetrole;
double EU_vendre = profit_vendre;

double bestEU_sansInfo = Math.Max(EU_forer, EU_vendre);

Console.WriteLine("1. Sans information :");
Console.WriteLine($"   E[Profit(forer)] = {EU_forer:N0} EUR");
Console.WriteLine($"   E[Profit(vendre)] = {EU_vendre:N0} EUR");
Console.WriteLine($"   => Decision : {(EU_forer > EU_vendre ? "FORER" : "VENDRE")}\n");

// 2. EVPI
double maxProfit_petrole = Math.Max(profit_forer_petrole, profit_vendre);
double maxProfit_pasPetrole = Math.Max(profit_forer_pasPetrole, profit_vendre);
double EU_infoParf = pPetrole * maxProfit_petrole + (1 - pPetrole) * maxProfit_pasPetrole;

double EVPI = EU_infoParf - bestEU_sansInfo;

Console.WriteLine("2. Avec information parfaite :");
Console.WriteLine($"   Si petrole (30%) : forer => {maxProfit_petrole:N0}");
Console.WriteLine($"   Si pas petrole (70%) : vendre => {maxProfit_pasPetrole:N0}");
Console.WriteLine($"   E[Profit | info parfaite] = {EU_infoParf:N0} EUR");
Console.WriteLine($"   EVPI = {EVPI:N0} EUR\n");
=== Droits de Forage Petrolier ===

1. Sans information :
   E[Profit(forer)] = 100 000 EUR
   E[Profit(vendre)] = 200 000 EUR
   => Decision : VENDRE

2. Avec information parfaite :
   Si petrole (30%) : forer => 1 500 000
   Si pas petrole (70%) : vendre => 200 000
   E[Profit | info parfaite] = 590 000 EUR
   EVPI = 390 000 EUR

Analyse des résultats : forage sans information

Observation cle : Sans information, la decision optimale est de vendre les droits. Malgre un profit potentiel enorme si petrole (1.5M), le risque de perte (70% de chance de perdre 500k) rend le forage trop risque.

EVPI = 390,000 EUR represente une somme considerable ! Un oracle parfait permettrait de forer uniquement quand il y a du petrole et de vendre sinon. Cette valeur elevee reflète la forte asymetrie des consequences entre gains et pertes potentielles.

// 3. EVSI : Valeur de l'information du test sismique

// P(test+) = P(test+|petrole)P(petrole) + P(test+|pas petrole)P(pas petrole)
double pTestPos = sensibiliteTest * pPetrole + (1 - specificiteTest) * (1 - pPetrole);
double pTestNeg = 1 - pTestPos;

// P(petrole|test+) par Bayes
double pPetrole_testPos = (sensibiliteTest * pPetrole) / pTestPos;
// P(petrole|test-)
double pPetrole_testNeg = ((1 - sensibiliteTest) * pPetrole) / pTestNeg;

Console.WriteLine("3. Valeur du test sismique (EVSI) :");
Console.WriteLine($"   P(test+) = {pTestPos:P1}");
Console.WriteLine($"   P(petrole|test+) = {pPetrole_testPos:P1}");
Console.WriteLine($"   P(petrole|test-) = {pPetrole_testNeg:P1}\n");

// Decision si test positif
double EU_forer_testPos = pPetrole_testPos * profit_forer_petrole + (1 - pPetrole_testPos) * profit_forer_pasPetrole;
double bestEU_testPos = Math.Max(EU_forer_testPos, profit_vendre);
string decision_testPos = EU_forer_testPos > profit_vendre ? "FORER" : "VENDRE";

// Decision si test negatif
double EU_forer_testNeg = pPetrole_testNeg * profit_forer_petrole + (1 - pPetrole_testNeg) * profit_forer_pasPetrole;
double bestEU_testNeg = Math.Max(EU_forer_testNeg, profit_vendre);
string decision_testNeg = EU_forer_testNeg > profit_vendre ? "FORER" : "VENDRE";

Console.WriteLine($"   Si test+ : E[forer]={EU_forer_testPos:N0}, vendre={profit_vendre:N0} => {decision_testPos}");
Console.WriteLine($"   Si test- : E[forer]={EU_forer_testNeg:N0}, vendre={profit_vendre:N0} => {decision_testNeg}\n");

// E[Profit avec test] = P(test+) * best(test+) + P(test-) * best(test-) - cout_test
double EU_avecTest = pTestPos * bestEU_testPos + pTestNeg * bestEU_testNeg - coutTest;

double EVSI = (pTestPos * bestEU_testPos + pTestNeg * bestEU_testNeg) - bestEU_sansInfo;

Console.WriteLine($"   E[Profit avec test] = {EU_avecTest:N0} EUR");
Console.WriteLine($"   EVSI (avant cout) = {EVSI:N0} EUR");
Console.WriteLine($"   Cout test = {coutTest:N0} EUR");
Console.WriteLine($"   EVSI net = {EVSI - coutTest:N0} EUR\n");

// Decision finale
Console.WriteLine("=== Recommandation ===");
if (EU_avecTest > bestEU_sansInfo)
    Console.WriteLine($"Faire le test sismique (gain net = {EU_avecTest - bestEU_sansInfo:N0} EUR)");
else
    Console.WriteLine($"Ne pas faire le test, {(EU_forer > EU_vendre ? "forer directement" : "vendre directement")}");

Console.WriteLine($"\nEfficiency du test : EVSI/EVPI = {EVSI/EVPI:P0}");
3. Valeur du test sismique (EVSI) :
   P(test+) = 41,0 %
   P(petrole|test+) = 65,9 %
   P(petrole|test-) = 5,1 %

   Si test+ : E[forer]=817 073, vendre=200 000 => FORER
   Si test- : E[forer]=-398 305, vendre=200 000 => VENDRE

   E[Profit avec test] = 403 000 EUR
   EVSI (avant cout) = 253 000 EUR
   Cout test = 50 000 EUR
   EVSI net = 203 000 EUR

=== Recommandation ===
Faire le test sismique (gain net = 203 000 EUR)

Efficiency du test : EVSI/EVPI = 65 %

Interpretation des résultats du test sismique

Analyse des posterieurs : - Test positif (41% des cas) : P(petrole|test+) = 66%, ce qui rend le forage rentable (EU = 817k > 200k) - Test negatif (59% des cas) : P(petrole|test-) = 5%, ce qui rend la vente preferable

Valeur du test : - EVSI = 253k EUR : La valeur brute de l’information fournie par le test - EVSI net = 203k EUR : Après deduction du cout (50k), le test reste très rentable - Efficacite = 65% : Le test capture les 2/3 de la valeur d’un oracle parfait

Decision finale : Faire le test sismique avant de decider. Le gain net (203k) justifie largement le cout.

Point cle : Le test change la decision selon son résultat (forer si +, vendre si -), c’est pourquoi il a de la valeur. Un test qui donnerait toujours la même decision n’aurait aucune valeur.

4. Exemple 2 : Chasse au Tresor

Scénario

Un tresor est cache dans l’une des 5 cases d’une grille. Chaque fouille coute 10 EUR. Le tresor vaut 100 EUR.

Un detecteur de metal peut indiquer (avec bruit) si le tresor est proche.

Stratégie de fouille séquentielle

Sans information, toutes les cases ont la même probabilite (20%). Calculons le profit espere selon la position du tresor, en fouillant les cases dans l’ordre.

// Chasse au tresor avec Infer.NET

int nCases = 5;
double coutFouille = 10;
double valeurTresor = 100;

// Prior uniforme sur la position du tresor
double pTresorParCase = 1.0 / nCases;

Console.WriteLine("=== Chasse au Tresor ===\n");
Console.WriteLine($"Cases : {nCases}");
Console.WriteLine($"Cout par fouille : {coutFouille} EUR");
Console.WriteLine($"Valeur tresor : {valeurTresor} EUR");
Console.WriteLine($"Prior : P(tresor en case i) = {pTresorParCase:P0}\n");

// Strategie sans information : fouiller toutes les cases dans l'ordre
// E[cout] = somme sur k de P(tresor en case k) * (k * coutFouille)
// Avec prior uniforme et fouille sequentielle :
double EU_sansInfo = 0;
for (int k = 1; k <= nCases; k++)
{
    // Si tresor en case k, on fouille k cases
    EU_sansInfo += pTresorParCase * (valeurTresor - k * coutFouille);
}

Console.WriteLine("1. Sans information (fouille sequentielle) :");
Console.WriteLine($"   E[Profit] = {EU_sansInfo:F1} EUR\n");

// Avec information parfaite : on fouille directement la bonne case
double EU_infoParf = valeurTresor - coutFouille;
double EVPI_tresor = EU_infoParf - EU_sansInfo;

Console.WriteLine("2. Avec information parfaite :");
Console.WriteLine($"   E[Profit] = {EU_infoParf:F1} EUR");
Console.WriteLine($"   EVPI = {EVPI_tresor:F1} EUR");
=== Chasse au Tresor ===

Cases : 5
Cout par fouille : 10 EUR
Valeur tresor : 100 EUR
Prior : P(tresor en case i) = 20 %

1. Sans information (fouille sequentielle) :
   E[Profit] = 70,0 EUR

2. Avec information parfaite :
   E[Profit] = 90,0 EUR
   EVPI = 20,0 EUR

Interpretation des résultats — vérification du scénario chasse au trésor

Verification des calculs :

Cas (tresor en case k) Probabilite Profit net Contribution
k = 1 20% 100 - 10 = 90 18 EUR
k = 2 20% 100 - 20 = 80 16 EUR
k = 3 20% 100 - 30 = 70 14 EUR
k = 4 20% 100 - 40 = 60 12 EUR
k = 5 20% 100 - 50 = 50 10 EUR
Total 70 EUR

Analyse : - En moyenne, on fouille 3 cases avant de trouver le tresor - EVPI = 20 EUR correspond au cout moyen des fouilles “inutiles” evitees - L’EVPI est relativement faible (20 EUR sur 100 EUR) car même sans information, on trouve toujours le tresor. L’information ne fait que reduire le nombre de tentatives.

Detecteur de metal : information imparfaite

Le detecteur emet un signal “proche” avec une probabilite qui decroit exponentiellement avec la distance au centre (case 3). Voyons comment ce signal imparfait ameliore notre stratégie de fouille.

// Avec detecteur de metal (information imparfaite)

// Le detecteur indique "proche" ou "loin"
// P(proche|tresor dans case i) depend de la distance au centre
// Supposons detecteur pointe vers case 3 (centre)

int caseDetecteur = 3; // Case centrale
double pProcheGivenDistance(int distance) => Math.Exp(-distance); // Decroit avec distance

// Probabilites conditionnelles
Console.WriteLine("\n3. Detecteur de metal (centre en case 3) :\n");

double[] pProche = new double[nCases];
for (int i = 0; i < nCases; i++)
{
    int distance = Math.Abs(i + 1 - caseDetecteur);
    pProche[i] = pProcheGivenDistance(distance);
    Console.WriteLine($"   Case {i + 1}: distance={distance}, P(proche|tresor ici)={pProche[i]:F3}");
}

// P(detecteur dit proche)
double pSignalProche = pProche.Sum() / nCases;
Console.WriteLine($"\n   P(signal proche) = {pSignalProche:F3}");

// Posterieurs si signal proche (Bayes)
Console.WriteLine("\n   Si signal 'proche' :");
double[] posteriorProche = new double[nCases];
for (int i = 0; i < nCases; i++)
{
    posteriorProche[i] = (pProche[i] * pTresorParCase) / pSignalProche;
    Console.WriteLine($"     P(case {i + 1}|proche) = {posteriorProche[i]:F3}");
}

// Strategie optimale si proche : fouiller dans l'ordre des posterieurs
var ordreProche = Enumerable.Range(0, nCases)
    .OrderByDescending(i => posteriorProche[i])
    .ToArray();

Console.WriteLine($"\n   Ordre optimal si proche : {string.Join(" -> ", ordreProche.Select(i => $"Case {i+1}"))}");

// E[Profit | proche] avec strategie optimale
double EU_proche = 0;
double[] cumProb = new double[nCases];
for (int k = 0; k < nCases; k++)
{
    int caseIdx = ordreProche[k];
    EU_proche += posteriorProche[caseIdx] * (valeurTresor - (k + 1) * coutFouille);
}

Console.WriteLine($"   E[Profit | proche] = {EU_proche:F1} EUR");

// E[Profit | loin] (signal non-proche)
double[] posteriorLoin = new double[nCases];
for (int i = 0; i < nCases; i++)
{
    posteriorLoin[i] = ((1 - pProche[i]) * pTresorParCase) / (1 - pSignalProche);
}

var ordreLoin = Enumerable.Range(0, nCases)
    .OrderByDescending(i => posteriorLoin[i])
    .ToArray();

double EU_loin = 0;
for (int k = 0; k < nCases; k++)
{
    int caseIdx = ordreLoin[k];
    EU_loin += posteriorLoin[caseIdx] * (valeurTresor - (k + 1) * coutFouille);
}

Console.WriteLine($"   E[Profit | loin] = {EU_loin:F1} EUR");

// EVSI
double EU_avecDetecteur = pSignalProche * EU_proche + (1 - pSignalProche) * EU_loin;
double EVSI_detecteur = EU_avecDetecteur - EU_sansInfo;

Console.WriteLine($"\n   E[Profit avec detecteur] = {EU_avecDetecteur:F1} EUR");
Console.WriteLine($"   EVSI = {EVSI_detecteur:F1} EUR");
Console.WriteLine($"   Efficiency : {EVSI_detecteur / EVPI_tresor:P0} de l'info parfaite");

3. Detecteur de metal (centre en case 3) :

   Case 1: distance=2, P(proche|tresor ici)=0,135
   Case 2: distance=1, P(proche|tresor ici)=0,368
   Case 3: distance=0, P(proche|tresor ici)=1,000
   Case 4: distance=1, P(proche|tresor ici)=0,368
   Case 5: distance=2, P(proche|tresor ici)=0,135

   P(signal proche) = 0,401

   Si signal 'proche' :
     P(case 1|proche) = 0,067
     P(case 2|proche) = 0,183
     P(case 3|proche) = 0,498
     P(case 4|proche) = 0,183
     P(case 5|proche) = 0,067

   Ordre optimal si proche : Case 3 -> Case 2 -> Case 4 -> Case 1 -> Case 5
   E[Profit | proche] = 79,8 EUR
   E[Profit | loin] = 76,6 EUR

   E[Profit avec detecteur] = 77,8 EUR
   EVSI = 7,8 EUR
   Efficiency : 39 % de l'info parfaite

Interpretation du detecteur de metal

Modèle du signal :

Le detecteur utilise \(P(\text{proche} | \text{tresor en case } i) = e^{-|i - 3|}\), ce qui donne une probabilite de 100% si le tresor est au centre, et des probabilites decroissantes vers les extremites.

Mise a jour bayesienne :

Quand le detecteur dit “proche”, les posterieurs se concentrent sur les cases centrales (case 3 devient ~50% probable). La stratégie optimale est alors de fouiller en suivant les posterieurs decroissants.

Efficacite du detecteur : 39% de l’information parfaite. Cette efficacite moderee s’explique par le fait qu’un signal binaire ne peut pas localiser précisément parmi 5 cases.

Exercice 2 : Sélection d’une Source d’Information pour un Lancement Produit

Contexte : Une entreprise doit decider entre un lancement national (invest 500k) ou regional (invest 100k). Le marche peut etre favorable ou defavorable.

Données :

Paramètre Valeur
P(marche favorable) 55%
National + favorable 800k EUR
National + defavorable -200k EUR
Regional + favorable 200k EUR
Regional + defavorable 50k EUR

Sources d’information disponibles :

Source Sensibilite Specificite Cout
Étude de marche (A) 80% 70% 30k
Focus group (B) 60% 90% 20k

Objectif : Calculer l’EVPI, l’EVSI de chaque source, et choisir la meilleure stratégie.

Étapes : 1. Calculer la decision optimale sans information et l’EVPI 2. Calculer l’EVSI de la Source A (étude de marche) via Bayes 3. Calculer l’EVSI de la Source B (focus group) via Bayes 4. Comparer les EVSI nets et recommender la meilleure source

Indices : - Indice 1 : P(test+) = sensibilite * P(fav) + (1-specificite) * (1-P(fav)) - Indice 2 : P(fav|test+) = sensibilite * P(fav) / P(test+) - Indice 3 : EVSI_net = EVSI - cout ; choisir la source avec le meilleur EVSI net

// Exercice : Selection d'une Source d'Information pour un Lancement Produit

double pFavorable = 0.55;
double U_nat_fav = 800_000;    // National + favorable
double U_nat_defav = -200_000; // National + defavorable
double U_reg_fav = 200_000;    // Regional + favorable
double U_reg_defav = 50_000;   // Regional + defavorable

// Source A : Etude de marche
double sensA = 0.80, specA = 0.70, coutA = 30_000;
// Source B : Focus group
double sensB = 0.60, specB = 0.90, coutB = 20_000;

Console.WriteLine("=== Exercice : Selection de Source d'Information ===\n");

// TODO 1 : Decision optimale sans information et EVPI
// Etape a : Calculer E[U(national)] et E[U(regional)]
// Indice : E[U(action)] = P(fav) * U(action, fav) + P(defav) * U(action, defav)
double EU_nat = 0;  // TODO etudiant
double EU_reg = 0;  // TODO etudiant
double bestEU_sansInfo = 0;  // TODO etudiant : max des deux

// Etape b : Calculer l'EVPI
// Indice : Pour chaque etat, prendre max(national, regional), puis esperance ponderee
double EVPI_prod = 0;  // TODO etudiant

// TODO 2 : EVSI de la Source A (Etude de marche)
// Indice : P(testA+) = sensA * pFav + (1-specA) * (1-pFav)
// Puis posteriors par Bayes, puis bestEU pour chaque signal
double EVSI_A = 0;  // TODO etudiant

// TODO 3 : EVSI de la Source B (Focus group)
// Indice : Meme methode avec sensB et specB
double EVSI_B = 0;  // TODO etudiant

// TODO 4 : Comparer les sources et recommender
// Indice : EVSI_net = EVSI - cout ; choisir la source avec le meilleur EVSI net
// Etape : Afficher quelle source est recommandee et pourquoi
Console.WriteLine("Exercice a completer");
=== Exercice : Selection de Source d'Information ===

Exercice a completer

5. Quand l’Information a-t-elle de la Valeur ?

Conditions necessaires pour EVPI > 0

L’information parfaite a de la valeur si et seulement si :

La decision optimale change selon l’etat du monde.

Si la même action est optimale quel que soit l’etat, EVPI = 0. Formellement :

  • Si ∃ a* tel que ∀ω, a* = argmax_a U(a,ω), alors EVPI = 0
  • Sinon, EVPI > 0

Conditions pour EVSI > 0

L’information imparfaite (test, signal) a de la valeur si trois conditions sont reunies :

  1. EVPI > 0 : L’information parfaite aurait de la valeur
  2. Le signal est informatif : Il change les posterieurs (likelihood ≠ uniforme)
  3. Les posterieurs causent des changements de decision : Pour au moins un signal, la decision optimale differe de la decision a priori

Efficacite d’un test : ratio EVSI/EVPI

Le ratio EVSI/EVPI mesure la qualite relative d’un test :

Ratio Interpretation
0% Test inutile (ne change jamais la decision)
25% Test faible
50% Test modere
75% Bon test
100% Test parfait (equivalent a EVPI)

Ce ratio permet de comparer différents tests independamment de l’echelle des utilites.

Facteurs affectant la valeur de l’information

Facteur Effet sur VoI
Incertitude plus grande VoI augmente (plus a apprendre)
Qualite du test (sensibilite, specificite) VoI augmente
Cout de l’erreur (asymetrie des utilites) VoI augmente
Proximite du seuil de decision VoI augmente
Decision irreversible VoI augmente (pas de seconde chance)

Cas ou l’information n’a PAS de valeur

  1. Decision déjà claire : Une action domine clairement a priori
  2. Test non-informatif : Le signal est indépendant de l’etat
  3. Posterieurs insuffisants : Le signal ne change pas assez les croyances pour changer la decision

Verification empirique des conditions

Le code suivant illustre comment l’EVPI varie avec le niveau d’incertitude. Nous verrons que l’EVPI est maximal quand la decision est “sur le fil du rasoir”.

// Analyse de sensibilite : EVPI en fonction de l'incertitude

Console.WriteLine("=== EVPI en Fonction de P(petrole) ===\n");
Console.WriteLine("P(petrole) | EU_forer  | EU_vendre | EVPI");
Console.WriteLine("-----------|-----------|-----------|--------");

for (double p = 0.1; p <= 0.9; p += 0.1)
{
    double eu_f = p * profit_forer_petrole + (1 - p) * profit_forer_pasPetrole;
    double eu_v = profit_vendre;
    double best_sans = Math.Max(eu_f, eu_v);
    
    double max_p = Math.Max(profit_forer_petrole, profit_vendre);
    double max_np = Math.Max(profit_forer_pasPetrole, profit_vendre);
    double eu_parfait = p * max_p + (1 - p) * max_np;
    
    double evpi = eu_parfait - best_sans;
    
    Console.WriteLine($"{p,10:P0} | {eu_f,9:N0} | {eu_v,9:N0} | {evpi,6:N0}");
}

Console.WriteLine();
Console.WriteLine("=> L'EVPI est maximal quand l'incertitude rend la decision difficile.");
Console.WriteLine("   Elle est nulle quand une action domine clairement.");
=== EVPI en Fonction de P(petrole) ===

P(petrole) | EU_forer  | EU_vendre | EVPI
-----------|-----------|-----------|--------
      10 % |  -300 000 |   200 000 | 130 000
      20 % |  -100 000 |   200 000 | 260 000
      30 % |   100 000 |   200 000 | 390 000
      40 % |   300 000 |   200 000 | 420 000
      50 % |   500 000 |   200 000 | 350 000
      60 % |   700 000 |   200 000 | 280 000
      70 % |   900 000 |   200 000 | 210 000
      80 % | 1 100 000 |   200 000 | 140 000
      90 % | 1 300 000 |   200 000 | 70 000

=> L'EVPI est maximal quand l'incertitude rend la decision difficile.
   Elle est nulle quand une action domine clairement.

Interpretation de l’analyse de sensibilite

Profil de l’EVPI en fonction de P(petrole) :

Le tableau ci-dessus revele un phenomene fondamental en théorie de la decision :

Zone de P(petrole) Comportement Explication
0-25% EVPI croissant Decision claire (vendre), mais valeur du “quand même petrole” augmente
25-40% EVPI maximal Zone d’incertitude maximale, proche du seuil de basculement
40-100% EVPI decroissant Decision claire (forer), oracle moins necessaire

Point cle : Le seuil de decision se situe la ou E[forer] = E[vendre]. Calcul : \(p \times 1.5M + (1-p) \times (-0.5M) = 0.2M\) Solution : \(p = 0.35\) (35%)

L’EVPI est maximal autour de P = 35-40% car c’est la que l’incertitude a le plus d’impact sur la decision. Loin de ce seuil, la decision est “robuste” et l’information a moins de valeur.

Cette propriete est générale : la valeur de l’information est maximale quand la decision est marginale.

#load "../../Infer/SvgChartHelper.cs"

// Visualisation graphique : EVPI en fonction de P(petrole).
// Technique C548-L2 : SVG inline via SvgChartHelper.cs (#6942 MERGED), zero-dependance NuGet.
// Le CDN Plotly precedent rendait BLANC en static (GitHub sandbox les <script src=cdn.plot.ly>).
// Cf #6927 / #3801 / #6942 / See #6954 (App-7b modele).

// Calcul des valeurs EVPI pour differentes probabilites.
var evpiValues = new List<(double p, double evpi, string decision)>();
for (double p = 0.05; p <= 0.95; p += 0.05)
{
    double eu_f = p * profit_forer_petrole + (1 - p) * profit_forer_pasPetrole;
    double eu_v = profit_vendre;
    double best_sans = Math.Max(eu_f, eu_v);
    double max_p = Math.Max(profit_forer_petrole, profit_vendre);
    double max_np = Math.Max(profit_forer_pasPetrole, profit_vendre);
    double eu_parfait = p * max_p + (1 - p) * max_np;
    double evpi_val = eu_parfait - best_sans;
    string decision = eu_f > eu_v ? "F" : "V";
    evpiValues.Add((p, evpi_val, decision));
}
double maxEvpi = evpiValues.Max(x => x.evpi);

// Bar chart SVG inline : EVPI vs P(petrole). Couleur uniforme (palette canon C548-L2).
// Le decodage F=Forer / V=Vendre par barre est preserve via la legende textuelle ci-dessous.
var xLabels29 = evpiValues.Select(v => (v.p * 100).ToString("F0") + "%").ToArray();
var yEvpi29 = evpiValues.Select(v => v.evpi).ToArray();
display(SvgChartHelper.Bar("Profil de l'EVPI selon P(petrole)", xLabels29, yEvpi29));

Console.WriteLine($"Seuil de basculement (Forer/Vendre) : P = 35%");
Console.WriteLine("EVPI maximal autour de P = 35-40% ou la decision est marginale :");
Console.WriteLine("l'information parfaite vaut le plus quand l'incertitude change la decision.");
Profil de l'EVPI selon P(petrole)01228502457003685504914005%10%15%20%25%30%35%40%45%50%55%60%65%70%75%80%85%90%
Seuil de basculement (Forer/Vendre) : P = 35%
EVPI maximal autour de P = 35-40% ou la decision est marginale :
l'information parfaite vaut le plus quand l'incertitude change la decision.

Transition vers l’implementation generique

Après avoir vu les calculs manuels, nous allons maintenant construire un calculateur generique reutilisable pour n’importe quel problème de decision sous incertitude.

6. Implementation Generique avec Infer.NET

// Calculateur generique de VoI avec Infer.NET

public class ValueOfInformation
{
    public string[] States { get; set; }
    public double[] Priors { get; set; }
    public string[] Actions { get; set; }
    public double[,] Utilities { get; set; } // Utilities[action, state]
    
    public (double euNoInfo, string bestActionNoInfo) ComputeWithoutInfo()
    {
        double maxEU = double.NegativeInfinity;
        string bestAction = null;
        
        for (int a = 0; a < Actions.Length; a++)
        {
            double eu = 0;
            for (int s = 0; s < States.Length; s++)
                eu += Priors[s] * Utilities[a, s];
            
            if (eu > maxEU)
            {
                maxEU = eu;
                bestAction = Actions[a];
            }
        }
        return (maxEU, bestAction);
    }
    
    public double ComputeEVPI()
    {
        var (euNoInfo, _) = ComputeWithoutInfo();
        
        double euPerfect = 0;
        for (int s = 0; s < States.Length; s++)
        {
            double maxUForState = double.NegativeInfinity;
            for (int a = 0; a < Actions.Length; a++)
                maxUForState = Math.Max(maxUForState, Utilities[a, s]);
            euPerfect += Priors[s] * maxUForState;
        }
        
        return euPerfect - euNoInfo;
    }
    
    public double ComputeEVSI(double[,] likelihood) // likelihood[signal, state]
    {
        int nSignals = likelihood.GetLength(0);
        var (euNoInfo, _) = ComputeWithoutInfo();
        
        double euWithSignal = 0;
        
        for (int sig = 0; sig < nSignals; sig++)
        {
            // P(signal)
            double pSignal = 0;
            for (int s = 0; s < States.Length; s++)
                pSignal += likelihood[sig, s] * Priors[s];
            
            // Posteriors P(state|signal)
            double[] posteriors = new double[States.Length];
            for (int s = 0; s < States.Length; s++)
                posteriors[s] = (likelihood[sig, s] * Priors[s]) / pSignal;
            
            // Best action given signal
            double maxEUGivenSignal = double.NegativeInfinity;
            for (int a = 0; a < Actions.Length; a++)
            {
                double eu = 0;
                for (int s = 0; s < States.Length; s++)
                    eu += posteriors[s] * Utilities[a, s];
                maxEUGivenSignal = Math.Max(maxEUGivenSignal, eu);
            }
            
            euWithSignal += pSignal * maxEUGivenSignal;
        }
        
        return euWithSignal - euNoInfo;
    }
}

// Test avec le probleme du forage
var voiCalculator = new ValueOfInformation
{
    States = new[] { "Petrole", "Pas Petrole" },
    Priors = new[] { 0.3, 0.7 },
    Actions = new[] { "Forer", "Vendre" },
    Utilities = new double[,]
    {
        { 1_500_000, -500_000 },  // Forer
        { 200_000, 200_000 }       // Vendre
    }
};

var (eu, best) = voiCalculator.ComputeWithoutInfo();
Console.WriteLine($"Sans info : {best} avec EU = {eu:N0}");
Console.WriteLine($"EVPI = {voiCalculator.ComputeEVPI():N0}");

// Likelihood du test sismique
var likelihood = new double[,]
{
    { 0.9, 0.2 },  // P(test+|state)
    { 0.1, 0.8 }   // P(test-|state)
};

Console.WriteLine($"EVSI (test sismique) = {voiCalculator.ComputeEVSI(likelihood):N0}");
Sans info : Vendre avec EU = 200 000
EVPI = 390 000
EVSI (test sismique) = 253 000

Validation du calculateur

Verification : Les valeurs calculees (EU=200k, EVPI=390k, EVSI=253k) correspondent exactement aux calculs manuels de la section 3, validant l’implementation de la classe ValueOfInformation.

Note technique : Cette implementation procedurale est pedagogique. Pour des problemes complexes, Infer.NET automatise le calcul des posterieurs via l’inference variationnelle.

6.1 Visualisation Prior → Posterior avec Infer.NET

Cette section illustre visuellement comment l’observation d’un signal met a jour nos croyances et change potentiellement notre decision.

L’inference bayesienne d’Infer.NET calcule automatiquement les posterieurs, eliminant les calculs manuels de Bayes.

#load "../../Infer/SvgChartHelper.cs"

// Demonstration Prior -> Posterior avec Infer.NET pour le forage petrolier

// Modele generatif avec Infer.NET
Variable<bool> petrole = Variable.Bernoulli(0.3).Named("petrole");
Variable<bool> testSismique = Variable.New<bool>().Named("test_sismique");

// P(test+|petrole) = 90% (sensibilite)
// P(test+|pas petrole) = 20% (1 - specificite)
using (Variable.If(petrole))
    testSismique.SetTo(Variable.Bernoulli(0.9));
using (Variable.IfNot(petrole))
    testSismique.SetTo(Variable.Bernoulli(0.2));

InferenceEngine engineVOI = new InferenceEngine();
engineVOI.Compiler.CompilerChoice = Microsoft.ML.Probabilistic.Compiler.CompilerChoice.Roslyn;

// Utilites
double U_forer_petrole = 1_500_000;
double U_forer_pasPetrole = -500_000;
double U_vendre = 200_000;

Console.WriteLine("=== Inference Bayesienne avec Infer.NET ===\n");

// 1. PRIOR (sans observation)
Console.WriteLine("1. PRIOR (avant le test sismique) :");
Bernoulli priorPetrole = engineVOI.Infer<Bernoulli>(petrole);
double p_prior = priorPetrole.GetProbTrue();
double EU_forer_prior = p_prior * U_forer_petrole + (1 - p_prior) * U_forer_pasPetrole;
Console.WriteLine($"   P(petrole) = {p_prior:P1}");
Console.WriteLine($"   E[U(forer)] = {EU_forer_prior:N0} EUR");
Console.WriteLine($"   E[U(vendre)] = {U_vendre:N0} EUR");
Console.WriteLine($"   => Decision : {(EU_forer_prior > U_vendre ? "FORER" : "VENDRE")}\n");

// 2. POSTERIOR si test POSITIF
testSismique.ObservedValue = true;
Console.WriteLine("2. POSTERIOR (test sismique POSITIF) :");
Bernoulli posteriorTestPos = engineVOI.Infer<Bernoulli>(petrole);
double p_pos = posteriorTestPos.GetProbTrue();
double EU_forer_pos = p_pos * U_forer_petrole + (1 - p_pos) * U_forer_pasPetrole;
Console.WriteLine($"   P(petrole|test+) = {p_pos:P1}  (etait {p_prior:P1})");
Console.WriteLine($"   E[U(forer)|test+] = {EU_forer_pos:N0} EUR");
Console.WriteLine($"   => Decision : {(EU_forer_pos > U_vendre ? "FORER" : "VENDRE")}");
Console.WriteLine($"   Impact : +{p_pos - p_prior:P1} de confiance\n");

// 3. POSTERIOR si test NEGATIF
testSismique.ClearObservedValue();
testSismique.ObservedValue = false;
Console.WriteLine("3. POSTERIOR (test sismique NEGATIF) :");
Bernoulli posteriorTestNeg = engineVOI.Infer<Bernoulli>(petrole);
double p_neg = posteriorTestNeg.GetProbTrue();
double EU_forer_neg = p_neg * U_forer_petrole + (1 - p_neg) * U_forer_pasPetrole;
Console.WriteLine($"   P(petrole|test-) = {p_neg:P1}  (etait {p_prior:P1})");
Console.WriteLine($"   E[U(forer)|test-] = {EU_forer_neg:N0} EUR");
Console.WriteLine($"   => Decision : {(EU_forer_neg > U_vendre ? "FORER" : "VENDRE")}");
Console.WriteLine($"   Impact : {p_neg - p_prior:P1} de confiance\n");

// Resume visuel
Console.WriteLine("=== Visualisation de l'Impact de l'Information ===");
Console.WriteLine();
{
    var pLabels35 = new[] { "Prior", "Posterior T+ (FORER)", "Posterior T- (VENDRE)" };
    var pValues35 = new double[] { p_prior, p_pos, p_neg };
    display(SvgChartHelper.Bar("Impact du test sismique : P(petrole) prior vs posteriors", pLabels35, pValues35));
}

Console.WriteLine();
Console.WriteLine("=> Le test change la decision : c'est pourquoi il a de la valeur !");
=== Inference Bayesienne avec Infer.NET ===

1. PRIOR (avant le test sismique) :
Compiling model...done.
   P(petrole) = 30,0 %
   E[U(forer)] = 100 000 EUR
   E[U(vendre)] = 200 000 EUR
   => Decision : VENDRE

2. POSTERIOR (test sismique POSITIF) :
Compiling model...done.
   P(petrole|test+) = 65,9 %  (etait 30,0 %)
   E[U(forer)|test+] = 817 073 EUR
   => Decision : FORER
   Impact : +35,9 % de confiance

3. POSTERIOR (test sismique NEGATIF) :
Compiling model...done.
   P(petrole|test-) = 5,1 %  (etait 30,0 %)
   E[U(forer)|test-] = -398 305 EUR
   => Decision : VENDRE
   Impact : -24,9 % de confiance

=== Visualisation de l'Impact de l'Information ===
Impact du test sismique : P(petrole) prior vs posteriors00.1780.3560.5330.711PriorPosterior T+ (FORER)Posterior T- (VENDRE)

=> Le test change la decision : c'est pourquoi il a de la valeur !

Visualisation du Factor Graph

Le factor graph ci-dessous illustre la structure du modèle bayesien utilise pour l’inference. Cette visualisation complete les calculs numériques en montrant :

  • La variable latente petrole : presence ou non de petrole (prior Bernoulli)
  • La variable testSismique : résultat du test sismique conditionnel a ‘petrole’
  • Les facteurs Bernoulli : le prior sur ‘petrole’ et la likelihood P(test|petrole)

Le diagramme ci-dessous rend cette structure sous forme de graphe (au lieu de la representation textuelle de la cellule suivante) : les variables aleatoires sont des cercles, les facteurs probabilistes des rectangles, la decision un losange et l’utilite un noeud arrondi – c’est un diagramme d’influence, l’extension decisionnelle du factor graph :

flowchart TD
    fPrior["facteur<br/>Prior Bernoulli(0.30)"] --> petrole(("petrole"))
    petrole --> fLik["facteur<br/>P(test | petrole)<br/>sens=90%, spec=80%"]
    fLik --> test(("test_sismique"))
    test -. observe .-> D{"Decision<br/>Forer / Vendre"}
    petrole --> U["Utilite<br/>U(action, etat)"]
    D --> U
    classDef fac fill:#fff3cd,stroke:#b8860b,color:#5c4400
    classDef var fill:#cfe2ff,stroke:#084298,color:#052c65
    classDef dec fill:#f8d7da,stroke:#842029,color:#58151c
    classDef ut fill:#d1e7dd,stroke:#0f5132,color:#0a3622
    class fPrior,fLik fac
    class petrole,test var
    class D dec
    class U ut

Lecture. Le prior Bernoulli(0.30) définit la croyance a priori sur la presence de petrole (variable latente). Le facteur de vraisemblance P(test | petrole) relie l’etat cache au résultat observable test_sismique (sensibilite 90 %, specificite 80 %). Observer le test (arc pointille) met a jour le posterior sur petrole, qui alimente l’utilite U(action, etat) – c’est ce posterior, calcule automatiquement par Infer.NET, qui rend la Decision (Forer / Vendre) optimale en esperance. La valeur de l’information mesure exactement le gain d’utilite apporte par cet arc d’observation.

// Visualisation du Factor Graph pour le modele de forage petrolier
// Ce graphe montre la structure probabiliste du probleme de decision

if (ShowFactorGraph)
{
    Console.WriteLine("=== Factor Graph : Modele de Valeur de l'Information ===\n");
    
    // Representation textuelle du factor graph
    // Structure : Variable (cercle) -> Factor (carre) -> Variable
    Console.WriteLine("Structure du modele bayesien :");
    Console.WriteLine();
    Console.WriteLine("    +-------------------+");
    Console.WriteLine("    |   Prior: 30%     |");
    Console.WriteLine("    |   Bernoulli      |");
    Console.WriteLine("    +--------+----------+");
    Console.WriteLine("             |");
    Console.WriteLine("             v");
    Console.WriteLine("    (( petrole ))     <- Variable latente (etat du monde)");
    Console.WriteLine("             |");
    Console.WriteLine("             |");
    Console.WriteLine("    +--------+----------+         +------------------+");
    Console.WriteLine("    |   Likelihood     |         |    Utilites      |");
    Console.WriteLine("    | P(test|petrole)  |         | U(action,etat)   |");
    Console.WriteLine("    | sens=90%,spec=80%|         +------------------+");
    Console.WriteLine("    +--------+----------+                  |");
    Console.WriteLine("             |                             |");
    Console.WriteLine("             v                             v");
    Console.WriteLine("    (( test_sismique ))           [[ Decision ]]");
    Console.WriteLine("             |                    Forer ou Vendre");
    Console.WriteLine("             |");
    Console.WriteLine("    +--------+----------+");
    Console.WriteLine("    |   Observation    |");
    Console.WriteLine("    |   test+ ou test- |");
    Console.WriteLine("    +-------------------+");
    Console.WriteLine();
    Console.WriteLine("Legende :");
    Console.WriteLine("  (( )) = Variable aleatoire");
    Console.WriteLine("  [[ ]] = Noeud de decision");
    Console.WriteLine("  +--+  = Facteur (contrainte probabiliste)");
    Console.WriteLine();
    Console.WriteLine("Flux d'inference :");
    Console.WriteLine("  1. Prior sur 'petrole' : P(petrole) = 0.30");
    Console.WriteLine("  2. Likelihood : P(test+|petrole) = 0.90, P(test+|~petrole) = 0.20");
    Console.WriteLine("  3. Observation : test_sismique = true (ou false)");
    Console.WriteLine("  4. Posterior : P(petrole|test) calcule par Infer.NET via EP");
    Console.WriteLine("  5. Decision : argmax_a E[U(a) | posterior]");
    
    // Tentative d'afficher le factor graph genere par Infer.NET si disponible
    try 
    {
        string graphHtml = FactorGraphHelper.GetLatestFactorGraphHtml();
        if (!graphHtml.Contains("Aucun fichier"))
        {
            Console.WriteLine("\n[Factor graph SVG genere par Infer.NET disponible ci-dessous]");
        }
    }
    catch { }
}
=== Factor Graph : Modele de Valeur de l'Information ===

Structure du modele bayesien :

    +-------------------+
    |   Prior: 30%     |
    |   Bernoulli      |
    +--------+----------+
             |
             v
    (( petrole ))     <- Variable latente (etat du monde)
             |
             |
    +--------+----------+         +------------------+
    |   Likelihood     |         |    Utilites      |
    | P(test|petrole)  |         | U(action,etat)   |
    | sens=90%,spec=80%|         +------------------+
    +--------+----------+                  |
             |                             |
             v                             v
    (( test_sismique ))           [[ Decision ]]
             |                    Forer ou Vendre
             |
    +--------+----------+
    |   Observation    |
    |   test+ ou test- |
    +-------------------+

Legende :
  (( )) = Variable aleatoire
  [[ ]] = Noeud de decision
  +--+  = Facteur (contrainte probabiliste)

Flux d'inference :
  1. Prior sur 'petrole' : P(petrole) = 0.30
  2. Likelihood : P(test+|petrole) = 0.90, P(test+|~petrole) = 0.20
  3. Observation : test_sismique = true (ou false)
  4. Posterior : P(petrole|test) calcule par Infer.NET via EP
  5. Decision : argmax_a E[U(a) | posterior]

Interpretation de la visualisation Prior → Posterior

Ce que montre Infer.NET : - Le moteur d’inference calcule automatiquement les posterieurs bayesiens - Pas besoin d’ecrire la formule de Bayes explicitement : on définit le modèle generatif et Infer.NET fait le reste

Impact des observations :

Situation P(petrole) Decision Justification
Prior 30% VENDRE EU(forer) = 100k < EU(vendre) = 200k
Test + 66% FORER EU(forer) = 817k > EU(vendre) = 200k
Test - 5% VENDRE EU(forer) = -398k < EU(vendre) = 200k

Visualisation des barres : - La barre # represente la probabilite de petrole - On voit clairement comment le test “pousse” le belief vers les extremes (66% ou 5%) - Cette polarisation est ce qui permet au test de changer la decision

Point cle technique : Infer.NET utilise Expectation Propagation (EP) pour calculer efficacement les posterieurs, même pour des modèles complexes avec de nombreuses variables.

7. Exemple guide : Faut-il Faire un Test Medical ?

Enonce

Un patient a 5% de chance d’avoir une maladie grave. Un test coute 100 EUR, avec sensibilite 95% et specificite 90%.

Le traitement coute 2000 EUR et : - Guerit 90% des malades (utilite 10000 si gueri) - A des effets secondaires pour les non-malades (utilite 8000)

Sans traitement : - Les malades ont utilite 3000 (maladie non traitee) - Les non-malades ont utilite 10000 (parfait)

Question : Le test vaut-il son prix ?

7bis. Application : Diagnostic Medical avec Tests Successifs

Scénario

Un patient presente des symptomes. Le medecin doit choisir entre : 1. Traiter immediatement (risque de sur-traitement) 2. Test 1 : Test rapide (50 EUR, sensibilite 70%, specificite 85%) 3. Test 2 : IRM (500 EUR, sensibilite 95%, specificite 98%) 4. Les deux tests (information maximale, cout eleve)

Cet exemple illustre la decision de chaîne de tests ou l’on peut effectuer plusieurs tests en sequence, chaque test ayant un cout et une qualite différente.

Modelisation du problème

Cascade de tests typique :

Symptomes -> Test rapide (peu couteux, moins précis)
                |
                +-> Positif -> IRM (couteux, très précis) -> Decision finale
                +-> Negatif -> Pas d'IRM necessaire

Avec 3 etats (sain, leger, grave) et 3 actions (rien, leger, lourd), l’espace de decision est riche. Les valeurs negatives pour “Sain + Traitement” representent les effets secondaires inutiles.

// RDD Medical : Diagnostic avec Tests Successifs et Couts
// Maladie latente (3 etats) : 0=sain, 1=leger, 2=grave

int nEtats = 3;
string[] etats = { "Sain", "Leger", "Grave" };
double[] priorMaladie = { 0.70, 0.20, 0.10 };  // Prior sur les etats

// === Definition des Tests ===
// Test 1 : Rapide (sensibilite 70%, specificite 85%, cout 50 EUR)
double coutT1 = 50;
double[,] likelihoodT1 = {
    // P(resultat | etat)     Sain   Leger  Grave
    /* Positif */           { 0.15,  0.70,  0.95 },
    /* Negatif */           { 0.85,  0.30,  0.05 }
};

// Test 2 : IRM (sensibilite 95%, specificite 98%, cout 500 EUR)
double coutT2 = 500;
double[,] likelihoodT2 = {
    // P(resultat | etat)     Sain   Leger  Grave
    /* Positif */           { 0.02,  0.95,  0.99 },
    /* Negatif */           { 0.98,  0.05,  0.01 }
};

// === Matrice d'Utilites ===
// U[action, etat] : action 0=Rien, 1=Traitement leger, 2=Traitement lourd
double[,] U = {
    //              Sain    Leger   Grave
    /* Rien */    {  100,   -200,  -5000 },  // Pas de traitement
    /* Leger */   {  -50,    800,   -500 },  // Traitement leger (effets secondaires si sain)
    /* Lourd */   { -500,    500,   2000 }   // Traitement lourd (toujours couteux mais sauve si grave)
};
string[] actions = { "Rien", "Traitement leger", "Traitement lourd" };

Console.WriteLine("=== RDD Medical : Tests Successifs ===\n");
Console.WriteLine("Etats possibles :");
for (int e = 0; e < nEtats; e++)
    Console.WriteLine($"  P({etats[e]}) = {priorMaladie[e]:P0}");
Console.WriteLine();

// === 1. Decision sans test ===
double[] EU_actions = new double[3];
for (int a = 0; a < 3; a++)
{
    for (int e = 0; e < nEtats; e++)
        EU_actions[a] += priorMaladie[e] * U[a, e];
}

int bestAction_noTest = EU_actions.Select((v, i) => (v, i)).OrderByDescending(x => x.v).First().i;
double bestEU_noTest = EU_actions[bestAction_noTest];

Console.WriteLine("1. SANS TEST :");
for (int a = 0; a < 3; a++)
    Console.WriteLine($"   E[U({actions[a]})] = {EU_actions[a]:F0}");
Console.WriteLine($"   => Decision : {actions[bestAction_noTest]} (EU = {bestEU_noTest:F0})\n");

// === 2. EVPI ===
double EU_parfait = 0;
for (int e = 0; e < nEtats; e++)
{
    double maxU_etat = double.NegativeInfinity;
    for (int a = 0; a < 3; a++)
        maxU_etat = Math.Max(maxU_etat, U[a, e]);
    EU_parfait += priorMaladie[e] * maxU_etat;
}
double EVPI_med = EU_parfait - bestEU_noTest;

Console.WriteLine($"2. EVPI = {EVPI_med:F0} EUR\n");

// === Fonction EVSI pour un test ===
double CalculerEVSI(double[,] likelihood, double[] prior, double[,] utilities, double bestEU_base)
{
    int nSignaux = likelihood.GetLength(0);
    int nEtats_loc = likelihood.GetLength(1);
    int nActions_loc = utilities.GetLength(0);
    
    double EU_avecTest = 0;
    
    for (int sig = 0; sig < nSignaux; sig++)
    {
        // P(signal)
        double pSignal = 0;
        for (int e = 0; e < nEtats_loc; e++)
            pSignal += likelihood[sig, e] * prior[e];
        
        // Posterieurs P(etat|signal)
        double[] posterior = new double[nEtats_loc];
        for (int e = 0; e < nEtats_loc; e++)
            posterior[e] = (likelihood[sig, e] * prior[e]) / pSignal;
        
        // Meilleure action etant donne le signal
        double maxEU_signal = double.NegativeInfinity;
        for (int a = 0; a < nActions_loc; a++)
        {
            double eu = 0;
            for (int e = 0; e < nEtats_loc; e++)
                eu += posterior[e] * utilities[a, e];
            maxEU_signal = Math.Max(maxEU_signal, eu);
        }
        
        EU_avecTest += pSignal * maxEU_signal;
    }
    
    return EU_avecTest - bestEU_base;
}

// === 3. EVSI Test 1 (Rapide) ===
double EVSI_T1 = CalculerEVSI(likelihoodT1, priorMaladie, U, bestEU_noTest);
double EVSI_net_T1 = EVSI_T1 - coutT1;

Console.WriteLine($"3. TEST 1 (Rapide, {coutT1} EUR) :");
Console.WriteLine($"   EVSI = {EVSI_T1:F0} EUR");
Console.WriteLine($"   EVSI net = {EVSI_net_T1:F0} EUR");
Console.WriteLine($"   Efficacite = {EVSI_T1 / EVPI_med:P0}\n");

// === 4. EVSI Test 2 (IRM) ===
double EVSI_T2 = CalculerEVSI(likelihoodT2, priorMaladie, U, bestEU_noTest);
double EVSI_net_T2 = EVSI_T2 - coutT2;

Console.WriteLine($"4. TEST 2 (IRM, {coutT2} EUR) :");
Console.WriteLine($"   EVSI = {EVSI_T2:F0} EUR");
Console.WriteLine($"   EVSI net = {EVSI_net_T2:F0} EUR");
Console.WriteLine($"   Efficacite = {EVSI_T2 / EVPI_med:P0}\n");

// === 5. Strategie optimale ===
Console.WriteLine("=== RECOMMANDATION ===");
var strategies = new[] {
    ("Pas de test", bestEU_noTest),
    ("Test 1 seul", bestEU_noTest + EVSI_net_T1),
    ("Test 2 seul", bestEU_noTest + EVSI_net_T2)
};

var bestStrat = strategies.OrderByDescending(s => s.Item2).First();
Console.WriteLine($"Strategie optimale : {bestStrat.Item1}");
Console.WriteLine($"EU attendue = {bestStrat.Item2:F0} EUR");
Console.WriteLine();
Console.WriteLine($"Le test rapide ({coutT1} EUR) offre {EVSI_T1/EVPI_med:P0} de l'info parfaite");
Console.WriteLine($"L'IRM ({coutT2} EUR) offre {EVSI_T2/EVPI_med:P0} mais coute {coutT2/coutT1:F0}x plus cher");
=== RDD Medical : Tests Successifs ===

Etats possibles :
  P(Sain) = 70 %
  P(Leger) = 20 %
  P(Grave) = 10 %

1. SANS TEST :
   E[U(Rien)] = -470
   E[U(Traitement leger)] = 75
   E[U(Traitement lourd)] = -50
   => Decision : Traitement leger (EU = 75)

2. EVPI = 355 EUR

3. TEST 1 (Rapide, 50 EUR) :
   EVSI = 155 EUR
   EVSI net = 105 EUR
   Efficacite = 44 %

4. TEST 2 (IRM, 500 EUR) :
   EVSI = 273 EUR
   EVSI net = -227 EUR
   Efficacite = 77 %

=== RECOMMANDATION ===
Strategie optimale : Test 1 seul
EU attendue = 180 EUR

Le test rapide (50 EUR) offre 44 % de l'info parfaite
L'IRM (500 EUR) offre 77 % mais coute 10x plus cher

Interpretation du diagnostic medical avec tests successifs

Contexte clinique :

Le medecin fait face a un problème de decision a 3 etats et 3 actions : - Etats : Sain (70%), Maladie legere (20%), Maladie grave (10%) - Actions : Ne rien faire, Traitement leger, Traitement lourd - Tests : Rapide (50 EUR) ou IRM (500 EUR)

Analyse des résultats :

Metrique Sans test Test rapide IRM
Decision optimale Ttt leger Depend du résultat Depend du résultat
EU 75 EUR 180 EUR -152 EUR
EVSI – 155 EUR 273 EUR
EVSI net – +105 EUR -227 EUR
Efficacite – 44% 77%

Paradoxe resolu : qualite vs cout

L’IRM est plus informative (77% vs 44%) mais son cout eleve (500 EUR) depasse sa valeur nette ! C’est un cas classique ou le “meilleur” test n’est pas le plus rentable.

Recommandation : - Faire le test rapide uniquement (gain net = 105 EUR) - L’IRM ne serait justifiee que si son cout baissait a moins de 273 EUR

Lecon pratique : En medecine comme ailleurs, le test le plus précis n’est pas toujours le meilleur choix. Le ratio cout/benefice doit guider la decision.

#load "../../Infer/SvgChartHelper.cs"

// Visualisation : Comparaison des tests medicaux (EVSI, Cout, Efficacite)
// Barres groupees comparant les metriques cles des deux tests (EVSI brute / Cout / EVSI nette)

Console.WriteLine("\n=== Visualisation : Comparaison des Tests Medicaux ===\n");

// Donnees pour la visualisation (une ligne par test) : nom, EVSI brute, cout, EVSI nette, efficacite
var testsData = new[] {
    ("Test Rapide", EVSI_T1, coutT1, EVSI_net_T1, EVSI_T1 / EVPI_med),
    ("IRM", EVSI_T2, coutT2, EVSI_net_T2, EVSI_T2 / EVPI_med)
};

// Tableau synthetique : rend les 6 valeurs (EVSI brute / Cout / EVSI nette) en Console
Console.WriteLine("  Test        | EVSI brute | Cout | EVSI nette | Efficacite (EVSI/EVPI)");
Console.WriteLine("  ------------|------------|------|------------|------------------------");
foreach (var t in testsData)
    Console.WriteLine($"  {t.Item1,-11} | {t.Item2,9:F0} | {t.Item3,4:F0} | {t.Item4,9:F0} | {t.Item5,8:P0}");

Console.WriteLine();
Console.WriteLine("Barres groupees ci-dessous : EVSI brute / Cout / EVSI nette (EUR), une serie par metrique.");
{
    var names45a = testsData.Select(t => t.Item1).ToArray();
    var series45a = new[] {
        testsData.Select(t => t.Item2).ToArray(),  // EVSI brute
        testsData.Select(t => t.Item3).ToArray(),  // Cout
        testsData.Select(t => t.Item4).ToArray()   // EVSI nette
    };
    display(SvgChartHelper.GroupedBar(
        "Comparaison des tests medicaux : EVSI brute, Cout, EVSI nette (EUR)",
        names45a, series45a, new[] { "EVSI brute", "Cout", "EVSI nette" }));
}
Console.WriteLine();
{
    var names45b = new[] { "Test Rapide", "IRM" };
    var vals45b = new double[] { Math.Round((EVSI_T1 / EVPI_med) * 100, 1), Math.Round((EVSI_T2 / EVPI_med) * 100, 1) };
    display(SvgChartHelper.Bar("Efficacite (EVSI/EVPI) : proportion de l'info parfaite capturee (%)", names45b, vals45b));
}

Console.WriteLine();
Console.WriteLine("=> Le Test Rapide est OPTIMAL : meilleure rentabilite malgre efficacite inferieure");
Console.WriteLine($"   Ratio cout-efficacite : Test Rapide = {(EVSI_T1/coutT1):F1} EUR/EUR de cout");

=== Visualisation : Comparaison des Tests Medicaux ===

  Test        | EVSI brute | Cout | EVSI nette | Efficacite (EVSI/EVPI)
  ------------|------------|------|------------|------------------------
  Test Rapide |       155 |   50 |       105 |     44 %
  IRM         |       273 |  500 |      -227 |     77 %

Barres groupees ci-dessous : EVSI brute / Cout / EVSI nette (EUR), une serie par metrique.
Comparaison des tests medicaux : EVSI brute, Cout, EVSI nette (EUR)-285.592-74.646136.3347.246558.192Test RapideIRMEVSI bruteCoutEVSI nette
Efficacite (EVSI/EVPI) : proportion de l'info parfaite capturee (%)020.73641.47262.20882.944Test RapideIRM

=> Le Test Rapide est OPTIMAL : meilleure rentabilite malgre efficacite inferieure
   Ratio cout-efficacite : Test Rapide = 3,1 EUR/EUR de cout

Extension : stratégie séquentielle

Une approche plus sophistiquee serait de faire le test rapide d’abord, puis l’IRM uniquement si le test rapide est positif. Cette stratégie “en entonnoir” est typique des protocoles medicaux reels et necessite un arbre de decision a plusieurs niveaux.

Règle pratique : Un test n’est rentable que si EVSI > Cout du test.

// Exercice : Decision de test medical

// Parametres
double pMaladie_ex = 0.05;
double sensibilite_ex = 0.95;
double specificite_ex = 0.90;
double coutTest_ex = 100;
double coutTraitement = 2000;

// Utilites brutes
double U_traiter_malade_gueri = 10000 - coutTraitement;
double U_traiter_malade_echec = 3000 - coutTraitement;
double U_traiter_sain = 8000 - coutTraitement;
double U_pasTraiter_malade = 3000;
double U_pasTraiter_sain = 10000;

// Utilite esperee de traiter un malade (90% guerison)
double U_traiter_malade = 0.9 * U_traiter_malade_gueri + 0.1 * U_traiter_malade_echec;

Console.WriteLine("=== Exercice : Test Medical ===\n");
Console.WriteLine($"P(maladie) = {pMaladie_ex:P0}");
Console.WriteLine($"Cout test = {coutTest_ex} EUR\n");

// TODO 1 : Calculer E[U] sans information pour chaque action (traiter vs pas traiter)
// Formule : E[U(action)] = P(malade) * U(action, malade) + P(sain) * U(action, sain)

// TODO 2 : Calculer l'EVPI (Expected Value of Perfect Information)
// Formule : EVPI = E[max_a U(a, s)] - max_a E[U(a)]
// Indice : pour chaque etat (malade/sain), trouvez la meilleure action

// TODO 3 : Calculer l'EVSI (Expected Value of Sample Information) avec le test
// Etapes :
//   a) Calculer P(test positif) = sensibilite * P(malade) + (1-specificite) * P(sain)
//   b) Calculer P(malade | test positif) et P(malade | test negatif) par Bayes
//   c) Pour chaque resultat de test, calculer la meilleure action
//   d) EVSI = E[max_a U(a | test)] - max_a E[U(a)]

// TODO 4 : Comparer EVSI avec le cout du test et decider
// Indice : faire le test si EVSI > coutTest
=== Exercice : Test Medical ===

P(maladie) = 5 %
Cout test = 100 EUR

Interpretation de vos résultats

Après avoir implemente le code, analysez :

  1. L’EVPI est-elle grande ou petite par rapport au cout du traitement ?
  2. L’EVSI est-elle superieure au cout du test ?
  3. La decision change-t-elle selon le résultat du test (positif vs negatif) ?
  4. Si la prevalence etait 20% au lieu de 5%, le test serait-il plus utile ?
#load "../../Infer/SvgChartHelper.cs"

// Visualisation recapitulative : Comparaison VoI sur tous les exemples du notebook

Console.WriteLine("=== Tableau Recapitulatif : Valeur de l'Information ===\n");

// Donnees des exemples vus dans le notebook
var exemples = new[] {
    ("Parapluie", "Oracle meteo", 15.0, 15.0, 100.0, "Oui", "pts"),
    ("Forage", "Test sismique", 390_000.0, 253_000.0, 65.0, "Oui", "EUR"),
    ("Tresor", "Detecteur", 20.0, 7.8, 39.0, "Oui", "EUR"),
    ("Medical 3 etats", "Test rapide", 355.0, 155.0, 44.0, "Oui", "EUR"),
    ("Medical 3 etats", "IRM", 355.0, 273.0, 77.0, "Non (cout)", "EUR"),
    ("Medical rare", "Test 95/90", 215.0, 0.0, 0.0, "Non", "EUR")
};

// En-tete
Console.WriteLine($"{"Exemple",-16} {"Test",-14} {"EVPI",-10} {"EVSI",-10} {"Eff%",-8} {"Rentable",-12}");
Console.WriteLine(new string('-', 80));

foreach (var (exemple, test, evpi, evsi, eff, rentable, unite) in exemples)
{
    string evpiStr = evpi >= 1000 ? $"{evpi/1000:F0}k" : $"{evpi:F0}";
    string evsiStr = evsi >= 1000 ? $"{evsi/1000:F0}k" : $"{evsi:F0}";
    
    Console.WriteLine($"{exemple,-16} {test,-14} {evpiStr,-10} {evsiStr,-10} {eff,-7:F0}% {rentable,-12}");
}

Console.WriteLine();
Console.WriteLine("Observations cles :");
Console.WriteLine();
Console.WriteLine("  1. EVSI <= EVPI toujours (information imparfaite ne peut depasser parfaite)");
Console.WriteLine("  2. Efficacite varie de 0% (test inutile) a 65% (tres bon test)");
Console.WriteLine("  3. Meme un test performant peut etre inutile si prior trop extreme");
Console.WriteLine("  4. Cout vs valeur : IRM (77% eff) moins rentable que test rapide (44% eff)");
Console.WriteLine();

// Visualisation en barres de l'efficacite : la couleur encode la rentabilite (par-barre).
{
    var labels49 = exemples.Select(e => e.Item2).ToArray();
    var effs49 = exemples.Select(e => Math.Round(e.Item5, 1)).ToArray();
    // Couleur conditionnelle par barre : bleu si rentable (colonne "Rentable" commence par
    // "Oui"), orange sinon. Rend visible le paradoxe cout/valeur - l'IRM affiche 77 %
    // d'efficacite mais reste NON rentable a cause de son cout - qu'une couleur unique masquait.
    var colors49 = exemples.Select(e => e.Item6.StartsWith("Oui") ? "#4C72B0" : "#DD8452").ToArray();
    var legend49 = new List<(string, string)> { ("#4C72B0", "rentable"), ("#DD8452", "non rentable") };
    display(SvgChartHelper.Bar(
        "Efficacite des tests (EVSI/EVPI) - couleur = rentabilite",
        labels49, effs49, width: 720, colors: colors49, legend: legend49));
}
Console.WriteLine("  Chart : hauteur = efficacite (EVSI/EVPI, %), couleur = rentabilite (voir legende).");
=== Tableau Recapitulatif : Valeur de l'Information ===

Exemple          Test           EVPI       EVSI       Eff%     Rentable    
--------------------------------------------------------------------------------
Parapluie        Oracle meteo   15         15         100    % Oui         
Forage           Test sismique  390k       253k       65     % Oui         
Tresor           Detecteur      20         8          39     % Oui         
Medical 3 etats  Test rapide    355        155        44     % Oui         
Medical 3 etats  IRM            355        273        77     % Non (cout)  
Medical rare     Test 95/90     215        0          0      % Non         

Observations cles :

  1. EVSI <= EVPI toujours (information imparfaite ne peut depasser parfaite)
  2. Efficacite varie de 0% (test inutile) a 65% (tres bon test)
  3. Meme un test performant peut etre inutile si prior trop extreme
  4. Cout vs valeur : IRM (77% eff) moins rentable que test rapide (44% eff)
Efficacite des tests (EVSI/EVPI) - couleur = rentabilite0275481108Oracle meteoTest sismiqueDetecteurTest rapideIRMTest 95/90rentablenon rentable
  Chart : hauteur = efficacite (EVSI/EVPI, %), couleur = rentabilite (voir legende).

Points cles a retenir de ce notebook

1. L’EVPI mesure le “cout de l’ignorance” - C’est la différence entre decider en sachant tout et decider dans l’incertitude - Borne superieure : aucun test imparfait ne peut valoir plus que l’EVPI

2. L’EVSI mesure la valeur d’un test spécifique - Depend de la qualite du test (sensibilite, specificite) - Doit etre compare au cout du test pour une decision rentable

3. Trois conditions pour qu’un test ait de la valeur :

EVSI > 0  <=>  (EVPI > 0) AND (test informatif) AND (posterieurs changent decision)

4. Applications pratiques vues :

Domaine Exemple Conclusion
Petrole Test sismique avant forage Rentable (EVSI net = 203k)
Chasse au tresor Detecteur de metal Moderement utile (39% efficacite)
Medecine Tests successifs Depistage rapide optimal
Medecine Test sur maladie rare Inutile même gratuit (EVSI = 0)

5. Integration avec Infer.NET : - Infer.NET calcule automatiquement les posterieurs bayesiens - Elimine les calculs manuels de la règle de Bayes - Permet de se concentrer sur la modelisation du problème

8. Resume

Concept Formule Interpretation
EVPI E[max U|ω] - max E[U] Valeur d’un oracle parfait
EVSI E[max U|signal] - max E[U] Valeur d’un signal imparfait
Efficiency EVSI / EVPI Qualite relative de l’information

Conditions pour VoI > 0

  1. L’information doit etre pertinente (changer les posterieurs)
  2. Les posterieurs doivent changer la decision
  3. Le cout de l’information doit etre inferieur a l’EVSI

Pour aller plus loin

Si vous voulez… Consultez…
Decisions robustes DecInfer-07-Expert-Systems
Decisions séquentielles DecInfer-08-Sequential

References

  • Raiffa & Schlaifer (1961) : Applied Statistical Decision Theory
  • Russell & Norvig : AI, Chapter 16.6
  • Howard (1966) : Information Value Theory

Exercice 3 : VoI pour un Portfolio d’Investissement

Objectif : Calculez l’EVPI et l’EVSI pour un problème de decision d’investissement avec 3 etats economiques et 3 actions possibles.

Contexte : Un investisseur dispose de 100k EUR et hesite entre 3 stratégies : actions, obligations ou immobiliser. L’economie peut etre en croissance, stagnation ou recession. Une analyse economique (cout : 5k EUR) peut fournir un signal imparfait sur l’etat futur.

Données :

Paramètre Valeur
P(croissance) 40%
P(stagnation) 35%
P(recession) 25%
Utilite (k EUR) Croissance Stagnation Recession
Actions 130 95 60
Obligations 105 100 95
Immobiliser 100 100 100

Sensibilite de l’analyse : P(signal croissance|croissance) = 80%, P(signal stagnation|stagnation) = 60%, P(signal recession|recession) = 75%. Pour les autres cas, la probabilite est repartie uniformement.

Indices : - Indice 1 : Sans information, calculer E[U(action)] = Somme P(etat) * U(action, etat) pour chaque action - Indice 2 : EVPI = Somme P(etat) * max_action U(action, etat) - max_action E[U(action)] - Étape 1 : Determiner la decision optimale sans information et calculer l’EVPI - Étape 2 : Construire la matrice de vraisemblance du signal (3x3) et calculer les posteriors par Bayes - Étape 3 : Calculer l’EVSI et conclure si l’analyse economique (5k EUR) est rentable

// Exercice : VoI pour un Portfolio d'Investissement

// Priors sur les etats economiques
double[] priorsEco = { 0.40, 0.35, 0.25 };  // croissance, stagnation, recession
string[] etatsEco = { "Croissance", "Stagnation", "Recession" };
string[] actionsEco = { "Actions", "Obligations", "Immobiliser" };

// Matrice d'utilites U[action, etat] (en k EUR)
double[,] U_portfolio = {
    //  Croissance  Stagnation  Recession
    {     130,        95,        60  },  // Actions
    {     105,       100,        95  },  // Obligations
    {     100,       100,       100  }   // Immobiliser
};

// Sensibilite de l'analyse economique
// P(signal i | etat j) : diagonale forte, reste uniforme
double[,] likelihoodEco = {
    //       Croissance  Stagnation  Recession   (colonnes = etat reel)
    /* sig Croissance */ { 0.80, 0.10, 0.125 },
    /* sig Stagnation  */ { 0.10, 0.60, 0.125 },
    /* sig Recession   */ { 0.10, 0.30, 0.75  }
};
double coutAnalyse = 5.0;  // k EUR

Console.WriteLine("=== Exercice : VoI pour un Portfolio d'Investissement ===\n");

// TODO 1 : Calculer E[U(action)] pour chaque action sans information
// Indice : E[U(a)] = somme_sur_etat P(etat) * U(a, etat)
// double[] EU_actions = ...

// TODO 2 : Determiner la decision optimale et calculer l'EVPI
// Indice : EVPI = somme P(etat) * max_a U(a, etat) - max_a E[U(a)]
// double EVPI_portfolio = ...

// TODO 3 : Calculer l'EVSI avec la matrice de vraisemblance
// Indice : pour chaque signal, calculer P(etat|signal) par Bayes
// puis bestEU(signal) = max_a E[U(a)|signal]
// EVSI = somme P(signal) * bestEU(signal) - max_a E[U(a)]
// double EVSI_portfolio = ...

// TODO 4 : Conclure si l'analyse (5k EUR) est rentable
// Indice : comparer EVSI_portfolio avec coutAnalyse
// Console.WriteLine($"Analyse economique : EVSI net = {EVSI_portfolio - coutAnalyse:F1} k EUR => ...");

Console.WriteLine("Exercice a completer");
=== Exercice : VoI pour un Portfolio d'Investissement ===

Exercice a completer

Conclusion

Ce notebook a pose le cadre de la valeur de l’information (VoI) : une théorie de decision qui quantifie, en esperance d’utilite, combien il vaut la peine d’acquerir une information supplementaire avant de decider.

La VoI, un pont entre probabilite et decision

La VoI articule trois quantites complementaires, chacune repondant a une question différente :

Question pratique Quantite Reponse
Jusqu’ou un oracle pourrait-il ameliorer ma decision ? EVPI Borne superieure de la valeur d’information
Combien vaut ce test concret ? EVSI Valeur reelle, a comparer au cout du test
Mon test est-il proche de l’optimal ? Efficiency = EVSI / EVPI Ratio de qualite (0 a 1)

Le triptyque “pertinence - impact - cout”

La condition EVSI > 0 se decompose en trois exigences cumulatives, demontrees par les exemples du notebook :

  1. Pertinence : le signal doit modifier les posterieurs (un test non informatif a une EVSI nulle, même s’il est gratuit).
  2. Impact : les posterieurs doivent changer la decision optimale (informer sans faire basculer l’action n’apporte rien).
  3. Cout : le cout d’acquisition doit rester inferieur a l’EVSI pour que le test soit rentable.

Le cas de la maladie rare (EVSI = 0 malgre un test gratuit) illustre l’echec du premier critere : un test sur un événement de faible probabilite anterieure deplace a peine les croyances. A l’inverse, le test sismique avant forage satisfait les trois critères et s’avere rentable (EVSI net de 203k).

Ce que Infer.NET apporte a l’analyse

Les calculs d’EVPI et d’EVSI reposent sur des posterieurs bayesiens qu’Infer.NET calcule automatiquement a partir de la modelisation du graphe. La valeur ajoutee n’est pas dans la formule de VoI elle-même (une esperance d’utilite), mais dans l’elimination des calculs manuels de la règle de Bayes : le practicien se concentre sur la structure du problème (etats, actions, signaux, utilites) et laisse le moteur inferentiel propager les croyances.

Arc pedagogique de la serie

Après les fondations de l’utilite (DecInfer-01 et 03 : axiomes VNM, CRRA/CARA), l’extension multi-attribut (DecInfer-04) et les reseaux de decision (DecInfer-05), ce carnet DecInfer-06 boucle la boucle en montrant quand l’incertitude elle-même a un prix. La suite immediate est la robustesse face a l’incertitude epistemique (DecInfer-07 : critères minimax, Hurwicz, Laplace) puis les decisions séquentielles (DecInfer-08 : MDP, bandits, POMDP), ou la VoI devient dynamique – c’est précisément le formalisme qui sous-tend l’indice de Gittins et l’exploration-exploitation.

A retenir : dans tout problème de decision sous incertitude, se demander quelle information pourrait changer ma decision, et a quel prix est le reflexe fondamental que la VoI formalise. Le calcul est bayesien ; l’intuition est economique.

Retour au sommet