GameTheory-11-BayesianGames-CSharp

Jeux Bayesiens en forme normale : Information Incomplete et Croyances

Objectifs d’apprentissage

  • Modeliser un jeu bayesien (Harsanyi) : types, croyances a priori, profils d’actions.
  • Calculer un equilibre bayesien de Nash (BNE) en stratégies pures par best-response.
  • Resoudre analytiquement le duopole de Cournot avec couts incertains (système lineaire 2x2).
  • Simuler une enchere au premier prix et verifier le theoreme d’equivalence du revenu.

Prerequis

  • GameTheory-02-NormalForm-CSharp (Nash en information complete).
  • Probabilites : probabilite conditionnelle, esperance.

Duree estimee : 65 minutes

Twin C# de GameTheory-11-BayesianGames-Python (Python + numpy). Complementarite (#3801 Prong B) : les DEUX twins resolvent Cournot analytiquement (le scipy.optimize.fsolve que d’anciennes versions creditaient au twin Python etait un import mort, retire par #10438) ; ce twin C# pousse la demarche jusqu’a exhiber que le determinant du système vaut 6 independamment du prior – l’identite algebrique que la resolution explicite rend visible. Aucun NuGet : BCL .NET seule.

#r "Microsoft.DotNet.Interactive"

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.DotNet.Interactive;

// Helper formatage invariant (la culture FR ne persiste pas entre cellules .NET Interactive)
static string FI(double x, string fmt = "F4") => x.ToString(fmt, CultureInfo.InvariantCulture);

// RNG deterministe pour les simulations d'enchere (reproductibilite)
static Random NewRng(int seed) => new Random(seed);

"Imports OK : System.Linq, Globalization, DotNet.Interactive (BCL seule, 0 NuGet)".Display();
Imports OK : System.Linq, Globalization, DotNet.Interactive (BCL seule, 0 NuGet)

1. Information imparfaite vs information incomplete

1.1 Distinction fondamentale

  • Information imparfaite : un joueur n’observe pas les coups passes (jeux dynamiques, actions simultanees).
  • Information incomplete : un joueur ignore les caractéristiques (couts, valuations, préférences) d’autrui.

1.2 Transformation de Harsanyi

Harsanyi (1967) ramene l’information incomplete a l’information imparfaite en introduisant la Nature :

  1. La Nature tire un profil de types \(t = (t_1, \dots, t_n)\) selon une distribution a priori \(p(t)\) commune.
  2. Chaque joueur \(i\) observe son propre type \(t_i\) (information privee).
  3. Les joueurs choisissent simultanement une action ; le gain depend du profil de types ET du profil d’actions.

Les croyances d’un joueur sur les types d’autrui se deduisent de \(p\) par règle de Bayes :

\[P(t_{-i} \mid t_i) = \frac{p(t_i, t_{-i})}{\sum_{t'_{-i}} p(t_i, t'_{-i})}\]

// Representation generale d'un jeu bayesien (Harsanyi).
// Cle du prior : profil de types joint en chaine "|".

public class BayesianGame
{
    public string Name;
    public string[] Players;
    public Dictionary<string, string[]> Types;    // joueur -> types possibles
    public Dictionary<string, double> Prior;       // cle = string.Join("|", profil de types)
    public Dictionary<string, string[]> Actions;   // joueur -> actions possibles
    public Func<string[], string[], double[]> Payoffs; // (types, actions) -> gains par joueur

    static string Key(params string[] parts) => string.Join("|", parts);

    public BayesianGame(string name, string[] players, Dictionary<string,string[]> types,
        Dictionary<string,double> prior, Dictionary<string,string[]> actions,
        Func<string[],string[],double[]> payoffs)
    {
        Name = name; Players = players; Types = types; Prior = prior; Actions = actions; Payoffs = payoffs;
        double total = prior.Values.Sum();
        if (Math.Abs(total - 1.0) > 1e-6)
            throw new ArgumentException("Le prior doit sommer a 1, obtenu " + FI(total));
    }

    // Produit cartesien de plusieurs ensembles de types
    public static IEnumerable<string[]> Cartesian(string[][] pools)
    {
        var result = new List<string[]> { new string[0] };
        foreach (var pool in pools)
        {
            var next = new List<string[]>();
            foreach (var r in result)
                foreach (var p in pool)
                {
                    var arr = new string[r.Length + 1];
                    Array.Copy(r, arr, r.Length); arr[r.Length] = p;
                    next.Add(arr);
                }
            result = next;
        }
        return result;
    }

    // P(types d'autrui | mon type) par la regle de Bayes
    public double GetConditionalProb(string player, string playerType, string[] otherTypes)
    {
        int pidx = Array.IndexOf(Players, player);
        var full = new List<string>(otherTypes);
        full.Insert(pidx, playerType);
        double num = Prior.GetValueOrDefault(Key(full.ToArray()), 0.0);

        var others = Players.Where(p => p != player).ToArray();
        double den = 0.0;
        foreach (var combo in Cartesian(others.Select(o => Types[o]).ToArray()))
        {
            var f = new List<string>(combo);
            f.Insert(pidx, playerType);
            den += Prior.GetValueOrDefault(Key(f.ToArray()), 0.0);
        }
        return den == 0 ? 0 : num / den;
    }
}

public static void DisplayBayesianGame(BayesianGame g)
{
    var sb = new System.Text.StringBuilder();
    sb.AppendLine("Jeu Bayesien : " + g.Name);
    sb.AppendLine(new string('=', 50));
    sb.AppendLine("Joueurs : " + string.Join(", ", g.Players));
    sb.AppendLine("Types :");
    foreach (var kv in g.Types)
        sb.AppendLine("  " + kv.Key + " -> [" + string.Join(", ", kv.Value) + "]");
    sb.AppendLine("Actions :");
    foreach (var kv in g.Actions)
        sb.AppendLine("  " + kv.Key + " -> [" + string.Join(", ", kv.Value) + "]");
    sb.AppendLine("Distribution a priori :");
    foreach (var kv in g.Prior)
        if (kv.Value > 0) sb.AppendLine("  P" + kv.Key + " = " + FI(kv.Value, "F3"));
    sb.ToString().Display();
}

"Classe BayesianGame definie (Harsanyi, croyances par Bayes)".Display();
Classe BayesianGame definie (Harsanyi, croyances par Bayes)

Lecture de la représentation : le prior joint comme unique source de vérité

Trois choix de modélisation dans cette classe portent toute la suite du notebook :

  • Le prior est stocké sur les profils joints (clé "N|O", "N|F"), pas joueur par joueur. Si la Nature tirait les types indépendamment, on pourrait se contenter de probabilités individuelles ; le format joint couvre sans surcoût le cas général des croyances corrélées — et le calcul conditionnel n’a pas à savoir laquelle des deux situations il traverse.
  • GetConditionalProb applique la règle de Bayes littéralement (§1.2) : numérateur = probabilité du profil complet, dénominateur = somme des probabilités des mondes compatibles avec mon type. Si ce dénominateur vaut 0 — un type que le prior rend impossible — la fonction rend 0 proprement : pas de division par zéro, pas d’exception, une croyance nulle.
  • Le constructeur valide que le prior somme à 1 (tolérance 1e-6, avec le total fautif dans le message d’erreur). C’est un garde-fou placé au bon endroit : une distribution mal saisie est attrapée à la construction, là où sa correction est triviale, plutôt qu’à la première énumération d’équilibre — où elle se traduirait par des croyances silencieusement fausses et des BNE faux sans aucun message d’erreur.

Notez enfin Cartesian : le produit cartésien des ensembles de types est le moteur unique de toutes les énumérations qui suivent — espérances de gain, recherche d’équilibres — réutilisé tel quel par les deux fonctions de la section suivante.

2. Equilibre bayesien de Nash

2.1 Definition

Un profil de stratégies \(s = (s_1, \dots, s_n)\) (ou \(s_i : T_i \to A_i\) mappe le type d’un joueur a une action) est un equilibre bayesien de Nash si, pour chaque joueur \(i\) et chaque type \(t_i\) :

\[s_i(t_i) \in \arg\max_{a_i \in A_i} \; \sum_{t_{-i}} P(t_{-i} \mid t_i) \cdot u_i\big(t_i, t_{-i},\, a_i,\, s_{-i}(t_{-i})\big)\]

2.2 Interpretation

Chaque type joue sa meilleure reponse en esperance, sachant son type et croyant que les autres jouent selon le profil. La verification est purement combinatoire (pas de solveur) : on enumere les deviations alternatives et on exige qu’aucune ne soit strictement profitable.

// Gain espere d'un joueur (type donne, action donnee) face aux strategies pures d'autrui.
public static double ComputeExpectedPayoff(BayesianGame game, string player, string playerType,
    string playerAction, Dictionary<string, Dictionary<string,string>> strategies)
{
    int pidx = Array.IndexOf(game.Players, player);
    var others = game.Players.Where(p => p != player).ToArray();
    double expected = 0.0;
    foreach (var otherTypes in BayesianGame.Cartesian(others.Select(o => game.Types[o]).ToArray()))
    {
        double prob = game.GetConditionalProb(player, playerType, otherTypes);
        if (prob == 0) continue;
        var allTypes = new List<string>(otherTypes);
        allTypes.Insert(pidx, playerType);
        var allActions = new List<string>();
        int oi = 0;
        for (int k = 0; k < game.Players.Length; k++)
        {
            if (k == pidx) allActions.Add(playerAction);
            else { allActions.Add(strategies[game.Players[k]][otherTypes[oi]]); oi++; }
        }
        double[] pay = game.Payoffs(allTypes.ToArray(), allActions.ToArray());
        expected += prob * pay[pidx];
    }
    return expected;
}

// Verifie qu'un profil de strategies pures est un BNE (aucune deviation profitable).
public static bool IsBayesianNashEquilibrium(BayesianGame game,
    Dictionary<string, Dictionary<string,string>> strategies)
{
    foreach (var player in game.Players)
        foreach (var t in game.Types[player])
        {
            string cur = strategies[player][t];
            double uCur = ComputeExpectedPayoff(game, player, t, cur, strategies);
            foreach (var alt in game.Actions[player])
            {
                if (alt == cur) continue;
                double uAlt = ComputeExpectedPayoff(game, player, t, alt, strategies);
                if (uAlt > uCur + 1e-6) return false;
            }
        }
    return true;
}

"Fonctions definies : ComputeExpectedPayoff, IsBayesianNashEquilibrium".Display();
Fonctions definies : ComputeExpectedPayoff, IsBayesianNashEquilibrium

Lecture : une vérification combinatoire, pas un solveur

Les deux fonctions traduisent mot à mot la définition du §2.1 :

  • ComputeExpectedPayoff évalue l’espérance de gain d’un joueur de type donné jouant une action donnée : elle somme sur les types d’autrui pondérés par les croyances conditionnelles — exactement le \(\sum_{t_{-i}} P(t_{-i} \mid t_i) \cdot u_i(\dots)\) de la définition. Les combinaisons de probabilité nulle sont sautées (if (prob == 0) continue) : un monde impossible n’entre pas dans l’espérance.
  • IsBayesianNashEquilibrium est un vérificateur, pas un chercheur : pour chaque joueur, chaque type, elle compare l’action du profil à toutes les déviations alternatives et ne rejette que si une déviation est strictement profitable (tolérance 1e-6, qui protège des artefacts d’arrondi de la sommation flottante). C’est la condition faible de la définition — l’indifférence ne casse pas un équilibre, on le verra au §3.

Le prix de cette simplicité est le coût d’énumération : une action par type et par joueur, donc |A₁|^|T₁| × … candidats. Au §3 cela reste 2 × 2 × 2 = 8 profils à tester — on voit la définition tourner, ce qui est exactement le but pédagogique. Pour un grand espace de types, il faudrait chercher par meilleure réponse itérée plutôt qu’énumérer.

3. Exemple : Bataille des Sexes avec incertitude

J1 n’a pas d’incertitude (type unique N). J2 a deux types possibles : - type O (prefere l’Opera) avec probabilite \(p = 0.7\) - type F (prefere le Foot) avec probabilite \(1-p = 0.3\)

Les gains : si les deux choisissent la même activite, chacun gagne ; sinon (0,0). Mais le type de J2 change quelle activite il prefere. J1 ne sait pas a qui il a affaire : c’est l’essence du jeu bayesien.

On enumere toutes les stratégies pures (J1 : 1 action ; J2 : 1 action par type, soit 2x2 = 4) et on garde celles qui passent le test de meilleure reponse.

// Construction de la Bataille des Sexes bayesienne
double pBos = 0.7;

double[] bosPayoffs(string[] types, string[] actions)
{
    string t2 = types[1];
    string a1 = actions[0], a2 = actions[1];
    if (a1 == a2)
    {
        if (a1 == "O")
            return t2 == "O" ? new[]{2.0, 1.0} : new[]{2.0, 0.0};
        else
            return t2 == "F" ? new[]{1.0, 2.0} : new[]{1.0, 0.0};
    }
    return new[]{0.0, 0.0};
}

var bosGame = new BayesianGame(
    name: "BoS with Uncertainty",
    players: new[]{"J1", "J2"},
    types: new Dictionary<string,string[]>{{"J1",new[]{"N"}},{"J2",new[]{"O","F"}}},
    prior: new Dictionary<string,double>{{"N|O", pBos}, {"N|F", 1-pBos}},
    actions: new Dictionary<string,string[]>{{"J1",new[]{"O","F"}},{"J2",new[]{"O","F"}}},
    payoffs: bosPayoffs
);

DisplayBayesianGame(bosGame);
("Probabilite que J2 prefere Opera : p = " + FI(pBos, "F2")).Display();

// Enumeration des equilibres bayesiens de Nash
var bneResults = new List<string>();
bneResults.Add("Recherche des equilibres bayesiens de Nash");
bneResults.Add(new string('=', 50));
int nBne = 0;
foreach (var a1 in bosGame.Actions["J1"])
  foreach (var a2O in bosGame.Actions["J2"])
    foreach (var a2F in bosGame.Actions["J2"])
    {
        var strat = new Dictionary<string,Dictionary<string,string>>{
            {"J1", new Dictionary<string,string>{{"N", a1}}},
            {"J2", new Dictionary<string,string>{{"O", a2O},{"F", a2F}}}};
        if (IsBayesianNashEquilibrium(bosGame, strat))
        {
            nBne++;
            double u1 = ComputeExpectedPayoff(bosGame, "J1", "N", a1, strat);
            double u2O = ComputeExpectedPayoff(bosGame, "J2", "O", a2O, strat);
            double u2F = ComputeExpectedPayoff(bosGame, "J2", "F", a2F, strat);
            bneResults.Add("");
            bneResults.Add("BNE #" + nBne + ":");
            bneResults.Add("  J1        joue " + a1);
            bneResults.Add("  J2 type O joue " + a2O);
            bneResults.Add("  J2 type F joue " + a2F);
            bneResults.Add("  Gains esperes : J1=" + FI(u1,"F2") + ", J2(O)=" + FI(u2O,"F2") + ", J2(F)=" + FI(u2F,"F2"));
        }
    }
bneResults.Add("");
bneResults.Add("Total : " + nBne + " equilibre(s) bayesien(s) de Nash en strategies pures.");
string.Join("\n", bneResults).Display();
Jeu Bayesien : BoS with Uncertainty
==================================================
Joueurs : J1, J2
Types :
  J1 -> [N]
  J2 -> [O, F]
Actions :
  J1 -> [O, F]
  J2 -> [O, F]
Distribution a priori :
  PN|O = 0.700
  PN|F = 0.300
Probabilite que J2 prefere Opera : p = 0.70
Recherche des equilibres bayesiens de Nash
==================================================

BNE #1:
  J1        joue O
  J2 type O joue O
  J2 type F joue O
  Gains esperes : J1=2.00, J2(O)=1.00, J2(F)=0.00

BNE #2:
  J1        joue O
  J2 type O joue O
  J2 type F joue F
  Gains esperes : J1=1.40, J2(O)=1.00, J2(F)=0.00

BNE #3:
  J1        joue F
  J2 type O joue F
  J2 type F joue F
  Gains esperes : J1=1.00, J2(O)=0.00, J2(F)=2.00

Total : 3 equilibre(s) bayesien(s) de Nash en strategies pures.

Lecture des trois équilibres : pooling et séparateur avant la lettre

L’énumération trouve 3 BNE — lisons-les un par un, car leur structure annonce le vocabulaire du §6 (signaling) :

  • BNE #1 — J2 se fond (pooling) sur O : J1 joue O, et les deux types de J2 jouent O. J1 récolte 2.00 en espérance (match garanti sur O, quel que soit le type en face). Le point remarquable est la ligne J2 type F joue O avec gain 0.00 : le type Foot va quand même à l’Opéra. Pourquoi ne dévie-t-il pas ? Parce que sa déviation (jouer F face à un J1 qui joue O) rapporte aussi 0 — il est indifférent, et l’équilibre n’exige que l’absence de déviation strictement profitable.
  • BNE #2 — J2 se révèle (séparateur) : le type O joue O, le type F joue F. J1, qui ne peut pas deviner, arbitre en espérance : 0.7 × 2 + 0.3 × 0 = 1.40. Vérifiez qu’il ne gagne pas à dévier : jouer F contre ce profil séparateur ne rapporte que 0.3 × 1 = 0.3.
  • BNE #3 — pooling sur F : tout le monde au Foot ; J1 obtient 1.00, le type F est récompensé (2.00), le type O sacrifié (0.00) — indifférent là encore, sa déviation O contre J1=F rapportant 0 aussi.

Deux leçons se dégagent. La multiplicité est structurelle — comme les deux Nash purs de la Bataille des Sexes classique en information complète, trois profils coexistent ici, et J1, qui préférerait le pooling-Opéra (2.00), ne choisit pas l’équilibre. Et la moitié des types sont indifférents à leur sort : c’est précisément cette marge d’indifférence qui laisse autant de profils survivre au test de meilleure réponse.

4. Cournot avec couts incertains

4.1 Le modèle

Duopole avec demande lineaire \(P = a - Q\). Chaque firme \(i\) a un cout marginal \(c_i \in \{c_L, c_H\}\) tire independamment avec \(P(c_L) = \pi\). La firme connait son cout mais ignore celui du rival.

4.2 Stratégie d’equilibre (solution analytique)

Une stratégie est une quantite par type : \(q_L\) si cout bas, \(q_H\) si cout haut. La condition du premier ordre d’une firme de type \(\theta\) donne :

\[a - 2 q_\theta - \mathbb{E}[q_{-i}] - c_\theta = 0, \qquad \mathbb{E}[q_{-i}] = \pi q_L + (1-\pi) q_H\]

En reinjectant dans les deux CPO (types \(L\) et \(H\)) on obtient le système lineaire 2x2 :

\[\begin{pmatrix} 2+\pi & 1-\pi \\ \pi & 3-\pi \end{pmatrix}\begin{pmatrix} q_L \\ q_H \end{pmatrix} = \begin{pmatrix} a-c_L \\ a-c_H \end{pmatrix}\]

Résultat algebraique (Prong B) : le determinant vaut \((2+\pi)(3-\pi) - \pi(1-\pi) = 6\), independamment de \(\pi\). Le twin Python resout le même système analytiquement lui aussi (l’fsolve anciennement credité etait un import mort, retire par #10438) ; ce twin rend l’identite du determinant explicite dans le code et dans la sortie.

\[q_L^* = \tfrac{(a-c_L)(3-\pi) - (1-\pi)(a-c_H)}{6}, \qquad q_H^* = \tfrac{(2+\pi)(a-c_H) - \pi(a-c_L)}{6}\]

// Resolution analytique du Cournot bayesien (systeme lineaire 2x2, determinant = 6)
public static (double qL, double qH) SolveCournotBayesian(double a=100, double cL=10, double cH=30, double probL=0.5)
{
    double pi = probL;
    double a11 = 2 + pi, a12 = 1 - pi;
    double a21 = pi,     a22 = 3 - pi;
    double b1 = a - cL,  b2 = a - cH;
    double det = a11*a22 - a12*a21;   // = 6, independant de pi
    double qL = (b1*a22 - a12*b2) / det;
    double qH = (a11*b2 - b1*a21) / det;
    return (qL, qH);
}

double aC = 100, cL = 10, cH = 30, probL = 0.5;
var (qL, qH) = SolveCournotBayesian(aC, cL, cH, probL);
double Eq = probL*qL + (1-probL)*qH;
double detSys = (2+probL)*(3-probL) - (1-probL)*probL;

var sb = new System.Text.StringBuilder();
sb.AppendLine("Cournot avec couts incertains (resolution analytique)");
sb.AppendLine(new string('=', 54));
sb.AppendLine("Demande : P = " + aC + " - Q    couts : c_L = " + cL + ", c_H = " + cH + "    P(c_L) = " + FI(probL,"F2"));
sb.AppendLine("Determinant du systeme = " + detSys + " (= 6, independant du prior)");
sb.AppendLine();
sb.AppendLine("Solution du BNE (par firme) :");
sb.AppendLine("  q*(c_L) = " + FI(qL));
sb.AppendLine("  q*(c_H) = " + FI(qH));
sb.AppendLine("  E[q du rival] = " + FI(Eq));
sb.AppendLine();
sb.AppendLine("Scenarios possibles :");
sb.AppendLine("  scenario      proba      q1      q2    Prix     pi1     pi2");
sb.AppendLine("  " + new string('-', 56));
double[] probs = {probL*probL, probL*(1-probL), (1-probL)*probL, (1-probL)*(1-probL)};
(double c1,double c2)[] combos = {(cL,cL),(cL,cH),(cH,cL),(cH,cH)};
for (int k = 0; k < 4; k++)
{
    var (c1,c2) = combos[k];
    double q1 = c1==cL?qL:qH, q2 = c2==cL?qL:qH;
    double P = aC - q1 - q2;
    double pi1 = (P-c1)*q1, pi2 = (P-c2)*q2;
    string scen = "  (" + (c1==cL?"c_L":"c_H") + "," + (c2==cL?"c_L":"c_H") + ")";
    sb.AppendLine(scen.PadRight(13) + " " + FI(probs[k],"F2").PadLeft(7) + " "
        + FI(q1).PadLeft(8) + FI(q2).PadLeft(8) + FI(P,"F1").PadLeft(8)
        + FI(pi1,"F1").PadLeft(8) + FI(pi2,"F1").PadLeft(8));
}
sb.ToString().Display();
Cournot avec couts incertains (resolution analytique)
======================================================
Demande : P = 100 - Q    couts : c_L = 10, c_H = 30    P(c_L) = 0.50
Determinant du systeme = 6 (= 6, independant du prior)

Solution du BNE (par firme) :
  q*(c_L) = 31.6667
  q*(c_H) = 21.6667
  E[q du rival] = 26.6667

Scenarios possibles :
  scenario      proba      q1      q2    Prix     pi1     pi2
  --------------------------------------------------------
  (c_L,c_L)      0.25  31.6667 31.6667    36.7   844.4   844.4
  (c_L,c_H)      0.25  31.6667 21.6667    46.7  1161.1   361.1
  (c_H,c_L)      0.25  21.6667 31.6667    46.7   361.1  1161.1
  (c_H,c_H)      0.25  21.6667 21.6667    56.7   577.8   577.8

Lecture : ce que le déterminant 6 garantit

Le résultat central est dans la deuxième ligne de la sortie : Determinant du systeme = 6 — non pas une valeur approchée mais l’entier exact, parce que (2+0.5)(3−0.5) − (1−0.5)(0.5) = 6.25 − 0.25 est exact en arithmétique flottante.

Pourquoi c’est plus qu’une curiosité : dans les formules de Cramer du §4.2, le déterminant divise les deux numérateurs. Tant qu’il ne s’annule pas, le système a une unique solution — un déterminant indépendant de π dit : aucune valeur du prior ne peut faire dégénérer le jeu ; le BNE existe et est unique pour tout π.

Les quantités : q(c_L) = 31.6667 et q(c_H) = 21.6667. Une identité cachée les relie : en soustrayant les deux solutions fermées du §4.2, les termes en π se compensent et il reste \(q_L^* - q_H^* = (c_H - c_L)/2 = 10\) — l’écart de production entre les deux types est la moitié de l’écart des coûts, quel que soit le prior (la vérification en travers de la comparaison suivante y revient).

La table des scénarios se lit ex post : chaque ligne est un monde réalisé après que la Nature a tiré les deux coûts. La firme bas-coût gagne 1161.1 face à une rivale haute-coût (prix 46.7, avance de coût 20) et seulement 844.4 dans le miroir (c_L,c_L) — le prix y est plus bas (36.7) parce que les deux produisent beaucoup. Les poids 0.25 traduisent π = 0.5 indépendant : l’espérance ex ante d’une firme mélange ces quatre mondes.

// Comparaison : information complete vs incomplete
var sb = new System.Text.StringBuilder();
sb.AppendLine("Comparaison : Information complete vs incomplete");
sb.AppendLine(new string('=', 60));
sb.AppendLine();
sb.AppendLine("1. Information complete (Nash classique, q_i = (a - 2 c_i + c_j)/3) :");
foreach (var (c1,c2) in new[]{(cL,cL),(cL,cH),(cH,cH)})
{
    double q1 = (aC - 2*c1 + c2)/3, q2 = (aC - 2*c2 + c1)/3;
    double P = aC - q1 - q2;
    sb.AppendLine("  (c1=" + (c1==cL?"c_L":"c_H") + ", c2=" + (c2==cL?"c_L":"c_H")
        + ") : q1=" + FI(q1,"F2") + ", q2=" + FI(q2,"F2") + ", P=" + FI(P,"F1"));
}
sb.AppendLine();
sb.AppendLine("2. Information incomplete (prob_L = 0.5) :");
sb.AppendLine("  q*(c_L) = " + FI(qL) + ", q*(c_H) = " + FI(qH));
sb.AppendLine();
sb.AppendLine("Observations :");
sb.AppendLine("  - En info incomplete, le type a cout BAS produit PLUS qu'en info complete :");
sb.AppendLine("    q_L^incomplete = " + FI(qL) + " vs q_L^complete(cc_L) = " + FI((aC - 2*cL + cL)/3) + ".");
sb.AppendLine("    (E[q_rival]=26.67 < 30 : rival moins agressif en moyenne -> il produit davantage)");
sb.AppendLine("  - Le type a cout HAUT produit MOINS qu'en info complete :");
sb.AppendLine("    q_H^incomplete = " + FI(qH) + " vs q_H^complete(cc_H) = " + FI((aC - 2*cH + cH)/3) + ".");
sb.AppendLine("    (E[q_rival]=26.67 > 23.33 : rival plus agressif en moyenne -> il produit moins)");
sb.ToString().Display();
Comparaison : Information complete vs incomplete
============================================================

1. Information complete (Nash classique, q_i = (a - 2 c_i + c_j)/3) :
  (c1=c_L, c2=c_L) : q1=30.00, q2=30.00, P=40.0
  (c1=c_L, c2=c_H) : q1=36.67, q2=16.67, P=46.7
  (c1=c_H, c2=c_H) : q1=23.33, q2=23.33, P=53.3

2. Information incomplete (prob_L = 0.5) :
  q*(c_L) = 31.6667, q*(c_H) = 21.6667

Observations :
  - En info incomplete, le type a cout BAS produit PLUS qu'en info complete :
    q_L^incomplete = 31.6667 vs q_L^complete(cc_L) = 30.0000.
    (E[q_rival]=26.67 < 30 : rival moins agressif en moyenne -> il produit davantage)
  - Le type a cout HAUT produit MOINS qu'en info complete :
    q_H^incomplete = 21.6667 vs q_H^complete(cc_H) = 23.3333.
    (E[q_rival]=26.67 > 23.33 : rival plus agressif en moyenne -> il produit moins)

Lecture : l’incertitude sur l’autre amortit sa propre réponse

La sortie superpose les deux régimes, et chaque bloc d’observations se vérifie sur les chiffres :

  • Type bas-coût : 30.00 en information complète face à une rivale c_L, mais 31.6667 en information incomplète. La raison tient dans l’espérance que la firme se fait de sa rivale : E[q_rival] = 26.67, entre les 30.00 d’une rivale c_L et les 23.33 d’une rivale c_H. Une rivale en moyenne moins agressive laisse plus de marché → produire davantage.
  • Type haut-coût : exactement le miroir — 23.33 en complet (face à c_H), 21.6667 en incomplète : la rivale moyenne (26.67) est plus agressive qu’une rivale c_H, le type coûteux se retire davantage.

Lisons maintenant en travers plutôt que type par type. En information complète, les quantités d’équilibme balayent tout l’éventail des mondes : de 16.67 (une firme c_H face à c_L) à 36.67 (une firme c_L face à c_H), soit 20 unités d’écart. En information incomplète, une firme ne conditionne que sur son type : ses deux quantités possibles vivent dans [21.67, 31.67] — 10 unités d’écart, exactement la moitié. Et ce n’est pas un hasard : reportez les solutions fermées du §4.2, la différence se simplifie en \(q_L^* - q_H^* = (c_H - c_L)/2 = 10\), indépendamment du prior — le même genre d’identité que le déterminant 6. Ne sachant pas qui est en face, chaque type joue « au milieu » de ce qu’il jouerait selon le monde réalisé.

5. Encheres a valeurs privees

5.1 Enchere au premier prix

\(n\) encherisseurs, valuations \(v_i \sim U[0,1]\) independantes (i.i.d.). Chacun fait une offre scellee ; le plus offrant paie son offre et gagne l’objet. L’equilibre bayesien symetrique est :

\[b(v) = \frac{n-1}{n}\, v\]

5.2 Theoreme d’equivalence du revenu

Pour des encherisseurs neutres au risque et des valuations i.i.d., tous les formats d’enchere standard (premier prix, second prix/Vickrey, anglais, hollandais) rapportent au vendeur le même revenu espere :

\[\mathbb{E}[R] = \frac{n-1}{n+1}\]

On verifie numeriquement ce résultat en simulant les deux formats.

// Simulation Monte-Carlo de l'enchere au premier prix (b(v) = (n-1)/n * v)
public static (double meanRevenue, double meanSurplus, double efficiency, double theoretical)
    AnalyzeFirstPriceAuction(int n, int nSims, int seed=42)
{
    var rng = NewRng(seed);
    double sumRev = 0, sumSurp = 0; int effCount = 0;
    for (int s = 0; s < nSims; s++)
    {
        var values = new double[n];
        for (int i = 0; i < n; i++) values[i] = rng.NextDouble();
        var bids = new double[n];
        double coef = (double)(n-1)/n;
        for (int i = 0; i < n; i++) bids[i] = coef * values[i];
        int winner = 0;
        for (int i = 1; i < n; i++) if (bids[i] > bids[winner]) winner = i;
        int highest = 0;
        for (int i = 1; i < n; i++) if (values[i] > values[highest]) highest = i;
        sumRev += bids[winner];
        sumSurp += values[winner] - bids[winner];
        if (winner == highest) effCount++;
    }
    return (sumRev/nSims, sumSurp/nSims, (double)effCount/nSims, (double)(n-1)/(n+1));
}

int nBidders = 2, nSim = 20000;
var (rev, surp, eff, theo) = AnalyzeFirstPriceAuction(nBidders, nSim);
double bv2 = (double)(nBidders-1)/nBidders;
var sb = new System.Text.StringBuilder();
sb.AppendLine("Enchere au premier prix avec " + nBidders + " encherisseurs");
sb.AppendLine(new string('=', 50));
sb.AppendLine("Valuations : v_i ~ U[0,1] i.i.d.");
sb.AppendLine("Strategie d'equilibre : b(v) = " + FI(bv2,"F3") + " * v");
sb.AppendLine();
sb.AppendLine("Resultats de simulation (" + nSim + " encheres, seed=42) :");
sb.AppendLine("  Revenue moyen du vendeur : " + FI(rev) + "  (theorie = " + FI(theo) + ")");
sb.AppendLine("  Surplus moyen du gagnant  : " + FI(surp));
sb.AppendLine("  Efficacite (bien au plus valuant) : " + FI(eff*100,"F1") + "%");
sb.ToString().Display();
Enchere au premier prix avec 2 encherisseurs
==================================================
Valuations : v_i ~ U[0,1] i.i.d.
Strategie d'equilibre : b(v) = 0.500 * v

Resultats de simulation (20000 encheres, seed=42) :
  Revenue moyen du vendeur : 0.3336  (theorie = 0.3333)
  Surplus moyen du gagnant  : 0.3336
  Efficacite (bien au plus valuant) : 100.0%

Lecture : la stratégie d’équilibre à l’épreuve de la simulation

Avec n = 2, l’équilibre symétrique prédit b(v) = v/2 — la simulation met 20 000 enchères (seed 42) derrière cette prédiction :

  • Revenu moyen 0.3336 contre théorie 0.3333 : l’écart (≈ 0.0003) est inférieur à l’erreur-type Monte-Carlo de l’estimateur (de l’ordre de 0.0008 sur 20 000 tirages) — un tirage conforme, pas un biais.
  • Surplus du gagnant 0.3336, égal au revenu : pas une coïncidence. À l’équilibre b(v) = v/2, le gagnant paie la moitié de sa valuation : revenu espéré = E[max(v)/2] = 1/3, et surplus espéré = E[max(v) − max(v)/2] = E[max(v)/2] = 1/3. Les deux lignes mesurent la même quantité par deux chemins — une vérification interne gratuite de la simulation.
  • Efficacité 100.0 % : l’offre b(v) = v/2 est strictement croissante dans la valuation, donc l’ordre des offres reproduit l’ordre des valuations — le bien va toujours au plus valuant. C’est une propriété de l’équilibre symétrique du premier prix, pas de l’enchère en général (avec des asymétries entre enchérisseurs, elle peut tomber).
// Theoreme d'equivalence du revenu : comparaison 1er prix vs 2e prix (Vickrey)
var sb = new System.Text.StringBuilder();
sb.AppendLine("Comparaison : Premier prix vs Second prix (Vickrey)");
sb.AppendLine(new string('=', 60));
sb.AppendLine();
sb.AppendLine("  n   1er prix    2e prix   Theorie (n-1)/(n+1)");
sb.AppendLine("  " + new string('-', 44));
int nSimCmp = 20000;
foreach (var n in new[]{2,3,5,10})
{
    var rng = NewRng(42);
    double sum1 = 0, sum2 = 0;
    for (int s = 0; s < nSimCmp; s++)
    {
        var values = new double[n];
        for (int i = 0; i < n; i++) values[i] = rng.NextDouble();
        double maxBid = double.MinValue;
        for (int i = 0; i < n; i++) { double b = (double)(n-1)/n * values[i]; if (b > maxBid) maxBid = b; }
        sum1 += maxBid;
        Array.Sort(values, (x,y) => y.CompareTo(x));
        sum2 += values[1];
    }
    double r1 = sum1/nSimCmp, r2 = sum2/nSimCmp, rt = (double)(n-1)/(n+1);
    sb.AppendLine("  " + n.ToString().PadLeft(2) + "  " + FI(r1).PadLeft(10) + "  " + FI(r2).PadLeft(10) + "  " + FI(rt).PadLeft(10));
}
sb.AppendLine();
sb.AppendLine("Theoreme d'equivalence du revenu : les deux formats donnent le MEME");
sb.AppendLine("revenu espere (encherisseurs neutres au risque, valuations i.i.d.).");
sb.ToString().Display();
Comparaison : Premier prix vs Second prix (Vickrey)
============================================================

  n   1er prix    2e prix   Theorie (n-1)/(n+1)
  --------------------------------------------
   2      0.3336      0.3345      0.3333
   3      0.5002      0.4995      0.5000
   5      0.6666      0.6660      0.6667
  10      0.8177      0.8171      0.8182

Theoreme d'equivalence du revenu : les deux formats donnent le MEME
revenu espere (encherisseurs neutres au risque, valuations i.i.d.).

Lecture : le théorème à l’épreuve des quatre tailles

Le tableau superpose, pour n = 2, 3, 5, 10, le revenu moyen simulé des deux formats et la formule (n−1)/(n+1) :

  • n = 2 : 0.3336 (1er prix) contre 0.3345 (2e prix), théorie 0.3333 — les deux formats tombent à ±0.001 de la prédiction, de part et d’autre : c’est la signature d’un bruit d’échantillonnage, pas d’un biais de format.
  • n = 10 : 0.8177 contre 0.8171, théorie 0.8182 — même accord ; et plus il y a d’enchérisseurs, plus le revenu monte vers 1 (au second prix, le vendeur encaisse la deuxième valuation la plus élevée, qui croît avec n).
  • La comparaison est appariée : dans la boucle de simulation, chaque tirage de valuations sert aux deux formats (même RNG re-seedé à 42 par valeur de n) — à réalisations égales, seul le format diffère, ce qui divise la variance de la comparaison.

Ce que le théorème ne dit pas : les deux formats ne sont pas équivalents pour le surplus des joueurs ni face au risque. Au premier prix, l’enchérisseur porte l’incertitude sur ce qu’il faudra payer (stratégie d’offre à équilibrer) ; au second prix, dire sa vérité est faiblement dominant (exercice 2). L’équivalence porte uniquement sur l’espérance de revenu du vendeur, sous neutralité au risque — hypothèse explicite du §5.2.

6. Introduction aux jeux de signalisation (signaling)

6.1 Structure

Les jeux de signalisation sont des jeux dynamiques bayesiens : un emetteur (type prive) envoie un message \(m\) ; un receveur observe \(m\) et choisit une action. Le message peut reveler ou masquer le type.

6.2 Types d’equilibres

  • Separateurs : chaque type envoie un message distinct (le message revele le type).
  • Melangeants (pooling) : tous les types envoient le même message (le message ne revele rien).

Modèle de Spence (1973)

L’education coute plus cher au type Faible (cout marginal cF = 2) qu’au type Fort (cS = 1). Le salaire propose est w ; un employeur competitif paye w = E[productivite | diplome observe]. A l’equilibre separatateur, le diplome sert de signal credible : seuls les Forts l’acquierent car le cout pour les Faibles l’emporte le gain salarial.

// Modele de Spence : cout net d'acquerir un diplome selon le type
public static (double surplusF, double surplusS) SpenceSurplus(double wageGrad, double wageNoGrad,
    double costF, double costS)
{
    double gain = wageGrad - wageNoGrad;
    return (gain - costF, gain - costS);
}

double wGrad = 2.0, wNo = 1.0;
double cF = 2.0, cS = 1.0;
var (surpF, surpS) = SpenceSurplus(wGrad, wNo, cF, cS);
string fa = surpF < 0 ? "n'acquiere PAS" : "acquiere";
string fo = surpS < 0 ? "n'acquiere PAS" : "acquiere";
var sb = new System.Text.StringBuilder();
sb.AppendLine("Modele de Spence - le diplome comme signal");
sb.AppendLine(new string('=', 50));
sb.AppendLine("Salaire diplome = " + FI(wGrad,"F1") + ", salaire non diplome = " + FI(wNo,"F1"));
sb.AppendLine("Cout education : Faible = " + FI(cF,"F1") + ", Fort = " + FI(cS,"F1"));
sb.AppendLine();
sb.AppendLine("Surplus net du diplome (gain salarial - cout) :");
sb.AppendLine("  Faible : " + FI(wGrad-wNo,"F1") + " - " + FI(cF,"F1") + " = " + FI(surpF,"F2") + " -> " + fa);
sb.AppendLine("  Fort   : " + FI(wGrad-wNo,"F1") + " - " + FI(cS,"F1") + " = " + FI(surpS,"F2") + " -> " + fo);
sb.AppendLine();
if (surpF < 0 && surpS >= 0)
    sb.AppendLine("Equilibre SEPARATEUR credible : le diplome signale le type (seuls les Forts l'acquierent).");
else
    sb.AppendLine("Condition de single-crossing non respectee : pas de separation par le diplome.");
sb.ToString().Display();
Modele de Spence - le diplome comme signal
==================================================
Salaire diplome = 2.0, salaire non diplome = 1.0
Cout education : Faible = 2.0, Fort = 1.0

Surplus net du diplome (gain salarial - cout) :
  Faible : 1.0 - 2.0 = -1.00 -> n'acquiere PAS
  Fort   : 1.0 - 1.0 = 0.00 -> acquiere

Equilibre SEPARATEUR credible : le diplome signale le type (seuls les Forts l'acquierent).

Lecture : l’équilibre séparateur tient à la frontière de l’indifférence

La sortie affiche le calcul du single-crossing :

  • Faible : 1.0 − 2.0 = −1.00 → n’acquière PAS — le diplôme lui coûte plus qu’il ne rapporte : dévier (acheter le diplôme pour se faire passer pour un Fort) détruirait 1.00 d’utilité. Le signal est crédible contre ce type.
  • Fort : 1.0 − 1.0 = 0.00 → acquière — attention à cette ligne : le surplus est exactement nul. Le Fort est à l’indifférence ; le code tranche parce que le test est surpS < 0 (strictement négatif). Avec un gain salarial de 1.1, l’acquisition deviendrait strictement profitable ; avec 0.9, le Fort renoncerait et l’équilibre séparateur s’effondrerait.
  • La condition de séparation se lit donc sur le gain salarial wGrad − wNo : il doit vivre dans l’intervalle [cS, cF) = [1, 2) — au-dessus du coût du Fort (sinon il ne signale pas), en dessous du coût du Faible (sinon le signal est imité et ne dit plus rien).

C’est le single-crossing : c’est l’ordre des coûts (cS < cF), pas leur valeur exacte, qui rend le signal possible. Le modèle de Spence est l’archétype du §6 : le message (diplôme) n’a de valeur d’information que parce qu’il est différentiellement coûteux selon le type.

Exercices

Conformement a la règle C.1, les exercices ci-dessous sont des stubs a completer : ils s’executent sans erreur (rendent null ou affichent un message) et ne bloquent pas le notebook. Indice : chaque exercice est une variante d’un exemple ci-dessus.

Exercice 1 : Enchere All-Pay

Dans une enchere all-pay, tous les encherisseurs paient leur offre (pas seulement le gagnant). Pour \(n=2\) encherisseurs et \(v_i \sim U[0,1]\), la stratégie d’equilibre symetrique est \(b(v) = v^2/2\).

Indice : dans AnalyzeFirstPriceAuction, chaque joueur paie bids[i] (pas seulement bids[winner]). Le revenue moyen devient \(\sum_i \mathbb{E}[b(v_i)]\). Résultat attendu : \(\mathbb{E}[R] = 1/3 \approx 0{,}333\).

// Exercice 1 : Enchere All-Pay (a completer)
// TODO etudiant : simuler une enchere all-pay a n=2 encherisseurs avec b(v) = v^2 / 2.
// Etape 1 : tirer v1, v2 ~ U[0,1] ; Etape 2 : bids = v^2 / 2 ; Etape 3 : revenue = bids[0] + bids[1].
// Resultat attendu (theorie) : E[revenue] = 2 * E[v^2/2] = E[v^2] = 1/3 ~ 0.333.
double? allpayRevenue = null;  // TODO etudiant : affecter le revenue moyen simule
"Exercice 1 (All-Pay) a completer. Revenue theorique attendu ~ 0.333.".Display();
Exercice 1 (All-Pay) a completer. Revenue theorique attendu ~ 0.333.

Exercice 2 : Enchere de Vickrey et revelation sincere

Dans une enchere au second prix (Vickrey), le gagnant paie la deuxieme offre. On veut verifier empiriquement que dire la verite (\(b = v\)) est une stratégie faiblement dominante.

Indice : pour chaque tirage \((v_1, v_2)\), comparer le surplus de J1 quand \(b_1 = v_1\) vs \(b_1 = v_1 \pm \epsilon\).

// Exercice 2 : Enchere de Vickrey (a completer)
// TODO etudiant : verifier que b(v) = v est faiblement dominant au second prix.
// Etape 1 : tirer v1, v2 ~ U[0,1] ; Etape 2 : si v1 > v2, J1 gagne et paie v2 (surplus = v1 - v2).
// Etape 3 : comparer a b1 = v1 + eps et b1 = v1 - eps. Resultat : la verite n'est jamais battue.
double? vickreyAdvantage = null;  // TODO etudiant : difference d'esperance (verite - meilleure deviation)
"Exercice 2 (Vickrey) a completer.".Display();
Exercice 2 (Vickrey) a completer.

Exercice 3 : Sélection adverse (Akerlof, le marche des tacots)

Des voitures de qualite \(q \sim U[0,1]\). L’acheteur valorise \(q\) mais ne l’observe pas ; il offre \(p = \mathbb{E}[q \mid \text{vendu}]\). Le vendeur connait \(q\) et vend si \(p \geq q\). A l’equilibre, seules les voitures \(q \leq p\) sont mises en vente, donc \(p = p/2\), soit \(p = 0\) : sélection adverse.

Indice : resoudre \(p = \mathbb{E}[q \mid q \leq p] = p/2\).

// Exercice 3 : Selection adverse d'Akerlof (a completer)
// TODO etudiant : calculer le prix d'equilibre du marche des tacots.
// Etape 1 : poser E[q | q <= p] = p/2 ; Etape 2 : resoudre p = p/2 ; Etape 3 : conclure.
// Resultat attendu : p* = 0 -> le marche s'effondre (seuls les tacots restent).
double? akerlofPrice = null;  // TODO etudiant : prix d'equilibre
"Exercice 3 (Akerlof) a completer.".Display();
Exercice 3 (Akerlof) a completer.

Exercice 4 : Calcul d’un BNE sur un jeu a 2 types

Soit un jeu bayesien ou J1 a un type unique et J2 a deux types equiprobables. Construire la matrice de gain et utiliser IsBayesianNashEquilibrium pour enumerer tous les BNE.

Indice : reprendre la structure de la cellule BoS mais changer la fonction payoffs (par exemple un jeu Chicken bayesien : type agressif vs prudent).

// Exercice 4 : Enumeration d'un BNE (a completer)
// TODO etudiant : definir un nouveau jeu bayesien (ex. Chicken avec type agressif/prudent)
// et enumerer ses equilibres avec IsBayesianNashEquilibrium.
// Etape 1 : definir chickenPayoffs ; Etape 2 : construire le BayesianGame ; Etape 3 : enumerer.
int? nbBneExo = null;  // TODO etudiant : nombre de BNE trouves
"Exercice 4 (BNE) a completer.".Display();
Exercice 4 (BNE) a completer.

7. Resume

Points cles

  1. Harsanyi ramene l’information incomplete a l’information imparfaite via les types et un prior commun.
  2. Les croyances se deduisent par règle de Bayes ; un BNE exige la meilleure reponse en esperance de chaque type.
  3. Cournot bayesien : le système lineaire a un determinant 6 indépendant du prior (résultat algebraique propre au twin C#).
  4. Equivalence du revenu : tous les formats d’enchere standards rapportent le même revenu espere.
  5. Signaling : a l’equilibre separatateur, un signal couteux (diplome) revele crediblement le type.

Prochaine étape

  • Forme extensive et equilibre séquentiel (perfect Bayesian equilibrium).
  • Mécanismes de revelation (principe de revelation, Vickrey-Clarke-Groves).

References academiques

  • Harsanyi (1967), Games with Incomplete Information Played by Bayesian Players.
  • Spence (1973), Job Market Signaling.
  • Myerson (1981), Optimal Auction Design.
  • Krishna (2009), Auction Theory, Academic Press.

Twin C# dans le cadre du marathon de parite .NET/Python (#4956). Complementarite #3801 Prong B : Cournot analytique des DEUX cotes (l’fsolve anciennement credité au twin Python etait un import mort, retire par #10438) ; le twin C# exhibe l’identite determinant = 6 dans sa sortie.

Retour au sommet