Fumagalli_Motta_Tarantino_2020.Visualizations.Visualize

  1from abc import abstractmethod
  2from typing import Final, Optional
  3import warnings
  4
  5import math
  6import numpy as np
  7import matplotlib.pyplot as plt
  8
  9import Fumagalli_Motta_Tarantino_2020.Models as FMT20
 10
 11
 12class IVisualize:
 13    """
 14    Interface for all visualization classes containing useful methods.
 15
 16    Notes
 17    -----
 18    This module is compatible with python versions starting from 3.9, due to introduction of PEP 585. Therefore, the compatibility
 19    with mybinder.org is not guaranteed (uses python 3.7 at the moment ).
 20    """
 21
 22    COLORS: Final[list[str]] = ["salmon", "gold", "lawngreen", "turquoise", "thistle"]
 23    """Standard colors used in visualizations."""
 24    fontsize = "x-small"
 25    """Default font size for all plots."""
 26
 27    def __init__(
 28        self,
 29        model: FMT20.OptimalMergerPolicy,
 30        ax: Optional[plt.Axes] = None,
 31        default_style=True,
 32        dark_mode=False,
 33        **kwargs,
 34    ):
 35        """
 36        Parameters
 37        ----------
 38        model: Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy
 39            Model to create the visualization from.
 40        ax: Optional[matplotlib.pyplot.Axes]
 41            Axes used for the plot, if not specified, a new set of axes is generated.
 42        default_style: bool
 43            If true, default matplotlib style is used.
 44        dark_mode
 45            If true, dark mode is used.
 46        **kwargs
 47            Arguments for the creation of a new figure.
 48        """
 49        self._set_mode(default_style, dark_mode)
 50        self.model: FMT20.OptimalMergerPolicy = model
 51        self._set_axes(ax, **kwargs)
 52        warnings.filterwarnings("ignore")
 53
 54    def _set_axes(self, ax, **kwargs) -> None:
 55        if ax is None:
 56            self.fig, self.ax = plt.subplots(**kwargs)
 57        else:
 58            self.ax = ax
 59            self.fig = self.ax.get_figure()
 60        self.ax.patch.set_alpha(0)
 61
 62    def _set_mode(self, default_style: bool, dark_mode: bool) -> None:
 63        if dark_mode:
 64            self._set_dark_mode()
 65        else:
 66            self._set_light_mode(default_style)
 67
 68    @staticmethod
 69    def _set_dark_mode() -> None:
 70        plt.style.use("dark_background")
 71
 72    @staticmethod
 73    def _set_light_mode(default_style=False) -> None:
 74        if ("science" in plt.style.available) and not default_style:
 75            plt.style.use("science")
 76        else:
 77            plt.style.use("default")
 78
 79    def set_model(self, model: FMT20.OptimalMergerPolicy) -> None:
 80        """
 81        Change the model for the visualization.
 82
 83        Example
 84        -------
 85        ```python
 86        import Fumagalli_Motta_Tarantino_2020 as FMT20
 87
 88        model_one = FMT20.OptimalMergerPolicy()
 89        model_two = FMT20.OptimalMergerPolicy(development_success=False)
 90        visualizer = FMT20.Overview(model_one)
 91        visualizer.show()
 92        # set the new model
 93        visualizer.set_model(model_two)
 94        # overwrite the previous plot
 95        visualizer.show()
 96        ```
 97
 98        Parameters
 99        ----------
100        model: Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy
101            New model to generate the plots from.
102        """
103        self.model = model
104        self.ax.clear()
105        self._reset_legend()
106        self._set_axes(self.ax)
107
108    def _reset_legend(self) -> None:
109        try:
110            self.ax.get_legend().remove()
111        except AttributeError:
112            pass
113
114    def _set_primary_legend(self, equal_opacity=True) -> None:
115        legend: plt.legend = self.ax.legend(
116            bbox_to_anchor=(1.02, 1), loc="upper left", borderaxespad=0, framealpha=0
117        )
118        if equal_opacity:
119            for entry in legend.legend_handles:
120                entry.set_alpha(1)
121
122    def _set_tight_layout(self, y_spacing: float = None, x_spacing: float = 0) -> None:
123        if y_spacing is not None or x_spacing is not None:
124            self.ax.margins(y=y_spacing, x=x_spacing)
125        self.fig.tight_layout()
126
127    @abstractmethod
128    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
129        """
130        Plots the visual representation for the object.
131
132        Example
133        -------
134        ```
135        import Fumagalli_Motta_Tarantino_2020 as FMT20
136
137        model = FMT20.OptimalMergerPolicy()
138        visualizer = FMT20.MergerPoliciesAssetRange(m)
139        fig, ax = visualizer.plot()
140        # use the figure and axes as you wish, for example:
141        fig.show()
142        ```
143
144        Parameters
145        ----------
146        **kwargs
147            Options for further customization of the plots .
148
149        Returns
150        -------
151        Figure
152            Containing the axes with the plots (use Figure.show() to display).
153        Axes
154            Containing the plots (arrange custom summary).
155        """
156        raise NotImplementedError
157
158    def show(self, **kwargs) -> None:
159        """
160        Shows the visual representation for the object.
161
162        Example
163        -------
164        ```
165        model = Models.OptimalMergerPolicy()
166        visualizer = MergerPoliciesAssetRange(m)
167        visualizer.show()
168        ```
169
170        Parameters
171        ----------
172        **kwargs
173            Same options as Fumagalli_Motta_Tarantino_2020.Visualize.IVisualize.plot.
174        """
175        self.plot(**kwargs)
176        self.fig.show()
177
178    @staticmethod
179    def get_model_label(m: type(FMT20.OptimalMergerPolicy)) -> str:
180        """
181        Returns a label for a given model extending Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy.
182
183        Parameters
184        ----------
185        m: type(FMT20.OptimalMergerPolicy)
186            Type of the model
187        """
188
189        def _check_type(model_to_check, type_to_check):
190            return (
191                isinstance(model_to_check, type_to_check)
192                or model_to_check == type_to_check
193            )
194
195        if _check_type(m, FMT20.OptimalMergerPolicy):
196            return "Optimal Merger Policy"
197        if _check_type(m, FMT20.ProCompetitive):
198            return "Pro-Competitive"
199        if _check_type(m, FMT20.ResourceWaste):
200            return "Resource Waste"
201        if _check_type(m, FMT20.PerfectInformation):
202            return "Perfect Information"
203        if _check_type(m, FMT20.CournotCompetition):
204            return "Cournot Competition"
205        if _check_type(m, FMT20.EquityContract):
206            return "Equity Contract"
207
208    def _get_parameter_legend(self, **kwargs) -> str:
209        """
210        Generates a legend for the parameter values of a Fumagalli_Motta_Tarantino_2020.Models.BaseModel in latex format.
211
212        Returns
213        -------
214        str
215            Containing the legend for the parameter values.
216        """
217        separator_name_value = "="
218        separator_parameters = kwargs.get("separator", " ; ")
219        output_str = ""
220        counter = 2
221        number_parameters_per_line = kwargs.get("parameter_number", 6)
222        for parameter, value in [
223            ("A", self.model.startup_assets),
224            ("B", self.model.private_benefit),
225            ("K", self.model.development_costs),
226            ("p", self.model.success_probability),
227            ("CS^m", self.model.cs_without_innovation),
228            ("\\pi^m_I", self.model.incumbent_profit_without_innovation),
229            ("CS^M", self.model.cs_with_innovation),
230            ("\\pi^M_I", self.model.incumbent_profit_with_innovation),
231            ("CS^d", self.model.cs_duopoly),
232            ("\\pi^d_I", self.model.incumbent_profit_duopoly),
233            ("\\pi^d_S", self.model.startup_profit_duopoly),
234        ]:
235            separator = (
236                ""
237                if counter == 12
238                else (
239                    "\n"
240                    if counter % number_parameters_per_line == 0
241                    else separator_parameters
242                )
243            )
244            output_str += f"${parameter}{separator_name_value}{round(value, ndigits=3)}${separator}"
245            counter += 1
246        return output_str
247
248    @staticmethod
249    def _get_summary_latex(summary: FMT20.Outcome) -> str:
250        """
251        Generates a chronological entry for the legend based on the input model.
252
253        Returns
254        -------
255        str
256            Chronological entry for the legend of the input model.
257        """
258        separator: str = "$\\to$"
259        return (
260            f"{summary.early_bidding_type.abbreviation()}"
261            f"{IVisualize._get_takeover_legend(summary.early_bidding_type, summary.early_takeover)}{separator}"
262            f"{IVisualize._get_development_attempt_legend(summary.development_attempt)}"
263            f"{IVisualize._get_development_outcome_legend(summary.development_attempt, summary.development_outcome)}{separator}"
264            f"{summary.late_bidding_type.abbreviation()}"
265            f"{IVisualize._get_takeover_legend(summary.late_bidding_type, summary.late_takeover)}"
266        )
267
268    @staticmethod
269    def _get_takeover_legend(bid_attempt: FMT20.Takeover, is_takeover: bool) -> str:
270        """
271        Generates a string representation for legend about the takeover (option and approval).
272
273        Parameters
274        ----------
275        bid_attempt: Fumagalli_Motta_Tarantino_2020.FMT20.Takeover
276            Option for takeover chosen by the incumbent.
277        is_takeover: bool
278            If true, the takeover is approved by AA and the start-up.
279
280        Returns
281        -------
282        str
283            String representation for legend about takeover (option and approval).
284        """
285        if bid_attempt is FMT20.Takeover.No:
286            return ""
287        return "$(\\checkmark)$" if is_takeover else "$(\\times)$"
288
289    @staticmethod
290    def _get_development_attempt_legend(is_developing: bool) -> str:
291        """
292        Generates a string representation for legend about the development attempt.
293
294        Parameters
295        ----------
296        is_developing: bool
297            True, if the owner is developing the product (otherwise, the product is shelved).
298
299        Returns
300        -------
301        str
302            String representation for legend about the development attempt.
303        """
304        return "" if is_developing else "$\\emptyset$"
305
306    @staticmethod
307    def _get_development_outcome_legend(
308        is_developing: bool, is_successful: bool
309    ) -> str:
310        """
311        Generates a string representation for legend about the development outcome.
312
313        Parameters
314        ----------
315        is_developing: bool
316            True, if the owner is developing the product (otherwise, the product is shelved).
317        is_successful: bool
318            True, if the development of the product is successful.
319
320        Returns
321        -------
322        str
323            String representation for legend about the development outcome.
324        """
325        if is_developing:
326            return "$\\checkmark$" if is_successful else "$\\times$"
327        return ""
328
329    @staticmethod
330    def _get_symbol_legend() -> str:
331        """
332        Generates a legend for the used abbreviations in the plot legends.
333
334        Returns
335        -------
336        str
337            Containing the legend for the used abbreviations.
338        """
339        return (
340            "${\\bf Merger\\thickspace policies}$:\n"
341            f"{FMT20.MergerPolicies.legend()}\n"
342            "${\\bf Bidding\\thickspace types}$:\n"
343            f"{FMT20.Takeover.legend()}\n"
344            "${\\bf Takeover\\thickspace outcome\\thickspace}$:\n"
345            f"{FMT20.Takeover.Pooling.abbreviation()}|{FMT20.Takeover.Separating.abbreviation()}$(\\checkmark)$: Takeover is approved by the startup and AA\n"
346            f"{FMT20.Takeover.Pooling.abbreviation()}|{FMT20.Takeover.Separating.abbreviation()}$(\\times)$: Takeover is blocked  by AA or not accepted by the startup\n"
347            "${\\bf Development\\thickspace outcome}$:\n"
348            f"$\\emptyset$: Product development was shelved\n"
349            f"$\\checkmark$: Product development was attempted and successful\n"
350            f"$\\times$: Product development was attempted and not successful\n"
351        )
352
353    @staticmethod
354    def _get_payoff_legend(market_situations_only=False) -> str:
355        payoff_str = (
356            "$\\pi_S$: Profit of the start-up\n"
357            "$\\pi_I$: Profit of the incumbent\n"
358            "$CS$: Consumer surplus\n"
359            "$W$: Total welfare\n"
360            if not market_situations_only
361            else ""
362        )
363        return (
364            "${\\bf Market\\thickspace configurations}$\n"
365            f"{payoff_str}"
366            "$m$: Monopoly without the innovation\n"
367            "$M$: Monopoly (innovation in possession of incumbent)\n"
368            "$d$: Duopoly (requires successful development by the start-up)\n"
369        )
370
371    def _get_model_characteristics(
372        self,
373        separator=" ; ",
374        model_parameters=True,
375        model_thresholds=True,
376        thresholds_newline=True,
377        optimal_policy=False,
378        **kwargs,
379    ) -> str:
380        newline = "\n" if thresholds_newline else separator
381        parameter_text = (
382            f"${{\\bf Parameters}}$\n{self._get_parameter_legend(separator=separator, **kwargs)}\n"
383            if model_parameters
384            else ""
385        )
386        threshold_title = kwargs.get(
387            "threshold_title",
388            "Thresholds\\thickspace for\\thickspace the\\thickspace Start-up\\thickspace Assets",
389        )
390        thresholds = (
391            f"${{\\bf {threshold_title}}}$\n"
392            f"{self._get_model_characteristics_thresholds(separator, newline)}"
393            if model_thresholds
394            else ""
395        )
396        optimal = (
397            f"Optimal policy: {self.model.get_optimal_merger_policy().abbreviation()}\n"
398            if optimal_policy
399            else ""
400        )
401        return f"{parameter_text}{thresholds}{optimal}"
402
403    def _get_model_characteristics_thresholds(
404        self, separator: str, newline: str
405    ) -> str:
406        return (
407            f"$F(0) = {self._round_floats(self._get_asset_distribution_value(0))}${separator}"
408            f"$F(K) = {self._round_floats(self._get_asset_distribution_value(self.model.development_costs))}${separator}"
409            f"$F(\\bar{{A}}) = {self._round_floats(self.model.asset_threshold_cdf)}${separator}"
410            f"$F(\\bar{{A}}^T) = {self._round_floats(self.model.asset_threshold_late_takeover_cdf)}${newline}"
411            f"$\\Gamma(\\cdot) = {self._round_floats(self.model.asset_distribution_threshold_welfare)}${separator}"
412            f"$\\Phi(\\cdot) = {self._round_floats(self.model.asset_distribution_threshold_profitable_without_late_takeover)}${separator}"
413            f"$\\Phi'(\\cdot) = {self._round_floats(self.model.asset_distribution_threshold_unprofitable_without_late_takeover)}${separator}"
414            f"$\\Phi^T(\\cdot) = {self._round_floats(self.model.asset_distribution_threshold_with_late_takeover)}${separator}"
415            f"$\\Lambda(\\cdot) = {self._round_floats(self.model.asset_distribution_threshold_shelving_approved)}$\n"
416        )
417
418    def _get_asset_distribution_value(self, value: float) -> float:
419        return self.model.asset_distribution.cumulative(
420            value, **self.model.asset_distribution_kwargs
421        )
422
423    def _get_inverse_asset_distribution_value(self, value: float) -> float:
424        return self.model.asset_distribution.inverse_cumulative(
425            value, **self.model.asset_distribution_kwargs
426        )
427
428    @staticmethod
429    def _round_floats(value: float, digits=3) -> str:
430        return f"{value:.{digits}f}"
431
432    def _get_model_characteristics_ax(self, ax: plt.Axes, **kwargs) -> None:
433        ax.set_title(kwargs.get("title", "Model Characteristics"))
434        ax.axis("off")
435        model_characteristics = self._get_model_characteristics(**kwargs)
436        text_to_annotate = (
437            f"{model_characteristics}"
438            f"{self._get_payoff_legend(market_situations_only=True)}"
439            f"{self._get_symbol_legend()}"
440        )
441        ax.annotate(
442            text_to_annotate,
443            xy=(0.5, 1),
444            xytext=(0, 0),
445            textcoords="offset points",
446            horizontalalignment="center",
447            verticalalignment="top",
448            fontsize=kwargs.get("fontsize", IVisualize.fontsize),
449        )
450
451
452class Timeline(IVisualize):
453    """
454    Visualizes the timeline of events for a specific model.
455    """
456
457    def __init__(self, model: FMT20.OptimalMergerPolicy, **kwargs):
458        super(Timeline, self).__init__(model, **kwargs)
459        self.high_stem = 0.6
460        self.low_stem = 0.3
461        self.stem_levels = [
462            -self.high_stem,
463            self.high_stem,
464            self.low_stem,
465            -self.high_stem,
466            self.high_stem,
467            -self.high_stem,
468            -self.low_stem,
469            self.high_stem,
470        ]
471        self._x_ticks = list(range(len(self.stem_levels)))
472
473    def _get_stem_labels(self) -> list[str]:
474        """
475        Generates the label and points in time of the events in the model.
476
477        Returns
478        -------
479        (list[str], list[str])
480            List containing label for the events and list containing the points in time of the events.
481        """
482        (
483            earl_takeover_attempt,
484            early_takeover,
485            late_takeover_attempt,
486            late_takeover,
487        ) = self._get_takeover_labels()
488        return [
489            "Competition authority\nestablishes "
490            + self._policy_label()
491            + "\nmerger policy",
492            earl_takeover_attempt,
493            early_takeover,
494            self._development_label(),
495            self._success_str(),
496            late_takeover_attempt,
497            late_takeover,
498            self._get_payoff_label(),
499        ]
500
501    def _get_payoff_label(self):
502        label = "Payoffs\n"
503        if self.model.is_early_takeover or self.model.is_late_takeover:
504            if self.model.is_development_successful:
505                return label + "($CS^M$, $\\pi^M_I$)"
506            return label + "($CS^m$, $\\pi^m_I$)"
507        return label + "($CS^d$, $\\pi^d_I$, $\\pi^d_S$)"
508
509    def _get_takeover_labels(self) -> list[str, str, str, str]:
510        if self.model.is_early_takeover:
511            late_takeover_attempt = "Start-up already\nacquired"
512            late_takeover = ""
513            self.stem_levels[5] = -self.low_stem
514            self.stem_levels[6] = 0
515        elif self.model.merger_policy in [
516            FMT20.MergerPolicies.Strict,
517            FMT20.MergerPolicies.Intermediate_late_takeover_prohibited,
518        ]:
519            late_takeover_attempt = "Competition authority\nblocks late\ntakeovers"
520            late_takeover = ""
521            self.stem_levels[5] = -self.low_stem
522            self.stem_levels[6] = 0
523        else:
524            late_takeover_attempt = self._takeover_attempt_label(
525                self.model.late_bidding_type
526            )
527            late_takeover = self._takeover_label(
528                self.model.late_bidding_type, self.model.is_late_takeover
529            )
530
531        return [
532            self._takeover_attempt_label(self.model.early_bidding_type),
533            self._takeover_label(
534                self.model.early_bidding_type, self.model.is_early_takeover
535            ),
536            late_takeover_attempt,
537            late_takeover,
538        ]
539
540    @staticmethod
541    def _get_x_labels() -> list[str]:
542        return [
543            "t=0",
544            "t=1a",
545            "t=1b",
546            "t=1c",
547            "t=1d",
548            "t=2a",
549            "t=2b",
550            "t=3",
551        ]
552
553    @staticmethod
554    def _takeover_attempt_label(takeover: FMT20.Takeover) -> str:
555        """
556        Generate label for takeover event.
557
558        Parameters
559        ----------
560        takeover: Fumagalli_Motta_Tarantino_2020.FMT20.Takeover
561            Option for takeover chosen by the incumbent.
562
563        Returns
564        -------
565        str
566            Label for takeover event.
567        """
568        return str(takeover) + "\nby incumbent"
569
570    def _policy_label(self) -> str:
571        """
572        Generate label for establishing of merger policy event.
573
574        Returns
575        -------
576        str
577            Label for establishing of merger policy event.
578        """
579        policy_str = str(self.model.merger_policy).lower()
580        if "intermediate" in policy_str:
581            return policy_str.replace("intermediate", "intermediate\n")
582        return policy_str
583
584    @staticmethod
585    def _takeover_label(
586        takeover_attempt: FMT20.Takeover, is_takeover_accepted: bool
587    ) -> str:
588        """
589        Generates a label about the takeover event (option and approval).
590
591        Parameters
592        ----------
593        takeover_attempt: FMT20.Takeover
594            Type of the bid by the incumbent.
595        is_takeover_accepted: bool
596            If true, the takeover is approved by AA and the start-up.
597
598        Returns
599        -------
600        str
601            Label about the takeover event (option and approval).
602        """
603        is_takeover_attempt = takeover_attempt is not FMT20.Takeover.No
604        if is_takeover_attempt and is_takeover_accepted:
605            return "Takeover\napproved"
606        if is_takeover_attempt and not is_takeover_accepted:
607            return "Takeover rejected\nby start-up"
608        return "No takeover\noccurs"
609
610    def _development_label(self) -> str:
611        """
612        Generates a label about the development event (attempt and shelving).
613
614        Returns
615        -------
616        str
617            Label about the development event (attempt and shelving).
618        """
619        if self.model.is_early_takeover:
620            return (
621                "Incumbent\n"
622                + ("develops" if self.model.is_owner_investing else "shelves")
623                + " product"
624                + "\n(killer acquisition)"
625                if self.model.is_killer_acquisition()
626                else ""
627            )
628        return (
629            "Start-up"
630            + ("" if self.model.is_owner_investing else " does not")
631            + "\nobtain"
632            + ("s" if self.model.is_owner_investing else "")
633            + " enough\nfinancial assets"
634        )
635
636    def _success_str(self) -> str:
637        """
638        Generates a label about the development outcome event.
639
640        Returns
641        -------
642        str
643            Label about the development outcome event.
644        """
645        if self.model.is_owner_investing:
646            if self.model.is_development_successful:
647                return "Development is\nsuccessful"
648            return "Development is\nnot successful"
649        return "Development was\nnot attempted."
650
651    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
652        """
653        Plots the visual representation for the object.
654
655        Example
656        -------
657        ```
658        import Fumagalli_Motta_Tarantino_2020 as FMT20
659
660        model = FMT20.OptimalMergerPolicy()
661        visualizer = FMT20.Timeline(m)
662        fig, ax = visualizer.plot()
663        # use the figure and axes as you wish, for example:
664        fig.show()
665        ```
666
667        Parameters
668        ----------
669        **kwargs
670            Options for further customization of the plots.
671            - title(str): Title for timeline<br>
672            - parameters(bool): If true, a legend containing the parameters is shown.
673            - x_offset(int): Moves the text at the stems horizontally.
674
675        Returns
676        -------
677        Figure
678            Containing the axes with the plots (use Figure.show() to display).
679        Axes
680            Containing the plots (arrange custom summary).
681        """
682        self.ax.set(title=kwargs.get("title", "Timeline"))
683        self._set_parameter_legend(kwargs.get("parameters", True))
684        self._draw_timeline(**kwargs)
685        self._set_x_axis()
686        self._set_y_axis()
687        self._set_tight_layout(y_spacing=0.45, x_spacing=0.02)
688        return self.fig, self.ax
689
690    def _draw_timeline(self, **kwargs):
691        self._annotate_stems(kwargs.get("x_offset", 0))
692        self._draw_vertical_stems()
693        self._draw_baseline()
694
695    def _annotate_stems(
696        self,
697        x_offset: int,
698    ) -> None:
699        for d, l, r in zip(self._x_ticks, self.stem_levels, self._get_stem_labels()):
700            self.ax.annotate(
701                str(r),
702                xy=(d, l),
703                xytext=(x_offset, np.sign(l) * 8),
704                textcoords="offset points",
705                horizontalalignment="center",
706                verticalalignment="bottom" if l > 0 else "top",
707                fontsize=IVisualize.fontsize,
708            )
709
710    def _draw_vertical_stems(self) -> None:
711        self.ax.vlines(
712            self._x_ticks, 0, self.stem_levels, color="lightgray", linewidths=1
713        )
714
715    def _draw_baseline(self) -> None:
716        self.ax.plot(
717            self._x_ticks,
718            np.zeros_like(self._x_ticks),
719            "-o",
720            color="k",
721            markerfacecolor="w",
722        )
723
724    def _set_x_axis(self) -> None:
725        self.ax.set_xticks(self._x_ticks)
726        self.ax.set_xticklabels(self._get_x_labels())
727        self.ax.xaxis.set_ticks_position("bottom")
728
729    def _set_y_axis(self) -> None:
730        self.ax.yaxis.set_visible(False)
731        self.ax.spines[["left", "top", "right"]].set_visible(False)
732
733    def _set_parameter_legend(self, show_parameters: bool) -> None:
734        x_coordinate = math.fsum(self._x_ticks) / len(self._x_ticks)
735        if show_parameters:
736            self.ax.annotate(
737                self._get_parameter_legend(),
738                xy=(x_coordinate, self.high_stem * 1.8),
739                horizontalalignment="center",
740                verticalalignment="top",
741                fontsize=IVisualize.fontsize,
742            )
743
744
745class Payoffs(IVisualize):
746    """
747    Visualizes the payoffs for a specific model.
748    """
749
750    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
751        """
752        Plots the visual representation for the object.
753
754        Example
755        -------
756        ```
757        import Fumagalli_Motta_Tarantino_2020 as FMT20
758
759        model = FMT20.OptimalMergerPolicy()
760        visualizer = FMT20.Payoffs(m)
761        fig, ax = visualizer.plot()
762        # use the figure and axes as you wish, for example:
763        fig.show()
764        ```
765
766        Parameters
767        ----------
768        **kwargs
769            Options for further customization of the plots.
770            - title(str): Title for plot<br>
771            - legend(bool): If true, a secondary legend is shown.<br>
772            - width(float): Width of the bars.<br>
773            - spacing(float): Spacing between the bars.<br>
774            - max_opacity(float): Opacity of the optimal payoffs.<br>
775            - min_opacity(float): Opacity of the not optimal payoffs.<br>
776
777        Returns
778        -------
779        Figure
780            Containing the axes with the plots (use Figure.show() to display).
781        Axes
782            Containing the plots (arrange custom summary).
783        """
784        payoffs: dict[str, float] = self._get_payoffs()
785        bar_width = kwargs.get("width", 0.35)
786        spacing = kwargs.get("spacing", 0.05)
787        self._plot_payoffs_bars(payoffs, bar_width, spacing, **kwargs)
788        self.ax.set_title(
789            kwargs.get("title", "Payoffs for different Market Configurations")
790        )
791        self._set_primary_legend()
792        self._set_secondary_legend(bar_width, kwargs.get("legend", True))
793        self._set_tight_layout(x_spacing=spacing)
794
795    def _set_secondary_legend(self, bar_width: float, show_legend: bool) -> None:
796        if show_legend:
797            self.ax.annotate(
798                self._get_payoff_legend(),
799                xy=(-bar_width, 0),
800                xytext=(0, -30),
801                textcoords="offset points",
802                horizontalalignment="left",
803                verticalalignment="top",
804                fontsize=IVisualize.fontsize,
805            )
806
807    def _plot_payoffs_bars(
808        self, payoffs: dict[str, float], bar_width: float, spacing: float, **kwargs
809    ) -> None:
810        """
811        Plots the bars representing the payoffs for different market configurations of different stakeholders on the specified axis.
812
813        Parameters
814        ----------
815        axis matplotlib.axes.Axes
816            To plot the bars on.
817        bar_width: float
818            Width of a bar in the plot.
819        spacing: float
820            Spacing between the bars on the plot.
821        **kwargs
822            Optional key word arguments for the payoff plot.<br>
823            - max_opacity(float): Opacity of the optimal payoffs.<br>
824            - min_opacity(float): Opacity of the not optimal payoffs.<br>
825        """
826        max_values: list[int] = self._set_max_values(list(payoffs.values()))
827        for number_bar, (label, height) in enumerate(payoffs.items()):
828            x_coordinate: float = self._get_x_coordinate(bar_width, number_bar, spacing)
829            self._set_x_label(label, x_coordinate)
830            if number_bar > 3:
831                label = "__nolegend__"
832            else:
833                label = self._set_payoff_label(label)
834            self.ax.bar(
835                x=x_coordinate,
836                width=bar_width,
837                height=height,
838                label=label,
839                color=self._get_color(number_bar),
840                alpha=(
841                    kwargs.get("max_opacity", 1)
842                    if number_bar in max_values
843                    else kwargs.get("min_opacity", 0.5)
844                ),
845            )
846        self._set_x_ticks()
847
848    def _set_x_label(self, label: str, x_coordinate: float) -> None:
849        self.ax.annotate(
850            label,
851            xy=(x_coordinate, 0),
852            xytext=(0, -15),
853            textcoords="offset points",
854            horizontalalignment="center",
855            verticalalignment="bottom",
856            fontsize=IVisualize.fontsize,
857        )
858
859    def _set_x_ticks(self) -> None:
860        self.ax.tick_params(
861            axis="x",
862            which="both",
863            bottom=False,
864            top=False,
865            labelbottom=False,
866        )
867
868    @staticmethod
869    def _set_payoff_label(label) -> str:
870        payoff_type = label[:-3]
871        if "CS" in payoff_type:
872            return "Consumer Surplus"
873        if "W" in payoff_type:
874            return "Total Welfare"
875        if "I" in payoff_type:
876            return "Profit Incumbent ($\\pi_I$)"
877        return "Profit Start-up ($\\pi_S$)"
878
879    @staticmethod
880    def _set_max_values(payoffs: list[float]) -> list[int]:
881        return [
882            Payoffs._get_max_index(0, payoffs),
883            Payoffs._get_max_index(1, payoffs),
884            Payoffs._get_max_index(2, payoffs),
885            Payoffs._get_max_index(3, payoffs),
886        ]
887
888    @staticmethod
889    def _get_max_index(offset_index: int, payoffs: list[float]) -> int:
890        values: list[float] = payoffs[offset_index::4]
891        max_value: float = max(values)
892        group_index: int = values.index(max_value)
893        return group_index * 4 + offset_index
894
895    @staticmethod
896    def _get_x_coordinate(bar_width: float, number_bar: int, spacing: float) -> float:
897        group_spacing: int = (math.trunc(number_bar / 4) % 4) * 8
898        return spacing * (number_bar + 1 + group_spacing) + bar_width * number_bar
899
900    @staticmethod
901    def _get_color(number_bar: int, reverse_cycle=True) -> str:
902        color_id = number_bar % 4
903        color_id = len(IVisualize.COLORS) - color_id - 1 if reverse_cycle else color_id
904        return IVisualize.COLORS[color_id]
905
906    def _get_payoffs(self) -> dict[str, float]:
907        return {
908            "$\\pi_S^m$": 0,
909            "$\\pi_I^m$": self.model.incumbent_profit_without_innovation,
910            "$CS^m$": self.model.cs_without_innovation,
911            "$W^m$": self.model.w_without_innovation,
912            "$\\pi^M_S$": 0,
913            "$\\pi^M_I$": self.model.incumbent_profit_with_innovation,
914            "$CS^M$": self.model.cs_with_innovation,
915            "$W^M$": self.model.w_with_innovation,
916            "$\\pi^d_S$": self.model.startup_profit_duopoly,
917            "$\\pi^d_I$": self.model.incumbent_profit_duopoly,
918            "$CS^d$": self.model.cs_duopoly,
919            "$W^d$": self.model.w_duopoly,
920        }
class IVisualize:
 13class IVisualize:
 14    """
 15    Interface for all visualization classes containing useful methods.
 16
 17    Notes
 18    -----
 19    This module is compatible with python versions starting from 3.9, due to introduction of PEP 585. Therefore, the compatibility
 20    with mybinder.org is not guaranteed (uses python 3.7 at the moment ).
 21    """
 22
 23    COLORS: Final[list[str]] = ["salmon", "gold", "lawngreen", "turquoise", "thistle"]
 24    """Standard colors used in visualizations."""
 25    fontsize = "x-small"
 26    """Default font size for all plots."""
 27
 28    def __init__(
 29        self,
 30        model: FMT20.OptimalMergerPolicy,
 31        ax: Optional[plt.Axes] = None,
 32        default_style=True,
 33        dark_mode=False,
 34        **kwargs,
 35    ):
 36        """
 37        Parameters
 38        ----------
 39        model: Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy
 40            Model to create the visualization from.
 41        ax: Optional[matplotlib.pyplot.Axes]
 42            Axes used for the plot, if not specified, a new set of axes is generated.
 43        default_style: bool
 44            If true, default matplotlib style is used.
 45        dark_mode
 46            If true, dark mode is used.
 47        **kwargs
 48            Arguments for the creation of a new figure.
 49        """
 50        self._set_mode(default_style, dark_mode)
 51        self.model: FMT20.OptimalMergerPolicy = model
 52        self._set_axes(ax, **kwargs)
 53        warnings.filterwarnings("ignore")
 54
 55    def _set_axes(self, ax, **kwargs) -> None:
 56        if ax is None:
 57            self.fig, self.ax = plt.subplots(**kwargs)
 58        else:
 59            self.ax = ax
 60            self.fig = self.ax.get_figure()
 61        self.ax.patch.set_alpha(0)
 62
 63    def _set_mode(self, default_style: bool, dark_mode: bool) -> None:
 64        if dark_mode:
 65            self._set_dark_mode()
 66        else:
 67            self._set_light_mode(default_style)
 68
 69    @staticmethod
 70    def _set_dark_mode() -> None:
 71        plt.style.use("dark_background")
 72
 73    @staticmethod
 74    def _set_light_mode(default_style=False) -> None:
 75        if ("science" in plt.style.available) and not default_style:
 76            plt.style.use("science")
 77        else:
 78            plt.style.use("default")
 79
 80    def set_model(self, model: FMT20.OptimalMergerPolicy) -> None:
 81        """
 82        Change the model for the visualization.
 83
 84        Example
 85        -------
 86        ```python
 87        import Fumagalli_Motta_Tarantino_2020 as FMT20
 88
 89        model_one = FMT20.OptimalMergerPolicy()
 90        model_two = FMT20.OptimalMergerPolicy(development_success=False)
 91        visualizer = FMT20.Overview(model_one)
 92        visualizer.show()
 93        # set the new model
 94        visualizer.set_model(model_two)
 95        # overwrite the previous plot
 96        visualizer.show()
 97        ```
 98
 99        Parameters
100        ----------
101        model: Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy
102            New model to generate the plots from.
103        """
104        self.model = model
105        self.ax.clear()
106        self._reset_legend()
107        self._set_axes(self.ax)
108
109    def _reset_legend(self) -> None:
110        try:
111            self.ax.get_legend().remove()
112        except AttributeError:
113            pass
114
115    def _set_primary_legend(self, equal_opacity=True) -> None:
116        legend: plt.legend = self.ax.legend(
117            bbox_to_anchor=(1.02, 1), loc="upper left", borderaxespad=0, framealpha=0
118        )
119        if equal_opacity:
120            for entry in legend.legend_handles:
121                entry.set_alpha(1)
122
123    def _set_tight_layout(self, y_spacing: float = None, x_spacing: float = 0) -> None:
124        if y_spacing is not None or x_spacing is not None:
125            self.ax.margins(y=y_spacing, x=x_spacing)
126        self.fig.tight_layout()
127
128    @abstractmethod
129    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
130        """
131        Plots the visual representation for the object.
132
133        Example
134        -------
135        ```
136        import Fumagalli_Motta_Tarantino_2020 as FMT20
137
138        model = FMT20.OptimalMergerPolicy()
139        visualizer = FMT20.MergerPoliciesAssetRange(m)
140        fig, ax = visualizer.plot()
141        # use the figure and axes as you wish, for example:
142        fig.show()
143        ```
144
145        Parameters
146        ----------
147        **kwargs
148            Options for further customization of the plots .
149
150        Returns
151        -------
152        Figure
153            Containing the axes with the plots (use Figure.show() to display).
154        Axes
155            Containing the plots (arrange custom summary).
156        """
157        raise NotImplementedError
158
159    def show(self, **kwargs) -> None:
160        """
161        Shows the visual representation for the object.
162
163        Example
164        -------
165        ```
166        model = Models.OptimalMergerPolicy()
167        visualizer = MergerPoliciesAssetRange(m)
168        visualizer.show()
169        ```
170
171        Parameters
172        ----------
173        **kwargs
174            Same options as Fumagalli_Motta_Tarantino_2020.Visualize.IVisualize.plot.
175        """
176        self.plot(**kwargs)
177        self.fig.show()
178
179    @staticmethod
180    def get_model_label(m: type(FMT20.OptimalMergerPolicy)) -> str:
181        """
182        Returns a label for a given model extending Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy.
183
184        Parameters
185        ----------
186        m: type(FMT20.OptimalMergerPolicy)
187            Type of the model
188        """
189
190        def _check_type(model_to_check, type_to_check):
191            return (
192                isinstance(model_to_check, type_to_check)
193                or model_to_check == type_to_check
194            )
195
196        if _check_type(m, FMT20.OptimalMergerPolicy):
197            return "Optimal Merger Policy"
198        if _check_type(m, FMT20.ProCompetitive):
199            return "Pro-Competitive"
200        if _check_type(m, FMT20.ResourceWaste):
201            return "Resource Waste"
202        if _check_type(m, FMT20.PerfectInformation):
203            return "Perfect Information"
204        if _check_type(m, FMT20.CournotCompetition):
205            return "Cournot Competition"
206        if _check_type(m, FMT20.EquityContract):
207            return "Equity Contract"
208
209    def _get_parameter_legend(self, **kwargs) -> str:
210        """
211        Generates a legend for the parameter values of a Fumagalli_Motta_Tarantino_2020.Models.BaseModel in latex format.
212
213        Returns
214        -------
215        str
216            Containing the legend for the parameter values.
217        """
218        separator_name_value = "="
219        separator_parameters = kwargs.get("separator", " ; ")
220        output_str = ""
221        counter = 2
222        number_parameters_per_line = kwargs.get("parameter_number", 6)
223        for parameter, value in [
224            ("A", self.model.startup_assets),
225            ("B", self.model.private_benefit),
226            ("K", self.model.development_costs),
227            ("p", self.model.success_probability),
228            ("CS^m", self.model.cs_without_innovation),
229            ("\\pi^m_I", self.model.incumbent_profit_without_innovation),
230            ("CS^M", self.model.cs_with_innovation),
231            ("\\pi^M_I", self.model.incumbent_profit_with_innovation),
232            ("CS^d", self.model.cs_duopoly),
233            ("\\pi^d_I", self.model.incumbent_profit_duopoly),
234            ("\\pi^d_S", self.model.startup_profit_duopoly),
235        ]:
236            separator = (
237                ""
238                if counter == 12
239                else (
240                    "\n"
241                    if counter % number_parameters_per_line == 0
242                    else separator_parameters
243                )
244            )
245            output_str += f"${parameter}{separator_name_value}{round(value, ndigits=3)}${separator}"
246            counter += 1
247        return output_str
248
249    @staticmethod
250    def _get_summary_latex(summary: FMT20.Outcome) -> str:
251        """
252        Generates a chronological entry for the legend based on the input model.
253
254        Returns
255        -------
256        str
257            Chronological entry for the legend of the input model.
258        """
259        separator: str = "$\\to$"
260        return (
261            f"{summary.early_bidding_type.abbreviation()}"
262            f"{IVisualize._get_takeover_legend(summary.early_bidding_type, summary.early_takeover)}{separator}"
263            f"{IVisualize._get_development_attempt_legend(summary.development_attempt)}"
264            f"{IVisualize._get_development_outcome_legend(summary.development_attempt, summary.development_outcome)}{separator}"
265            f"{summary.late_bidding_type.abbreviation()}"
266            f"{IVisualize._get_takeover_legend(summary.late_bidding_type, summary.late_takeover)}"
267        )
268
269    @staticmethod
270    def _get_takeover_legend(bid_attempt: FMT20.Takeover, is_takeover: bool) -> str:
271        """
272        Generates a string representation for legend about the takeover (option and approval).
273
274        Parameters
275        ----------
276        bid_attempt: Fumagalli_Motta_Tarantino_2020.FMT20.Takeover
277            Option for takeover chosen by the incumbent.
278        is_takeover: bool
279            If true, the takeover is approved by AA and the start-up.
280
281        Returns
282        -------
283        str
284            String representation for legend about takeover (option and approval).
285        """
286        if bid_attempt is FMT20.Takeover.No:
287            return ""
288        return "$(\\checkmark)$" if is_takeover else "$(\\times)$"
289
290    @staticmethod
291    def _get_development_attempt_legend(is_developing: bool) -> str:
292        """
293        Generates a string representation for legend about the development attempt.
294
295        Parameters
296        ----------
297        is_developing: bool
298            True, if the owner is developing the product (otherwise, the product is shelved).
299
300        Returns
301        -------
302        str
303            String representation for legend about the development attempt.
304        """
305        return "" if is_developing else "$\\emptyset$"
306
307    @staticmethod
308    def _get_development_outcome_legend(
309        is_developing: bool, is_successful: bool
310    ) -> str:
311        """
312        Generates a string representation for legend about the development outcome.
313
314        Parameters
315        ----------
316        is_developing: bool
317            True, if the owner is developing the product (otherwise, the product is shelved).
318        is_successful: bool
319            True, if the development of the product is successful.
320
321        Returns
322        -------
323        str
324            String representation for legend about the development outcome.
325        """
326        if is_developing:
327            return "$\\checkmark$" if is_successful else "$\\times$"
328        return ""
329
330    @staticmethod
331    def _get_symbol_legend() -> str:
332        """
333        Generates a legend for the used abbreviations in the plot legends.
334
335        Returns
336        -------
337        str
338            Containing the legend for the used abbreviations.
339        """
340        return (
341            "${\\bf Merger\\thickspace policies}$:\n"
342            f"{FMT20.MergerPolicies.legend()}\n"
343            "${\\bf Bidding\\thickspace types}$:\n"
344            f"{FMT20.Takeover.legend()}\n"
345            "${\\bf Takeover\\thickspace outcome\\thickspace}$:\n"
346            f"{FMT20.Takeover.Pooling.abbreviation()}|{FMT20.Takeover.Separating.abbreviation()}$(\\checkmark)$: Takeover is approved by the startup and AA\n"
347            f"{FMT20.Takeover.Pooling.abbreviation()}|{FMT20.Takeover.Separating.abbreviation()}$(\\times)$: Takeover is blocked  by AA or not accepted by the startup\n"
348            "${\\bf Development\\thickspace outcome}$:\n"
349            f"$\\emptyset$: Product development was shelved\n"
350            f"$\\checkmark$: Product development was attempted and successful\n"
351            f"$\\times$: Product development was attempted and not successful\n"
352        )
353
354    @staticmethod
355    def _get_payoff_legend(market_situations_only=False) -> str:
356        payoff_str = (
357            "$\\pi_S$: Profit of the start-up\n"
358            "$\\pi_I$: Profit of the incumbent\n"
359            "$CS$: Consumer surplus\n"
360            "$W$: Total welfare\n"
361            if not market_situations_only
362            else ""
363        )
364        return (
365            "${\\bf Market\\thickspace configurations}$\n"
366            f"{payoff_str}"
367            "$m$: Monopoly without the innovation\n"
368            "$M$: Monopoly (innovation in possession of incumbent)\n"
369            "$d$: Duopoly (requires successful development by the start-up)\n"
370        )
371
372    def _get_model_characteristics(
373        self,
374        separator=" ; ",
375        model_parameters=True,
376        model_thresholds=True,
377        thresholds_newline=True,
378        optimal_policy=False,
379        **kwargs,
380    ) -> str:
381        newline = "\n" if thresholds_newline else separator
382        parameter_text = (
383            f"${{\\bf Parameters}}$\n{self._get_parameter_legend(separator=separator, **kwargs)}\n"
384            if model_parameters
385            else ""
386        )
387        threshold_title = kwargs.get(
388            "threshold_title",
389            "Thresholds\\thickspace for\\thickspace the\\thickspace Start-up\\thickspace Assets",
390        )
391        thresholds = (
392            f"${{\\bf {threshold_title}}}$\n"
393            f"{self._get_model_characteristics_thresholds(separator, newline)}"
394            if model_thresholds
395            else ""
396        )
397        optimal = (
398            f"Optimal policy: {self.model.get_optimal_merger_policy().abbreviation()}\n"
399            if optimal_policy
400            else ""
401        )
402        return f"{parameter_text}{thresholds}{optimal}"
403
404    def _get_model_characteristics_thresholds(
405        self, separator: str, newline: str
406    ) -> str:
407        return (
408            f"$F(0) = {self._round_floats(self._get_asset_distribution_value(0))}${separator}"
409            f"$F(K) = {self._round_floats(self._get_asset_distribution_value(self.model.development_costs))}${separator}"
410            f"$F(\\bar{{A}}) = {self._round_floats(self.model.asset_threshold_cdf)}${separator}"
411            f"$F(\\bar{{A}}^T) = {self._round_floats(self.model.asset_threshold_late_takeover_cdf)}${newline}"
412            f"$\\Gamma(\\cdot) = {self._round_floats(self.model.asset_distribution_threshold_welfare)}${separator}"
413            f"$\\Phi(\\cdot) = {self._round_floats(self.model.asset_distribution_threshold_profitable_without_late_takeover)}${separator}"
414            f"$\\Phi'(\\cdot) = {self._round_floats(self.model.asset_distribution_threshold_unprofitable_without_late_takeover)}${separator}"
415            f"$\\Phi^T(\\cdot) = {self._round_floats(self.model.asset_distribution_threshold_with_late_takeover)}${separator}"
416            f"$\\Lambda(\\cdot) = {self._round_floats(self.model.asset_distribution_threshold_shelving_approved)}$\n"
417        )
418
419    def _get_asset_distribution_value(self, value: float) -> float:
420        return self.model.asset_distribution.cumulative(
421            value, **self.model.asset_distribution_kwargs
422        )
423
424    def _get_inverse_asset_distribution_value(self, value: float) -> float:
425        return self.model.asset_distribution.inverse_cumulative(
426            value, **self.model.asset_distribution_kwargs
427        )
428
429    @staticmethod
430    def _round_floats(value: float, digits=3) -> str:
431        return f"{value:.{digits}f}"
432
433    def _get_model_characteristics_ax(self, ax: plt.Axes, **kwargs) -> None:
434        ax.set_title(kwargs.get("title", "Model Characteristics"))
435        ax.axis("off")
436        model_characteristics = self._get_model_characteristics(**kwargs)
437        text_to_annotate = (
438            f"{model_characteristics}"
439            f"{self._get_payoff_legend(market_situations_only=True)}"
440            f"{self._get_symbol_legend()}"
441        )
442        ax.annotate(
443            text_to_annotate,
444            xy=(0.5, 1),
445            xytext=(0, 0),
446            textcoords="offset points",
447            horizontalalignment="center",
448            verticalalignment="top",
449            fontsize=kwargs.get("fontsize", IVisualize.fontsize),
450        )

Interface for all visualization classes containing useful methods.

Notes

This module is compatible with python versions starting from 3.9, due to introduction of PEP 585. Therefore, the compatibility with mybinder.org is not guaranteed (uses python 3.7 at the moment ).

IVisualize( model: Fumagalli_Motta_Tarantino_2020.Models.Base.OptimalMergerPolicy, ax: Optional[matplotlib.axes._axes.Axes] = None, default_style=True, dark_mode=False, **kwargs)
28    def __init__(
29        self,
30        model: FMT20.OptimalMergerPolicy,
31        ax: Optional[plt.Axes] = None,
32        default_style=True,
33        dark_mode=False,
34        **kwargs,
35    ):
36        """
37        Parameters
38        ----------
39        model: Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy
40            Model to create the visualization from.
41        ax: Optional[matplotlib.pyplot.Axes]
42            Axes used for the plot, if not specified, a new set of axes is generated.
43        default_style: bool
44            If true, default matplotlib style is used.
45        dark_mode
46            If true, dark mode is used.
47        **kwargs
48            Arguments for the creation of a new figure.
49        """
50        self._set_mode(default_style, dark_mode)
51        self.model: FMT20.OptimalMergerPolicy = model
52        self._set_axes(ax, **kwargs)
53        warnings.filterwarnings("ignore")
Parameters
  • model (Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy): Model to create the visualization from.
  • ax (Optional[matplotlib.pyplot.Axes]): Axes used for the plot, if not specified, a new set of axes is generated.
  • default_style (bool): If true, default matplotlib style is used.
  • dark_mode: If true, dark mode is used.
  • **kwargs: Arguments for the creation of a new figure.
COLORS: Final[list[str]] = ['salmon', 'gold', 'lawngreen', 'turquoise', 'thistle']

Standard colors used in visualizations.

fontsize = 'x-small'

Default font size for all plots.

def set_model( self, model: Fumagalli_Motta_Tarantino_2020.Models.Base.OptimalMergerPolicy) -> None:
 80    def set_model(self, model: FMT20.OptimalMergerPolicy) -> None:
 81        """
 82        Change the model for the visualization.
 83
 84        Example
 85        -------
 86        ```python
 87        import Fumagalli_Motta_Tarantino_2020 as FMT20
 88
 89        model_one = FMT20.OptimalMergerPolicy()
 90        model_two = FMT20.OptimalMergerPolicy(development_success=False)
 91        visualizer = FMT20.Overview(model_one)
 92        visualizer.show()
 93        # set the new model
 94        visualizer.set_model(model_two)
 95        # overwrite the previous plot
 96        visualizer.show()
 97        ```
 98
 99        Parameters
100        ----------
101        model: Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy
102            New model to generate the plots from.
103        """
104        self.model = model
105        self.ax.clear()
106        self._reset_legend()
107        self._set_axes(self.ax)

Change the model for the visualization.

Example
import Fumagalli_Motta_Tarantino_2020 as FMT20

model_one = FMT20.OptimalMergerPolicy()
model_two = FMT20.OptimalMergerPolicy(development_success=False)
visualizer = FMT20.Overview(model_one)
visualizer.show()
# set the new model
visualizer.set_model(model_two)
# overwrite the previous plot
visualizer.show()
Parameters
  • model (Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy): New model to generate the plots from.
@abstractmethod
def plot( self, **kwargs) -> (<class 'matplotlib.figure.Figure'>, <class 'matplotlib.axes._axes.Axes'>):
128    @abstractmethod
129    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
130        """
131        Plots the visual representation for the object.
132
133        Example
134        -------
135        ```
136        import Fumagalli_Motta_Tarantino_2020 as FMT20
137
138        model = FMT20.OptimalMergerPolicy()
139        visualizer = FMT20.MergerPoliciesAssetRange(m)
140        fig, ax = visualizer.plot()
141        # use the figure and axes as you wish, for example:
142        fig.show()
143        ```
144
145        Parameters
146        ----------
147        **kwargs
148            Options for further customization of the plots .
149
150        Returns
151        -------
152        Figure
153            Containing the axes with the plots (use Figure.show() to display).
154        Axes
155            Containing the plots (arrange custom summary).
156        """
157        raise NotImplementedError

Plots the visual representation for the object.

Example
import Fumagalli_Motta_Tarantino_2020 as FMT20

model = FMT20.OptimalMergerPolicy()
visualizer = FMT20.MergerPoliciesAssetRange(m)
fig, ax = visualizer.plot()
# use the figure and axes as you wish, for example:
fig.show()
Parameters
  • **kwargs: Options for further customization of the plots .
Returns
  • Figure: Containing the axes with the plots (use Figure.show() to display).
  • Axes: Containing the plots (arrange custom summary).
def show(self, **kwargs) -> None:
159    def show(self, **kwargs) -> None:
160        """
161        Shows the visual representation for the object.
162
163        Example
164        -------
165        ```
166        model = Models.OptimalMergerPolicy()
167        visualizer = MergerPoliciesAssetRange(m)
168        visualizer.show()
169        ```
170
171        Parameters
172        ----------
173        **kwargs
174            Same options as Fumagalli_Motta_Tarantino_2020.Visualize.IVisualize.plot.
175        """
176        self.plot(**kwargs)
177        self.fig.show()

Shows the visual representation for the object.

Example
model = Models.OptimalMergerPolicy()
visualizer = MergerPoliciesAssetRange(m)
visualizer.show()
Parameters
@staticmethod
def get_model_label(m: type) -> str:
179    @staticmethod
180    def get_model_label(m: type(FMT20.OptimalMergerPolicy)) -> str:
181        """
182        Returns a label for a given model extending Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy.
183
184        Parameters
185        ----------
186        m: type(FMT20.OptimalMergerPolicy)
187            Type of the model
188        """
189
190        def _check_type(model_to_check, type_to_check):
191            return (
192                isinstance(model_to_check, type_to_check)
193                or model_to_check == type_to_check
194            )
195
196        if _check_type(m, FMT20.OptimalMergerPolicy):
197            return "Optimal Merger Policy"
198        if _check_type(m, FMT20.ProCompetitive):
199            return "Pro-Competitive"
200        if _check_type(m, FMT20.ResourceWaste):
201            return "Resource Waste"
202        if _check_type(m, FMT20.PerfectInformation):
203            return "Perfect Information"
204        if _check_type(m, FMT20.CournotCompetition):
205            return "Cournot Competition"
206        if _check_type(m, FMT20.EquityContract):
207            return "Equity Contract"

Returns a label for a given model extending Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy.

Parameters
  • m (type(FMT20.OptimalMergerPolicy)): Type of the model
class Timeline(IVisualize):
453class Timeline(IVisualize):
454    """
455    Visualizes the timeline of events for a specific model.
456    """
457
458    def __init__(self, model: FMT20.OptimalMergerPolicy, **kwargs):
459        super(Timeline, self).__init__(model, **kwargs)
460        self.high_stem = 0.6
461        self.low_stem = 0.3
462        self.stem_levels = [
463            -self.high_stem,
464            self.high_stem,
465            self.low_stem,
466            -self.high_stem,
467            self.high_stem,
468            -self.high_stem,
469            -self.low_stem,
470            self.high_stem,
471        ]
472        self._x_ticks = list(range(len(self.stem_levels)))
473
474    def _get_stem_labels(self) -> list[str]:
475        """
476        Generates the label and points in time of the events in the model.
477
478        Returns
479        -------
480        (list[str], list[str])
481            List containing label for the events and list containing the points in time of the events.
482        """
483        (
484            earl_takeover_attempt,
485            early_takeover,
486            late_takeover_attempt,
487            late_takeover,
488        ) = self._get_takeover_labels()
489        return [
490            "Competition authority\nestablishes "
491            + self._policy_label()
492            + "\nmerger policy",
493            earl_takeover_attempt,
494            early_takeover,
495            self._development_label(),
496            self._success_str(),
497            late_takeover_attempt,
498            late_takeover,
499            self._get_payoff_label(),
500        ]
501
502    def _get_payoff_label(self):
503        label = "Payoffs\n"
504        if self.model.is_early_takeover or self.model.is_late_takeover:
505            if self.model.is_development_successful:
506                return label + "($CS^M$, $\\pi^M_I$)"
507            return label + "($CS^m$, $\\pi^m_I$)"
508        return label + "($CS^d$, $\\pi^d_I$, $\\pi^d_S$)"
509
510    def _get_takeover_labels(self) -> list[str, str, str, str]:
511        if self.model.is_early_takeover:
512            late_takeover_attempt = "Start-up already\nacquired"
513            late_takeover = ""
514            self.stem_levels[5] = -self.low_stem
515            self.stem_levels[6] = 0
516        elif self.model.merger_policy in [
517            FMT20.MergerPolicies.Strict,
518            FMT20.MergerPolicies.Intermediate_late_takeover_prohibited,
519        ]:
520            late_takeover_attempt = "Competition authority\nblocks late\ntakeovers"
521            late_takeover = ""
522            self.stem_levels[5] = -self.low_stem
523            self.stem_levels[6] = 0
524        else:
525            late_takeover_attempt = self._takeover_attempt_label(
526                self.model.late_bidding_type
527            )
528            late_takeover = self._takeover_label(
529                self.model.late_bidding_type, self.model.is_late_takeover
530            )
531
532        return [
533            self._takeover_attempt_label(self.model.early_bidding_type),
534            self._takeover_label(
535                self.model.early_bidding_type, self.model.is_early_takeover
536            ),
537            late_takeover_attempt,
538            late_takeover,
539        ]
540
541    @staticmethod
542    def _get_x_labels() -> list[str]:
543        return [
544            "t=0",
545            "t=1a",
546            "t=1b",
547            "t=1c",
548            "t=1d",
549            "t=2a",
550            "t=2b",
551            "t=3",
552        ]
553
554    @staticmethod
555    def _takeover_attempt_label(takeover: FMT20.Takeover) -> str:
556        """
557        Generate label for takeover event.
558
559        Parameters
560        ----------
561        takeover: Fumagalli_Motta_Tarantino_2020.FMT20.Takeover
562            Option for takeover chosen by the incumbent.
563
564        Returns
565        -------
566        str
567            Label for takeover event.
568        """
569        return str(takeover) + "\nby incumbent"
570
571    def _policy_label(self) -> str:
572        """
573        Generate label for establishing of merger policy event.
574
575        Returns
576        -------
577        str
578            Label for establishing of merger policy event.
579        """
580        policy_str = str(self.model.merger_policy).lower()
581        if "intermediate" in policy_str:
582            return policy_str.replace("intermediate", "intermediate\n")
583        return policy_str
584
585    @staticmethod
586    def _takeover_label(
587        takeover_attempt: FMT20.Takeover, is_takeover_accepted: bool
588    ) -> str:
589        """
590        Generates a label about the takeover event (option and approval).
591
592        Parameters
593        ----------
594        takeover_attempt: FMT20.Takeover
595            Type of the bid by the incumbent.
596        is_takeover_accepted: bool
597            If true, the takeover is approved by AA and the start-up.
598
599        Returns
600        -------
601        str
602            Label about the takeover event (option and approval).
603        """
604        is_takeover_attempt = takeover_attempt is not FMT20.Takeover.No
605        if is_takeover_attempt and is_takeover_accepted:
606            return "Takeover\napproved"
607        if is_takeover_attempt and not is_takeover_accepted:
608            return "Takeover rejected\nby start-up"
609        return "No takeover\noccurs"
610
611    def _development_label(self) -> str:
612        """
613        Generates a label about the development event (attempt and shelving).
614
615        Returns
616        -------
617        str
618            Label about the development event (attempt and shelving).
619        """
620        if self.model.is_early_takeover:
621            return (
622                "Incumbent\n"
623                + ("develops" if self.model.is_owner_investing else "shelves")
624                + " product"
625                + "\n(killer acquisition)"
626                if self.model.is_killer_acquisition()
627                else ""
628            )
629        return (
630            "Start-up"
631            + ("" if self.model.is_owner_investing else " does not")
632            + "\nobtain"
633            + ("s" if self.model.is_owner_investing else "")
634            + " enough\nfinancial assets"
635        )
636
637    def _success_str(self) -> str:
638        """
639        Generates a label about the development outcome event.
640
641        Returns
642        -------
643        str
644            Label about the development outcome event.
645        """
646        if self.model.is_owner_investing:
647            if self.model.is_development_successful:
648                return "Development is\nsuccessful"
649            return "Development is\nnot successful"
650        return "Development was\nnot attempted."
651
652    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
653        """
654        Plots the visual representation for the object.
655
656        Example
657        -------
658        ```
659        import Fumagalli_Motta_Tarantino_2020 as FMT20
660
661        model = FMT20.OptimalMergerPolicy()
662        visualizer = FMT20.Timeline(m)
663        fig, ax = visualizer.plot()
664        # use the figure and axes as you wish, for example:
665        fig.show()
666        ```
667
668        Parameters
669        ----------
670        **kwargs
671            Options for further customization of the plots.
672            - title(str): Title for timeline<br>
673            - parameters(bool): If true, a legend containing the parameters is shown.
674            - x_offset(int): Moves the text at the stems horizontally.
675
676        Returns
677        -------
678        Figure
679            Containing the axes with the plots (use Figure.show() to display).
680        Axes
681            Containing the plots (arrange custom summary).
682        """
683        self.ax.set(title=kwargs.get("title", "Timeline"))
684        self._set_parameter_legend(kwargs.get("parameters", True))
685        self._draw_timeline(**kwargs)
686        self._set_x_axis()
687        self._set_y_axis()
688        self._set_tight_layout(y_spacing=0.45, x_spacing=0.02)
689        return self.fig, self.ax
690
691    def _draw_timeline(self, **kwargs):
692        self._annotate_stems(kwargs.get("x_offset", 0))
693        self._draw_vertical_stems()
694        self._draw_baseline()
695
696    def _annotate_stems(
697        self,
698        x_offset: int,
699    ) -> None:
700        for d, l, r in zip(self._x_ticks, self.stem_levels, self._get_stem_labels()):
701            self.ax.annotate(
702                str(r),
703                xy=(d, l),
704                xytext=(x_offset, np.sign(l) * 8),
705                textcoords="offset points",
706                horizontalalignment="center",
707                verticalalignment="bottom" if l > 0 else "top",
708                fontsize=IVisualize.fontsize,
709            )
710
711    def _draw_vertical_stems(self) -> None:
712        self.ax.vlines(
713            self._x_ticks, 0, self.stem_levels, color="lightgray", linewidths=1
714        )
715
716    def _draw_baseline(self) -> None:
717        self.ax.plot(
718            self._x_ticks,
719            np.zeros_like(self._x_ticks),
720            "-o",
721            color="k",
722            markerfacecolor="w",
723        )
724
725    def _set_x_axis(self) -> None:
726        self.ax.set_xticks(self._x_ticks)
727        self.ax.set_xticklabels(self._get_x_labels())
728        self.ax.xaxis.set_ticks_position("bottom")
729
730    def _set_y_axis(self) -> None:
731        self.ax.yaxis.set_visible(False)
732        self.ax.spines[["left", "top", "right"]].set_visible(False)
733
734    def _set_parameter_legend(self, show_parameters: bool) -> None:
735        x_coordinate = math.fsum(self._x_ticks) / len(self._x_ticks)
736        if show_parameters:
737            self.ax.annotate(
738                self._get_parameter_legend(),
739                xy=(x_coordinate, self.high_stem * 1.8),
740                horizontalalignment="center",
741                verticalalignment="top",
742                fontsize=IVisualize.fontsize,
743            )

Visualizes the timeline of events for a specific model.

Timeline( model: Fumagalli_Motta_Tarantino_2020.Models.Base.OptimalMergerPolicy, **kwargs)
458    def __init__(self, model: FMT20.OptimalMergerPolicy, **kwargs):
459        super(Timeline, self).__init__(model, **kwargs)
460        self.high_stem = 0.6
461        self.low_stem = 0.3
462        self.stem_levels = [
463            -self.high_stem,
464            self.high_stem,
465            self.low_stem,
466            -self.high_stem,
467            self.high_stem,
468            -self.high_stem,
469            -self.low_stem,
470            self.high_stem,
471        ]
472        self._x_ticks = list(range(len(self.stem_levels)))
Parameters
  • model (Fumagalli_Motta_Tarantino_2020.Models.OptimalMergerPolicy): Model to create the visualization from.
  • ax (Optional[matplotlib.pyplot.Axes]): Axes used for the plot, if not specified, a new set of axes is generated.
  • default_style (bool): If true, default matplotlib style is used.
  • dark_mode: If true, dark mode is used.
  • **kwargs: Arguments for the creation of a new figure.
high_stem
low_stem
stem_levels
def plot( self, **kwargs) -> (<class 'matplotlib.figure.Figure'>, <class 'matplotlib.axes._axes.Axes'>):
652    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
653        """
654        Plots the visual representation for the object.
655
656        Example
657        -------
658        ```
659        import Fumagalli_Motta_Tarantino_2020 as FMT20
660
661        model = FMT20.OptimalMergerPolicy()
662        visualizer = FMT20.Timeline(m)
663        fig, ax = visualizer.plot()
664        # use the figure and axes as you wish, for example:
665        fig.show()
666        ```
667
668        Parameters
669        ----------
670        **kwargs
671            Options for further customization of the plots.
672            - title(str): Title for timeline<br>
673            - parameters(bool): If true, a legend containing the parameters is shown.
674            - x_offset(int): Moves the text at the stems horizontally.
675
676        Returns
677        -------
678        Figure
679            Containing the axes with the plots (use Figure.show() to display).
680        Axes
681            Containing the plots (arrange custom summary).
682        """
683        self.ax.set(title=kwargs.get("title", "Timeline"))
684        self._set_parameter_legend(kwargs.get("parameters", True))
685        self._draw_timeline(**kwargs)
686        self._set_x_axis()
687        self._set_y_axis()
688        self._set_tight_layout(y_spacing=0.45, x_spacing=0.02)
689        return self.fig, self.ax

Plots the visual representation for the object.

Example
import Fumagalli_Motta_Tarantino_2020 as FMT20

model = FMT20.OptimalMergerPolicy()
visualizer = FMT20.Timeline(m)
fig, ax = visualizer.plot()
# use the figure and axes as you wish, for example:
fig.show()
Parameters
  • **kwargs: Options for further customization of the plots.
    • title(str): Title for timeline
    • parameters(bool): If true, a legend containing the parameters is shown.
    • x_offset(int): Moves the text at the stems horizontally.
Returns
  • Figure: Containing the axes with the plots (use Figure.show() to display).
  • Axes: Containing the plots (arrange custom summary).
class Payoffs(IVisualize):
746class Payoffs(IVisualize):
747    """
748    Visualizes the payoffs for a specific model.
749    """
750
751    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
752        """
753        Plots the visual representation for the object.
754
755        Example
756        -------
757        ```
758        import Fumagalli_Motta_Tarantino_2020 as FMT20
759
760        model = FMT20.OptimalMergerPolicy()
761        visualizer = FMT20.Payoffs(m)
762        fig, ax = visualizer.plot()
763        # use the figure and axes as you wish, for example:
764        fig.show()
765        ```
766
767        Parameters
768        ----------
769        **kwargs
770            Options for further customization of the plots.
771            - title(str): Title for plot<br>
772            - legend(bool): If true, a secondary legend is shown.<br>
773            - width(float): Width of the bars.<br>
774            - spacing(float): Spacing between the bars.<br>
775            - max_opacity(float): Opacity of the optimal payoffs.<br>
776            - min_opacity(float): Opacity of the not optimal payoffs.<br>
777
778        Returns
779        -------
780        Figure
781            Containing the axes with the plots (use Figure.show() to display).
782        Axes
783            Containing the plots (arrange custom summary).
784        """
785        payoffs: dict[str, float] = self._get_payoffs()
786        bar_width = kwargs.get("width", 0.35)
787        spacing = kwargs.get("spacing", 0.05)
788        self._plot_payoffs_bars(payoffs, bar_width, spacing, **kwargs)
789        self.ax.set_title(
790            kwargs.get("title", "Payoffs for different Market Configurations")
791        )
792        self._set_primary_legend()
793        self._set_secondary_legend(bar_width, kwargs.get("legend", True))
794        self._set_tight_layout(x_spacing=spacing)
795
796    def _set_secondary_legend(self, bar_width: float, show_legend: bool) -> None:
797        if show_legend:
798            self.ax.annotate(
799                self._get_payoff_legend(),
800                xy=(-bar_width, 0),
801                xytext=(0, -30),
802                textcoords="offset points",
803                horizontalalignment="left",
804                verticalalignment="top",
805                fontsize=IVisualize.fontsize,
806            )
807
808    def _plot_payoffs_bars(
809        self, payoffs: dict[str, float], bar_width: float, spacing: float, **kwargs
810    ) -> None:
811        """
812        Plots the bars representing the payoffs for different market configurations of different stakeholders on the specified axis.
813
814        Parameters
815        ----------
816        axis matplotlib.axes.Axes
817            To plot the bars on.
818        bar_width: float
819            Width of a bar in the plot.
820        spacing: float
821            Spacing between the bars on the plot.
822        **kwargs
823            Optional key word arguments for the payoff plot.<br>
824            - max_opacity(float): Opacity of the optimal payoffs.<br>
825            - min_opacity(float): Opacity of the not optimal payoffs.<br>
826        """
827        max_values: list[int] = self._set_max_values(list(payoffs.values()))
828        for number_bar, (label, height) in enumerate(payoffs.items()):
829            x_coordinate: float = self._get_x_coordinate(bar_width, number_bar, spacing)
830            self._set_x_label(label, x_coordinate)
831            if number_bar > 3:
832                label = "__nolegend__"
833            else:
834                label = self._set_payoff_label(label)
835            self.ax.bar(
836                x=x_coordinate,
837                width=bar_width,
838                height=height,
839                label=label,
840                color=self._get_color(number_bar),
841                alpha=(
842                    kwargs.get("max_opacity", 1)
843                    if number_bar in max_values
844                    else kwargs.get("min_opacity", 0.5)
845                ),
846            )
847        self._set_x_ticks()
848
849    def _set_x_label(self, label: str, x_coordinate: float) -> None:
850        self.ax.annotate(
851            label,
852            xy=(x_coordinate, 0),
853            xytext=(0, -15),
854            textcoords="offset points",
855            horizontalalignment="center",
856            verticalalignment="bottom",
857            fontsize=IVisualize.fontsize,
858        )
859
860    def _set_x_ticks(self) -> None:
861        self.ax.tick_params(
862            axis="x",
863            which="both",
864            bottom=False,
865            top=False,
866            labelbottom=False,
867        )
868
869    @staticmethod
870    def _set_payoff_label(label) -> str:
871        payoff_type = label[:-3]
872        if "CS" in payoff_type:
873            return "Consumer Surplus"
874        if "W" in payoff_type:
875            return "Total Welfare"
876        if "I" in payoff_type:
877            return "Profit Incumbent ($\\pi_I$)"
878        return "Profit Start-up ($\\pi_S$)"
879
880    @staticmethod
881    def _set_max_values(payoffs: list[float]) -> list[int]:
882        return [
883            Payoffs._get_max_index(0, payoffs),
884            Payoffs._get_max_index(1, payoffs),
885            Payoffs._get_max_index(2, payoffs),
886            Payoffs._get_max_index(3, payoffs),
887        ]
888
889    @staticmethod
890    def _get_max_index(offset_index: int, payoffs: list[float]) -> int:
891        values: list[float] = payoffs[offset_index::4]
892        max_value: float = max(values)
893        group_index: int = values.index(max_value)
894        return group_index * 4 + offset_index
895
896    @staticmethod
897    def _get_x_coordinate(bar_width: float, number_bar: int, spacing: float) -> float:
898        group_spacing: int = (math.trunc(number_bar / 4) % 4) * 8
899        return spacing * (number_bar + 1 + group_spacing) + bar_width * number_bar
900
901    @staticmethod
902    def _get_color(number_bar: int, reverse_cycle=True) -> str:
903        color_id = number_bar % 4
904        color_id = len(IVisualize.COLORS) - color_id - 1 if reverse_cycle else color_id
905        return IVisualize.COLORS[color_id]
906
907    def _get_payoffs(self) -> dict[str, float]:
908        return {
909            "$\\pi_S^m$": 0,
910            "$\\pi_I^m$": self.model.incumbent_profit_without_innovation,
911            "$CS^m$": self.model.cs_without_innovation,
912            "$W^m$": self.model.w_without_innovation,
913            "$\\pi^M_S$": 0,
914            "$\\pi^M_I$": self.model.incumbent_profit_with_innovation,
915            "$CS^M$": self.model.cs_with_innovation,
916            "$W^M$": self.model.w_with_innovation,
917            "$\\pi^d_S$": self.model.startup_profit_duopoly,
918            "$\\pi^d_I$": self.model.incumbent_profit_duopoly,
919            "$CS^d$": self.model.cs_duopoly,
920            "$W^d$": self.model.w_duopoly,
921        }

Visualizes the payoffs for a specific model.

def plot( self, **kwargs) -> (<class 'matplotlib.figure.Figure'>, <class 'matplotlib.axes._axes.Axes'>):
751    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
752        """
753        Plots the visual representation for the object.
754
755        Example
756        -------
757        ```
758        import Fumagalli_Motta_Tarantino_2020 as FMT20
759
760        model = FMT20.OptimalMergerPolicy()
761        visualizer = FMT20.Payoffs(m)
762        fig, ax = visualizer.plot()
763        # use the figure and axes as you wish, for example:
764        fig.show()
765        ```
766
767        Parameters
768        ----------
769        **kwargs
770            Options for further customization of the plots.
771            - title(str): Title for plot<br>
772            - legend(bool): If true, a secondary legend is shown.<br>
773            - width(float): Width of the bars.<br>
774            - spacing(float): Spacing between the bars.<br>
775            - max_opacity(float): Opacity of the optimal payoffs.<br>
776            - min_opacity(float): Opacity of the not optimal payoffs.<br>
777
778        Returns
779        -------
780        Figure
781            Containing the axes with the plots (use Figure.show() to display).
782        Axes
783            Containing the plots (arrange custom summary).
784        """
785        payoffs: dict[str, float] = self._get_payoffs()
786        bar_width = kwargs.get("width", 0.35)
787        spacing = kwargs.get("spacing", 0.05)
788        self._plot_payoffs_bars(payoffs, bar_width, spacing, **kwargs)
789        self.ax.set_title(
790            kwargs.get("title", "Payoffs for different Market Configurations")
791        )
792        self._set_primary_legend()
793        self._set_secondary_legend(bar_width, kwargs.get("legend", True))
794        self._set_tight_layout(x_spacing=spacing)

Plots the visual representation for the object.

Example
import Fumagalli_Motta_Tarantino_2020 as FMT20

model = FMT20.OptimalMergerPolicy()
visualizer = FMT20.Payoffs(m)
fig, ax = visualizer.plot()
# use the figure and axes as you wish, for example:
fig.show()
Parameters
  • **kwargs: Options for further customization of the plots.
    • title(str): Title for plot
    • legend(bool): If true, a secondary legend is shown.
    • width(float): Width of the bars.
    • spacing(float): Spacing between the bars.
    • max_opacity(float): Opacity of the optimal payoffs.
    • min_opacity(float): Opacity of the not optimal payoffs.
Returns
  • Figure: Containing the axes with the plots (use Figure.show() to display).
  • Axes: Containing the plots (arrange custom summary).