Fumagalli_Motta_Tarantino_2020.Visualizations.VisualizeRanges

  1from typing import Callable
  2from copy import deepcopy
  3import matplotlib.gridspec
  4import matplotlib.pyplot as plt
  5from matplotlib.ticker import FixedLocator
  6from matplotlib.patches import Rectangle
  7
  8import Fumagalli_Motta_Tarantino_2020.Models as Models
  9from Fumagalli_Motta_Tarantino_2020.Models.Types import *
 10from Fumagalli_Motta_Tarantino_2020.Visualizations.Visualize import *
 11
 12
 13class AssetRange(IVisualize):
 14    """
 15    Visualizes the outcomes over an assets range for a specific model.
 16    """
 17
 18    def __init__(self, model: Models.OptimalMergerPolicy, **kwargs) -> None:
 19        super(AssetRange, self).__init__(model, **kwargs)
 20        self.labels: list[str] = []
 21        self.colors: dict[str, dict] = {}
 22        self._thresholds: list[Models.ThresholdItem] = self._get_essential_thresholds()
 23        self._check_thresholds()
 24        self._label_colors: dict[str:dict] = self._init_label_colors()
 25
 26    @staticmethod
 27    def _init_label_colors() -> dict[str, dict]:
 28        o = PossibleOutcomes
 29        d = {}
 30        vis_success = 1.0
 31        vis_fail = 0.7
 32        vis_no_att = 0.4
 33        for i in [
 34            (o.NoTakeoversSuccessfulDevelopment, 0, vis_success),
 35            (o.NoTakeoversFailedDevelopment, 0, vis_fail),
 36            (o.NoTakeoversDevelopmentNotAttempted, 0, vis_no_att),
 37            (o.RejectedEarlySeparatingUnsuccessfulDevelopment, 1, vis_success),
 38            (o.RejectedEarlySeparatingSuccessfulDevelopment, 1, vis_fail),
 39            (o.EarlySeparatingSuccessfulDevelopment, 2, vis_success),
 40            (o.EarlySeparatingUnsuccessfulDevelopment, 2, vis_fail),
 41            (o.EarlySeparatingDevelopmentNotAttempted, 2, vis_no_att),
 42            (o.EarlyPoolingSuccessfulDevelopment, 3, vis_success),
 43            (o.EarlyPoolingUnsuccessfulDevelopment, 3, vis_fail),
 44            (o.EarlyPoolingDevelopmentNotAttempted, 3, vis_no_att),
 45            (o.LatePooling, 4, vis_success),
 46            (o.LatePoolingRejectedEarlySeparating, 4, vis_fail),
 47        ]:
 48            d.update(
 49                AssetRange._init_label_color(
 50                    outcome_type=i[0], color_id=i[1], opacity=i[2]
 51                ),
 52            )
 53        return d
 54
 55    @staticmethod
 56    def _init_label_color(
 57        outcome_type: Models.PossibleOutcomes, color_id: int, opacity: float
 58    ) -> dict[dict]:
 59        return {
 60            IVisualize._get_summary_latex(outcome_type.outcome): {
 61                "color": IVisualize.COLORS[color_id],
 62                "opacity": opacity,
 63            }
 64        }
 65
 66    @staticmethod
 67    def plot_label_colors(show_plot=False) -> plt.Axes:
 68        """
 69        Plots the colors used in the legend for asset ranges matched to the outcome.
 70
 71        Returns
 72        -------
 73        plt.Axes
 74            Axis containing the plot.
 75        """
 76        label_colors = AssetRange._init_label_colors()
 77        fig, ax = plt.subplots()
 78        ax.set_axis_off()
 79        height = 0.1
 80        width = 0.1
 81        ax.set_ylim(bottom=0, top=len(label_colors) * height)
 82        ax.set_xlim(left=0, right=width * 1.05 + 0.02)
 83        for i, label in enumerate(label_colors):
 84            ax.text(
 85                width * 1.1,
 86                (i + 0.5) * height,
 87                label,
 88                horizontalalignment="left",
 89                verticalalignment="center",
 90            )
 91
 92            ax.add_patch(
 93                Rectangle(
 94                    xy=(0, i * height),
 95                    width=width,
 96                    height=height,
 97                    facecolor=label_colors[label]["color"],
 98                    alpha=label_colors[label]["opacity"],
 99                )
100            )
101        fig.tight_layout()
102        if show_plot:
103            fig.show()
104        return ax
105
106    def _check_thresholds(self) -> None:
107        assert (
108            self._thresholds is not None and len(self._thresholds) >= 2
109        ), "Essential thresholds are not valid"
110
111    def set_model(self, model: Models.OptimalMergerPolicy) -> None:
112        super(AssetRange, self).set_model(model)
113        self._thresholds = self._get_essential_thresholds()
114        self._check_thresholds()
115
116    def _get_outcomes_asset_range(
117        self,
118    ) -> list[Models.OptimalMergerPolicySummary]:
119        """
120        Generates a list with all essential threshold concerning the assets of a start-up and an additional list with
121        summaries of the outcomes of the model in between the thresholds.
122
123        Returns
124        -------
125        (list[Fumagalli_Motta_Tarantino_2020.FMT20.ThresholdItem], list[Fumagalli_Motta_Tarantino_2020.FMT20.OptimalMergerPolicySummary])
126            List containing the essential asset thresholds in the model and list containing the summaries of the outcomes of the model.
127        """
128        original_assets = self.model.startup_assets
129        summaries: list[Models.OptimalMergerPolicySummary] = []
130        for i in range(len(self._thresholds) - 1):
131            self._set_model_startup_assets(self._thresholds[i], self._thresholds[i + 1])
132            summaries.append(self.model.summary())
133        self.model.startup_assets = original_assets
134        return summaries
135
136    def _set_model_startup_assets(
137        self,
138        lower_threshold: Models.ThresholdItem,
139        upper_threshold: Models.ThresholdItem,
140    ) -> None:
141        self.model.startup_assets = (
142            self._get_inverse_asset_distribution_value(lower_threshold.value)
143            + self._get_inverse_asset_distribution_value(upper_threshold.value)
144        ) / 2
145
146    def _get_essential_thresholds(self) -> list[Models.ThresholdItem]:
147        """
148        Generates a list with all essential threshold concerning the assets of a start-up.
149
150        Returns
151        -------
152        list[Fumagalli_Motta_Tarantino_2020.FMT20.ThresholdItem]
153            List containing the essential asset thresholds in the model.
154        """
155        thresholds = self._get_available_thresholds()
156        essential_thresholds: list[Models.ThresholdItem] = []
157        for threshold in thresholds:
158            if self._valid_x_tick(threshold):
159                essential_thresholds.append(threshold)
160        thresholds = sorted(essential_thresholds, key=lambda x: x.value)
161        return thresholds
162
163    def _get_available_thresholds(self) -> list[Models.ThresholdItem]:
164        return [
165            Models.ThresholdItem("$F(0)$", self._get_x_min(), include=True),
166            Models.ThresholdItem(
167                "$F(K)$",
168                self._get_x_max(),
169                include=True,
170            ),
171            Models.ThresholdItem(
172                "$\\Gamma$", self.model.asset_distribution_threshold_welfare
173            ),
174            Models.ThresholdItem(
175                "$\\Phi$",
176                self.model.asset_distribution_threshold_profitable_without_late_takeover,
177            ),
178            Models.ThresholdItem(
179                "$\\Phi^T$", self.model.asset_distribution_threshold_with_late_takeover
180            ),
181            Models.ThresholdItem(
182                "$\\Phi^{\\prime}$",
183                self.model.asset_distribution_threshold_unprofitable_without_late_takeover,
184            ),
185            Models.ThresholdItem("$F(\\bar{A})$", self.model.asset_threshold_cdf),
186            Models.ThresholdItem(
187                "$F(\\bar{A}^T)$", self.model.asset_threshold_late_takeover_cdf
188            ),
189            Models.ThresholdItem(
190                "$\\Lambda(\\cdot)$",
191                self.model.asset_distribution_threshold_shelving_approved,
192            ),
193        ]
194
195    def _get_x_labels_ticks(self) -> (list[float], list[str]):
196        """
197        Generates the locations of the ticks on the x-axis and the corresponding labels on the x-axis.
198
199        Returns
200        -------
201        (list[float], list[str])
202            A list containing the ticks on the x-axis and a list containing the labels on the x-axis.
203        """
204        x_ticks: list[float] = []
205        x_labels: list[str] = []
206        for threshold in self._thresholds:
207            x_ticks.append(threshold.value)
208            x_labels.append(threshold.name)
209        return x_ticks, x_labels
210
211    def _set_x_axis(self, **kwargs) -> None:
212        x_ticks, x_labels = self._get_x_labels_ticks()
213        self._set_x_locators(x_ticks)
214        self._set_x_labels(x_labels)
215        self._set_x_ticks()
216        self.ax.set_xlabel(
217            kwargs.get("x_label", "Cumulative Distribution Value of Assets $F(A)$")
218        )
219
220    def _set_x_ticks(self) -> None:
221        self.ax.tick_params(
222            which="minor",
223            bottom=False,
224            top=True,
225            labelbottom=False,
226            labeltop=True,
227            axis="x",
228            pad=0,
229        )
230        self.ax.tick_params(which="major", top=False, pad=3, axis="x")
231        self.ax.tick_params(which="both", length=2, axis="x")
232
233    def _set_x_labels(self, x_labels: list[str]) -> None:
234        self.ax.set_xticklabels(x_labels[::2], fontsize=IVisualize.fontsize)
235        self.ax.set_xticklabels(
236            x_labels[1::2], minor=True, fontsize=IVisualize.fontsize
237        )
238
239    def _set_x_locators(self, x_ticks: list[float]) -> None:
240        self.ax.xaxis.set_major_locator(FixedLocator(x_ticks[::2]))
241        self.ax.xaxis.set_minor_locator(FixedLocator(x_ticks[1::2]))
242
243    def _draw_vertical_lines(
244        self, asset_thresholds: list[Models.ThresholdItem]
245    ) -> None:
246        for threshold in asset_thresholds:
247            if self._valid_x_tick(threshold) or threshold.include:
248                self.ax.axvline(threshold.value, linestyle=":", color="k", lw=0.5)
249
250    def _valid_x_tick(self, threshold):
251        return (
252            self._get_x_min() < threshold.value < self._get_x_max()
253        ) or threshold.include
254
255    @staticmethod
256    def _get_y_ticks(
257        spacing: float, bar_height: float, y_labels: list[str]
258    ) -> list[float]:
259        return [(i + 1) * spacing + bar_height * i for i in range(len(y_labels))]
260
261    def _set_y_ticks(self, bar_height: float, spacing: float, y_labels: list[str]):
262        y_ticks = self._get_y_ticks(spacing, bar_height, y_labels)
263        self.ax.set_yticks(y_ticks)
264        self.ax.set_yticklabels(y_labels, fontsize=IVisualize.fontsize)
265        self.ax.yaxis.set_ticks_position("none")
266
267    def _get_label_color(self, label) -> (str, str, float):
268        """
269        Returns the color and the final label for a legend entry.
270
271        Through this method, duplications in the legend are avoided.
272
273        Parameters
274        ----------
275        label: str
276
277        Returns
278        -------
279        (str, str, float)
280            String representing the final label, a string representing the color and a float representing the opacity.
281        """
282        if label in self.labels:
283            return (
284                "_nolegend_",
285                self.colors[label]["color"],
286                self.colors[label]["opacity"],
287            )
288        self.colors[label] = self._get_label_specific_color(label)
289        self.labels.append(label)
290        return label, self.colors[label]["color"], self.colors[label]["opacity"]
291
292    def _get_label_specific_color(self, label: str) -> dict:
293        if label in self._label_colors.keys():
294            return self._label_colors[label]
295        return {"color": IVisualize.COLORS[4], "opacity": 0.5}
296
297    def _get_summaries(self) -> list[list[Models.OptimalMergerPolicySummary]]:
298        return [self._get_outcomes_asset_range()]
299
300    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
301        """
302        Plots the outcome of a model over a range of assets.
303
304        Example
305        -------
306        ```
307        import Fumagalli_Motta_Tarantino_2020 as FMT20
308
309        model = FMT20.OptimalMergerPolicy()
310        visualizer = FMT20.MergerPoliciesAssetRange(m)
311        fig, ax = visualizer.plot()
312        # use the figure and axes as you wish, for example:
313        fig.show()
314        ```
315
316        Parameters
317        ----------
318        **kwargs
319            Options for further customization of the plots.
320            - title(str): Title for plot<br>
321            - x_label(str): Title for x-axis.<br>
322            - y_label(str): Title for y-axis.<br>
323            - legend(bool): If true, a secondary legend is shown.<br>
324            - thresholds(bool): If true, the essential thresholds are shown.<br>
325            - optimal_policy(bool): If true, the optimal policy is shown.
326            - y_offset(int): Moves the threshold legend vertically.
327
328        Returns
329        -------
330        Figure
331            Containing the axes with the plots (use Figure.show() to display).
332        Axes
333            Containing the plots (arrange custom summary).
334        """
335        merger_policies_summaries = self._get_summaries()
336        assert merger_policies_summaries is not None
337        self._clear_legend_list()
338        bar_height, spacing, y_labels = self._draw_all_bars(
339            merger_policies_summaries, **kwargs
340        )
341        self._set_asset_range_legends(**kwargs)
342        self._draw_vertical_lines(self._thresholds)
343        self._set_x_axis(**kwargs)
344        self._set_y_axis(bar_height, spacing, y_labels, **kwargs)
345        self.ax.set_title(kwargs.get("title", "Outcome dependent on Start-up Assets"))
346        self._set_tight_layout(y_spacing=spacing)
347        return self.fig, self.ax
348
349    def _set_y_axis(self, bar_height, spacing, y_labels, **kwargs):
350        self._set_y_ticks(bar_height, spacing, y_labels)
351        self.ax.set_ylabel(kwargs.get("y_label", "Merger Policy"))
352
353    def _set_asset_range_legends(self, **kwargs):
354        self._set_primary_legend(equal_opacity=False)
355        self._set_secondary_legend(
356            self._thresholds[0].value, kwargs.get("legend", True)
357        )
358        self._set_threshold_legend(
359            kwargs.get("thresholds", False),
360            kwargs.get("optimal_policy", False),
361            kwargs.get("y_offset", 0),
362        )
363
364    def _clear_legend_list(self) -> None:
365        self.labels.clear()
366        self.colors.clear()
367
368    def _draw_all_bars(
369        self, merger_policies_summaries, **kwargs
370    ) -> (float, float, list[str]):
371        spacing: float = kwargs.get("spacing", 0.1)
372        bar_height: float = kwargs.get("bar_height", 0.2)
373        y_labels: list[str] = []
374        for number_merger_policy, summaries in enumerate(merger_policies_summaries):
375            y_labels.append(summaries[0].set_policy.abbreviation())
376            for summary_index, summary in enumerate(summaries):
377                label: str = self._get_summary_latex(summary)
378                length: float = self._get_bar_length(summary_index)
379                y_coordinate = self._get_bar_y_coordinate(
380                    bar_height, number_merger_policy, spacing
381                )
382                self._draw_bar(
383                    y_coordinate,
384                    self._thresholds[summary_index].value,
385                    bar_height,
386                    length,
387                    label,
388                )
389        return bar_height, spacing, y_labels
390
391    def _get_bar_length(self, summary_index: int) -> float:
392        return (
393            self._thresholds[summary_index + 1].value
394            - self._thresholds[summary_index].value
395        )
396
397    @staticmethod
398    def _get_bar_y_coordinate(
399        bar_height: float, number_merger_policy: int, spacing: float
400    ) -> float:
401        return spacing * (number_merger_policy + 1) + bar_height * number_merger_policy
402
403    def _draw_bar(
404        self,
405        y_coordinate: float,
406        x_coordinate: float,
407        bar_height: float,
408        length: float,
409        label: str,
410    ) -> None:
411        label, color, opacity = self._get_label_color(label)
412        self.ax.barh(
413            y=y_coordinate,
414            width=length,
415            left=x_coordinate,
416            height=bar_height,
417            color=color,
418            label=label,
419            alpha=opacity,
420        )
421
422    def _set_threshold_legend(
423        self, show_legend: bool, show_optimal_policy: bool, y_offset: int
424    ) -> None:
425        if show_legend:
426            x_coordinate = self._get_x_max()
427            y_coordinate = self._get_y_max()
428            self.ax.annotate(
429                self._get_model_characteristics(
430                    separator="\n",
431                    model_parameters=False,
432                    thresholds_newline=False,
433                    threshold_title="",
434                    optimal_policy=show_optimal_policy,
435                ),
436                xy=(x_coordinate, y_coordinate),
437                xytext=(10, y_offset),
438                textcoords="offset points",
439                horizontalalignment="left",
440                verticalalignment="top",
441                fontsize=IVisualize.fontsize,
442            )
443
444    @staticmethod
445    def _get_y_max() -> float:
446        return 1
447
448    def _get_x_max(self):
449        return self._get_asset_distribution_value(self.model.development_costs)
450
451    def _get_x_min(self):
452        return self._get_asset_distribution_value(0)
453
454    def _set_secondary_legend(self, x_coordinate: float, show_legend: bool) -> None:
455        if show_legend:
456            self.ax.annotate(
457                self._get_symbol_legend(),
458                xy=(x_coordinate, 0),
459                xytext=(0, -50),
460                textcoords="offset points",
461                horizontalalignment="left",
462                verticalalignment="top",
463                fontsize=IVisualize.fontsize,
464            )
465
466
467class MergerPoliciesAssetRange(AssetRange):
468    def _get_outcomes_different_merger_policies(
469        self,
470    ) -> list[list[Models.OptimalMergerPolicySummary]]:
471        original_policy = self.model.merger_policy
472        outcomes: list[list[Models.OptimalMergerPolicySummary]] = []
473        for merger_policy in Models.MergerPolicies:
474            try:
475                self.model.merger_policy = merger_policy
476                outcomes.append(self._get_outcomes_asset_range())
477            except Models.Exceptions.MergerPolicyNotAvailable:
478                pass
479        self.model.merger_policy = original_policy
480        return outcomes
481
482    def _get_summaries(self) -> list[list[Models.OptimalMergerPolicySummary]]:
483        return self._get_outcomes_different_merger_policies()
484
485
486class MergerPoliciesAssetRangePerfectInformation(MergerPoliciesAssetRange):
487    def __init__(self, model: Models.PerfectInformation, **kwargs):
488        """
489        Uses a Fumagalli_Motta_Tarantino_2020.Models.BaseExtended.PerfectInformation for the visualization. See
490        Fumagalli_Motta_Tarantino_2020.Models.Base.CoreModel for other parameters.
491
492        Parameters
493        ----------
494        model: Fumagalli_Motta_Tarantino_2020.Models.BaseExtended.PerfectInformation
495            Model to create the visualization from.
496        """
497        super(MergerPoliciesAssetRangePerfectInformation, self).__init__(
498            model, **kwargs
499        )
500
501    def _get_available_thresholds(self) -> list[Models.ThresholdItem]:
502        return [
503            Models.ThresholdItem("$0$", self._get_x_min(), include=True),
504            Models.ThresholdItem("$K$", self._get_x_max(), include=True),
505            Models.ThresholdItem("$\\bar{A}$", self.model.asset_threshold),
506            Models.ThresholdItem(
507                "$\\bar{A}^T$", self.model.asset_threshold_late_takeover
508            ),
509        ]
510
511    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
512        """
513        Plots the visual representation for the object.
514
515        Example
516        -------
517        ```
518        import Fumagalli_Motta_Tarantino_2020 as FMT20
519
520        model = FMT20.PerfectInformation()
521        visualizer = FMT20.MergerPoliciesAssetRangePerfectInformation(m)
522        fig, ax = visualizer.plot()
523        # use the figure and axes as you wish, for example:
524        fig.show()
525        ```
526
527        Parameters
528        ----------
529        **kwargs
530            Options for further customization of the plots (see Fumagalli_Motta_Tarantino_2020.Visualizations.VisualizeRanges.AssetRange.plot).
531
532        Returns
533        -------
534        Figure
535            Containing the axes with the plots (use Figure.show() to display).
536        Axes
537            Containing the plots (arrange custom summary).
538        """
539        kwargs["x_label"] = kwargs.get("x_label", "Start-up Assets $A$")
540        return super(MergerPoliciesAssetRangePerfectInformation, self).plot(**kwargs)
541
542    def _set_model_startup_assets(
543        self,
544        lower_threshold: Models.ThresholdItem,
545        upper_threshold: Models.ThresholdItem,
546    ) -> None:
547        self.model.startup_assets = (lower_threshold.value + upper_threshold.value) / 2
548
549    @staticmethod
550    def _get_y_max() -> float:
551        return 0.55
552
553    def _get_x_max(self) -> float:
554        return self.model.development_costs
555
556    def _get_x_min(self) -> float:
557        return 0
558
559    def _get_model_characteristics_thresholds(
560        self, separator: str, newline: str
561    ) -> str:
562        return (
563            f"$K = {self._round_floats(self.model.development_costs)}${separator}"
564            f"$\\bar{{A}} = {self._round_floats(self.model.asset_threshold)}${separator}"
565            f"$\\bar{{A}}^T = {self._round_floats(self.model.asset_threshold_late_takeover)}${separator}"
566        )
567
568
569class Overview(IVisualize):
570    """
571    Combines Fumagalli_Motta_Tarantino_2020.Visualizations.Visualize.Timeline, Fumagalli_Motta_Tarantino_2020.Visualizations.Visualize.Payoffs,
572    Fumagalli_Motta_Tarantino_2020.Visualizations.VisualizeRanges.MergerPoliciesAssetRange as well as a legend for the
573    model characteristics.
574    """
575
576    def __init__(self, model: Models.OptimalMergerPolicy, figsize=(14, 10), **kwargs):
577        super().__init__(model, figsize=figsize, constrained_layout=True, **kwargs)
578        self.timeline: Optional[IVisualize] = None
579        self.payoffs: Optional[IVisualize] = None
580        self.range: Optional[IVisualize] = None
581        self.kwargs = kwargs
582        self._clear_main_axes()
583
584    def set_model(self, model: Models.OptimalMergerPolicy) -> None:
585        assert (
586            self.timeline is not None
587            and self.payoffs is not None
588            and self.range is not None
589        )
590        super(Overview, self).set_model(model)
591        self.timeline.set_model(model)
592        self.payoffs.set_model(model)
593        self.range.set_model(model)
594        self.fig.clear()
595
596    @staticmethod
597    def _clear_main_axes() -> None:
598        plt.axis("off")
599
600    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
601        """
602        Plots the visual representation for the object.
603
604        Example
605        -------
606        ```
607        import Fumagalli_Motta_Tarantino_2020 as FMT20
608
609        model = FMT20.OptimalMergerPolicy()
610        visualizer = FMT20.Overview(m)
611        fig, ax = visualizer.plot()
612        # use the figure and axes as you wish, for example:
613        fig.show()
614        ```
615
616        Parameters
617        ----------
618        **kwargs
619            Options for further customization of the plots (Note: all subplots use the same kwargs).
620            - figure_title(str): Title for plot.<br>
621            - fontsize(int): Fontsize for model characteristics.<br>
622            - model_thresholds(bool): If true, the essential thresholds are shown in model characteristics.<br>
623            $\\Rightarrow$ see the included visualizations for further arguments.
624
625        Returns
626        -------
627        Figure
628            Containing the axes with the plots (use Figure.show() to display).
629        Axes
630            Containing the plots (arrange custom summary).
631        """
632        spec = self.fig.add_gridspec(ncols=2, nrows=2)
633        self._set_fig_title(**kwargs)
634        self.timeline = self._generate_visualizer(spec[1, 0], Timeline, **kwargs)
635        self.payoffs = self._generate_visualizer(spec[0, 1], Payoffs, **kwargs)
636        self.range = self._generate_visualizer(
637            spec[1, 1], self._get_merger_policy_asset_range_type(), **kwargs
638        )
639        self._generate_characteristics_ax(spec[0, 0], **kwargs)
640        return self.fig, self.ax
641
642    def _set_fig_title(self, **kwargs):
643        self.fig.suptitle(
644            kwargs.get("figure_title", "Model Overview"),
645            fontsize=18,
646        )
647
648    def _get_merger_policy_asset_range_type(self) -> Callable:
649        return (
650            MergerPoliciesAssetRangePerfectInformation
651            if type(self.model) is Models.PerfectInformation
652            else MergerPoliciesAssetRange
653        )
654
655    def _generate_characteristics_ax(
656        self, coordinates: matplotlib.gridspec.GridSpec, **kwargs
657    ) -> None:
658        ax = self.fig.add_subplot(coordinates)
659        characteristics_kwargs = deepcopy(kwargs)
660        characteristics_kwargs["model_thresholds"] = characteristics_kwargs.get(
661            "model_thresholds", not characteristics_kwargs.get("thresholds", False)
662        )
663        characteristics_kwargs["optimal_policy"] = False
664        self._get_model_characteristics_ax(ax, **characteristics_kwargs)
665
666    def _generate_visualizer(
667        self, coordinates: matplotlib.gridspec.GridSpec, visualizer: Callable, **kwargs
668    ) -> IVisualize:
669        ax = self.fig.add_subplot(coordinates)
670        visualization: IVisualize = visualizer(self.model, ax=ax, **self.kwargs)
671        visualization.plot(legend=False, parameters=False, **kwargs)
672        return visualization
 14class AssetRange(IVisualize):
 15    """
 16    Visualizes the outcomes over an assets range for a specific model.
 17    """
 18
 19    def __init__(self, model: Models.OptimalMergerPolicy, **kwargs) -> None:
 20        super(AssetRange, self).__init__(model, **kwargs)
 21        self.labels: list[str] = []
 22        self.colors: dict[str, dict] = {}
 23        self._thresholds: list[Models.ThresholdItem] = self._get_essential_thresholds()
 24        self._check_thresholds()
 25        self._label_colors: dict[str:dict] = self._init_label_colors()
 26
 27    @staticmethod
 28    def _init_label_colors() -> dict[str, dict]:
 29        o = PossibleOutcomes
 30        d = {}
 31        vis_success = 1.0
 32        vis_fail = 0.7
 33        vis_no_att = 0.4
 34        for i in [
 35            (o.NoTakeoversSuccessfulDevelopment, 0, vis_success),
 36            (o.NoTakeoversFailedDevelopment, 0, vis_fail),
 37            (o.NoTakeoversDevelopmentNotAttempted, 0, vis_no_att),
 38            (o.RejectedEarlySeparatingUnsuccessfulDevelopment, 1, vis_success),
 39            (o.RejectedEarlySeparatingSuccessfulDevelopment, 1, vis_fail),
 40            (o.EarlySeparatingSuccessfulDevelopment, 2, vis_success),
 41            (o.EarlySeparatingUnsuccessfulDevelopment, 2, vis_fail),
 42            (o.EarlySeparatingDevelopmentNotAttempted, 2, vis_no_att),
 43            (o.EarlyPoolingSuccessfulDevelopment, 3, vis_success),
 44            (o.EarlyPoolingUnsuccessfulDevelopment, 3, vis_fail),
 45            (o.EarlyPoolingDevelopmentNotAttempted, 3, vis_no_att),
 46            (o.LatePooling, 4, vis_success),
 47            (o.LatePoolingRejectedEarlySeparating, 4, vis_fail),
 48        ]:
 49            d.update(
 50                AssetRange._init_label_color(
 51                    outcome_type=i[0], color_id=i[1], opacity=i[2]
 52                ),
 53            )
 54        return d
 55
 56    @staticmethod
 57    def _init_label_color(
 58        outcome_type: Models.PossibleOutcomes, color_id: int, opacity: float
 59    ) -> dict[dict]:
 60        return {
 61            IVisualize._get_summary_latex(outcome_type.outcome): {
 62                "color": IVisualize.COLORS[color_id],
 63                "opacity": opacity,
 64            }
 65        }
 66
 67    @staticmethod
 68    def plot_label_colors(show_plot=False) -> plt.Axes:
 69        """
 70        Plots the colors used in the legend for asset ranges matched to the outcome.
 71
 72        Returns
 73        -------
 74        plt.Axes
 75            Axis containing the plot.
 76        """
 77        label_colors = AssetRange._init_label_colors()
 78        fig, ax = plt.subplots()
 79        ax.set_axis_off()
 80        height = 0.1
 81        width = 0.1
 82        ax.set_ylim(bottom=0, top=len(label_colors) * height)
 83        ax.set_xlim(left=0, right=width * 1.05 + 0.02)
 84        for i, label in enumerate(label_colors):
 85            ax.text(
 86                width * 1.1,
 87                (i + 0.5) * height,
 88                label,
 89                horizontalalignment="left",
 90                verticalalignment="center",
 91            )
 92
 93            ax.add_patch(
 94                Rectangle(
 95                    xy=(0, i * height),
 96                    width=width,
 97                    height=height,
 98                    facecolor=label_colors[label]["color"],
 99                    alpha=label_colors[label]["opacity"],
100                )
101            )
102        fig.tight_layout()
103        if show_plot:
104            fig.show()
105        return ax
106
107    def _check_thresholds(self) -> None:
108        assert (
109            self._thresholds is not None and len(self._thresholds) >= 2
110        ), "Essential thresholds are not valid"
111
112    def set_model(self, model: Models.OptimalMergerPolicy) -> None:
113        super(AssetRange, self).set_model(model)
114        self._thresholds = self._get_essential_thresholds()
115        self._check_thresholds()
116
117    def _get_outcomes_asset_range(
118        self,
119    ) -> list[Models.OptimalMergerPolicySummary]:
120        """
121        Generates a list with all essential threshold concerning the assets of a start-up and an additional list with
122        summaries of the outcomes of the model in between the thresholds.
123
124        Returns
125        -------
126        (list[Fumagalli_Motta_Tarantino_2020.FMT20.ThresholdItem], list[Fumagalli_Motta_Tarantino_2020.FMT20.OptimalMergerPolicySummary])
127            List containing the essential asset thresholds in the model and list containing the summaries of the outcomes of the model.
128        """
129        original_assets = self.model.startup_assets
130        summaries: list[Models.OptimalMergerPolicySummary] = []
131        for i in range(len(self._thresholds) - 1):
132            self._set_model_startup_assets(self._thresholds[i], self._thresholds[i + 1])
133            summaries.append(self.model.summary())
134        self.model.startup_assets = original_assets
135        return summaries
136
137    def _set_model_startup_assets(
138        self,
139        lower_threshold: Models.ThresholdItem,
140        upper_threshold: Models.ThresholdItem,
141    ) -> None:
142        self.model.startup_assets = (
143            self._get_inverse_asset_distribution_value(lower_threshold.value)
144            + self._get_inverse_asset_distribution_value(upper_threshold.value)
145        ) / 2
146
147    def _get_essential_thresholds(self) -> list[Models.ThresholdItem]:
148        """
149        Generates a list with all essential threshold concerning the assets of a start-up.
150
151        Returns
152        -------
153        list[Fumagalli_Motta_Tarantino_2020.FMT20.ThresholdItem]
154            List containing the essential asset thresholds in the model.
155        """
156        thresholds = self._get_available_thresholds()
157        essential_thresholds: list[Models.ThresholdItem] = []
158        for threshold in thresholds:
159            if self._valid_x_tick(threshold):
160                essential_thresholds.append(threshold)
161        thresholds = sorted(essential_thresholds, key=lambda x: x.value)
162        return thresholds
163
164    def _get_available_thresholds(self) -> list[Models.ThresholdItem]:
165        return [
166            Models.ThresholdItem("$F(0)$", self._get_x_min(), include=True),
167            Models.ThresholdItem(
168                "$F(K)$",
169                self._get_x_max(),
170                include=True,
171            ),
172            Models.ThresholdItem(
173                "$\\Gamma$", self.model.asset_distribution_threshold_welfare
174            ),
175            Models.ThresholdItem(
176                "$\\Phi$",
177                self.model.asset_distribution_threshold_profitable_without_late_takeover,
178            ),
179            Models.ThresholdItem(
180                "$\\Phi^T$", self.model.asset_distribution_threshold_with_late_takeover
181            ),
182            Models.ThresholdItem(
183                "$\\Phi^{\\prime}$",
184                self.model.asset_distribution_threshold_unprofitable_without_late_takeover,
185            ),
186            Models.ThresholdItem("$F(\\bar{A})$", self.model.asset_threshold_cdf),
187            Models.ThresholdItem(
188                "$F(\\bar{A}^T)$", self.model.asset_threshold_late_takeover_cdf
189            ),
190            Models.ThresholdItem(
191                "$\\Lambda(\\cdot)$",
192                self.model.asset_distribution_threshold_shelving_approved,
193            ),
194        ]
195
196    def _get_x_labels_ticks(self) -> (list[float], list[str]):
197        """
198        Generates the locations of the ticks on the x-axis and the corresponding labels on the x-axis.
199
200        Returns
201        -------
202        (list[float], list[str])
203            A list containing the ticks on the x-axis and a list containing the labels on the x-axis.
204        """
205        x_ticks: list[float] = []
206        x_labels: list[str] = []
207        for threshold in self._thresholds:
208            x_ticks.append(threshold.value)
209            x_labels.append(threshold.name)
210        return x_ticks, x_labels
211
212    def _set_x_axis(self, **kwargs) -> None:
213        x_ticks, x_labels = self._get_x_labels_ticks()
214        self._set_x_locators(x_ticks)
215        self._set_x_labels(x_labels)
216        self._set_x_ticks()
217        self.ax.set_xlabel(
218            kwargs.get("x_label", "Cumulative Distribution Value of Assets $F(A)$")
219        )
220
221    def _set_x_ticks(self) -> None:
222        self.ax.tick_params(
223            which="minor",
224            bottom=False,
225            top=True,
226            labelbottom=False,
227            labeltop=True,
228            axis="x",
229            pad=0,
230        )
231        self.ax.tick_params(which="major", top=False, pad=3, axis="x")
232        self.ax.tick_params(which="both", length=2, axis="x")
233
234    def _set_x_labels(self, x_labels: list[str]) -> None:
235        self.ax.set_xticklabels(x_labels[::2], fontsize=IVisualize.fontsize)
236        self.ax.set_xticklabels(
237            x_labels[1::2], minor=True, fontsize=IVisualize.fontsize
238        )
239
240    def _set_x_locators(self, x_ticks: list[float]) -> None:
241        self.ax.xaxis.set_major_locator(FixedLocator(x_ticks[::2]))
242        self.ax.xaxis.set_minor_locator(FixedLocator(x_ticks[1::2]))
243
244    def _draw_vertical_lines(
245        self, asset_thresholds: list[Models.ThresholdItem]
246    ) -> None:
247        for threshold in asset_thresholds:
248            if self._valid_x_tick(threshold) or threshold.include:
249                self.ax.axvline(threshold.value, linestyle=":", color="k", lw=0.5)
250
251    def _valid_x_tick(self, threshold):
252        return (
253            self._get_x_min() < threshold.value < self._get_x_max()
254        ) or threshold.include
255
256    @staticmethod
257    def _get_y_ticks(
258        spacing: float, bar_height: float, y_labels: list[str]
259    ) -> list[float]:
260        return [(i + 1) * spacing + bar_height * i for i in range(len(y_labels))]
261
262    def _set_y_ticks(self, bar_height: float, spacing: float, y_labels: list[str]):
263        y_ticks = self._get_y_ticks(spacing, bar_height, y_labels)
264        self.ax.set_yticks(y_ticks)
265        self.ax.set_yticklabels(y_labels, fontsize=IVisualize.fontsize)
266        self.ax.yaxis.set_ticks_position("none")
267
268    def _get_label_color(self, label) -> (str, str, float):
269        """
270        Returns the color and the final label for a legend entry.
271
272        Through this method, duplications in the legend are avoided.
273
274        Parameters
275        ----------
276        label: str
277
278        Returns
279        -------
280        (str, str, float)
281            String representing the final label, a string representing the color and a float representing the opacity.
282        """
283        if label in self.labels:
284            return (
285                "_nolegend_",
286                self.colors[label]["color"],
287                self.colors[label]["opacity"],
288            )
289        self.colors[label] = self._get_label_specific_color(label)
290        self.labels.append(label)
291        return label, self.colors[label]["color"], self.colors[label]["opacity"]
292
293    def _get_label_specific_color(self, label: str) -> dict:
294        if label in self._label_colors.keys():
295            return self._label_colors[label]
296        return {"color": IVisualize.COLORS[4], "opacity": 0.5}
297
298    def _get_summaries(self) -> list[list[Models.OptimalMergerPolicySummary]]:
299        return [self._get_outcomes_asset_range()]
300
301    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
302        """
303        Plots the outcome of a model over a range of assets.
304
305        Example
306        -------
307        ```
308        import Fumagalli_Motta_Tarantino_2020 as FMT20
309
310        model = FMT20.OptimalMergerPolicy()
311        visualizer = FMT20.MergerPoliciesAssetRange(m)
312        fig, ax = visualizer.plot()
313        # use the figure and axes as you wish, for example:
314        fig.show()
315        ```
316
317        Parameters
318        ----------
319        **kwargs
320            Options for further customization of the plots.
321            - title(str): Title for plot<br>
322            - x_label(str): Title for x-axis.<br>
323            - y_label(str): Title for y-axis.<br>
324            - legend(bool): If true, a secondary legend is shown.<br>
325            - thresholds(bool): If true, the essential thresholds are shown.<br>
326            - optimal_policy(bool): If true, the optimal policy is shown.
327            - y_offset(int): Moves the threshold legend vertically.
328
329        Returns
330        -------
331        Figure
332            Containing the axes with the plots (use Figure.show() to display).
333        Axes
334            Containing the plots (arrange custom summary).
335        """
336        merger_policies_summaries = self._get_summaries()
337        assert merger_policies_summaries is not None
338        self._clear_legend_list()
339        bar_height, spacing, y_labels = self._draw_all_bars(
340            merger_policies_summaries, **kwargs
341        )
342        self._set_asset_range_legends(**kwargs)
343        self._draw_vertical_lines(self._thresholds)
344        self._set_x_axis(**kwargs)
345        self._set_y_axis(bar_height, spacing, y_labels, **kwargs)
346        self.ax.set_title(kwargs.get("title", "Outcome dependent on Start-up Assets"))
347        self._set_tight_layout(y_spacing=spacing)
348        return self.fig, self.ax
349
350    def _set_y_axis(self, bar_height, spacing, y_labels, **kwargs):
351        self._set_y_ticks(bar_height, spacing, y_labels)
352        self.ax.set_ylabel(kwargs.get("y_label", "Merger Policy"))
353
354    def _set_asset_range_legends(self, **kwargs):
355        self._set_primary_legend(equal_opacity=False)
356        self._set_secondary_legend(
357            self._thresholds[0].value, kwargs.get("legend", True)
358        )
359        self._set_threshold_legend(
360            kwargs.get("thresholds", False),
361            kwargs.get("optimal_policy", False),
362            kwargs.get("y_offset", 0),
363        )
364
365    def _clear_legend_list(self) -> None:
366        self.labels.clear()
367        self.colors.clear()
368
369    def _draw_all_bars(
370        self, merger_policies_summaries, **kwargs
371    ) -> (float, float, list[str]):
372        spacing: float = kwargs.get("spacing", 0.1)
373        bar_height: float = kwargs.get("bar_height", 0.2)
374        y_labels: list[str] = []
375        for number_merger_policy, summaries in enumerate(merger_policies_summaries):
376            y_labels.append(summaries[0].set_policy.abbreviation())
377            for summary_index, summary in enumerate(summaries):
378                label: str = self._get_summary_latex(summary)
379                length: float = self._get_bar_length(summary_index)
380                y_coordinate = self._get_bar_y_coordinate(
381                    bar_height, number_merger_policy, spacing
382                )
383                self._draw_bar(
384                    y_coordinate,
385                    self._thresholds[summary_index].value,
386                    bar_height,
387                    length,
388                    label,
389                )
390        return bar_height, spacing, y_labels
391
392    def _get_bar_length(self, summary_index: int) -> float:
393        return (
394            self._thresholds[summary_index + 1].value
395            - self._thresholds[summary_index].value
396        )
397
398    @staticmethod
399    def _get_bar_y_coordinate(
400        bar_height: float, number_merger_policy: int, spacing: float
401    ) -> float:
402        return spacing * (number_merger_policy + 1) + bar_height * number_merger_policy
403
404    def _draw_bar(
405        self,
406        y_coordinate: float,
407        x_coordinate: float,
408        bar_height: float,
409        length: float,
410        label: str,
411    ) -> None:
412        label, color, opacity = self._get_label_color(label)
413        self.ax.barh(
414            y=y_coordinate,
415            width=length,
416            left=x_coordinate,
417            height=bar_height,
418            color=color,
419            label=label,
420            alpha=opacity,
421        )
422
423    def _set_threshold_legend(
424        self, show_legend: bool, show_optimal_policy: bool, y_offset: int
425    ) -> None:
426        if show_legend:
427            x_coordinate = self._get_x_max()
428            y_coordinate = self._get_y_max()
429            self.ax.annotate(
430                self._get_model_characteristics(
431                    separator="\n",
432                    model_parameters=False,
433                    thresholds_newline=False,
434                    threshold_title="",
435                    optimal_policy=show_optimal_policy,
436                ),
437                xy=(x_coordinate, y_coordinate),
438                xytext=(10, y_offset),
439                textcoords="offset points",
440                horizontalalignment="left",
441                verticalalignment="top",
442                fontsize=IVisualize.fontsize,
443            )
444
445    @staticmethod
446    def _get_y_max() -> float:
447        return 1
448
449    def _get_x_max(self):
450        return self._get_asset_distribution_value(self.model.development_costs)
451
452    def _get_x_min(self):
453        return self._get_asset_distribution_value(0)
454
455    def _set_secondary_legend(self, x_coordinate: float, show_legend: bool) -> None:
456        if show_legend:
457            self.ax.annotate(
458                self._get_symbol_legend(),
459                xy=(x_coordinate, 0),
460                xytext=(0, -50),
461                textcoords="offset points",
462                horizontalalignment="left",
463                verticalalignment="top",
464                fontsize=IVisualize.fontsize,
465            )

Visualizes the outcomes over an assets range for a specific model.

AssetRange( model: Fumagalli_Motta_Tarantino_2020.Models.Base.OptimalMergerPolicy, **kwargs)
19    def __init__(self, model: Models.OptimalMergerPolicy, **kwargs) -> None:
20        super(AssetRange, self).__init__(model, **kwargs)
21        self.labels: list[str] = []
22        self.colors: dict[str, dict] = {}
23        self._thresholds: list[Models.ThresholdItem] = self._get_essential_thresholds()
24        self._check_thresholds()
25        self._label_colors: dict[str:dict] = self._init_label_colors()
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.
labels: list[str]
colors: dict[str, dict]
@staticmethod
def plot_label_colors(show_plot=False) -> matplotlib.axes._axes.Axes:
 67    @staticmethod
 68    def plot_label_colors(show_plot=False) -> plt.Axes:
 69        """
 70        Plots the colors used in the legend for asset ranges matched to the outcome.
 71
 72        Returns
 73        -------
 74        plt.Axes
 75            Axis containing the plot.
 76        """
 77        label_colors = AssetRange._init_label_colors()
 78        fig, ax = plt.subplots()
 79        ax.set_axis_off()
 80        height = 0.1
 81        width = 0.1
 82        ax.set_ylim(bottom=0, top=len(label_colors) * height)
 83        ax.set_xlim(left=0, right=width * 1.05 + 0.02)
 84        for i, label in enumerate(label_colors):
 85            ax.text(
 86                width * 1.1,
 87                (i + 0.5) * height,
 88                label,
 89                horizontalalignment="left",
 90                verticalalignment="center",
 91            )
 92
 93            ax.add_patch(
 94                Rectangle(
 95                    xy=(0, i * height),
 96                    width=width,
 97                    height=height,
 98                    facecolor=label_colors[label]["color"],
 99                    alpha=label_colors[label]["opacity"],
100                )
101            )
102        fig.tight_layout()
103        if show_plot:
104            fig.show()
105        return ax

Plots the colors used in the legend for asset ranges matched to the outcome.

Returns
  • plt.Axes: Axis containing the plot.
def set_model( self, model: Fumagalli_Motta_Tarantino_2020.Models.Base.OptimalMergerPolicy) -> None:
112    def set_model(self, model: Models.OptimalMergerPolicy) -> None:
113        super(AssetRange, self).set_model(model)
114        self._thresholds = self._get_essential_thresholds()
115        self._check_thresholds()

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.
def plot( self, **kwargs) -> (<class 'matplotlib.figure.Figure'>, <class 'matplotlib.axes._axes.Axes'>):
301    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
302        """
303        Plots the outcome of a model over a range of assets.
304
305        Example
306        -------
307        ```
308        import Fumagalli_Motta_Tarantino_2020 as FMT20
309
310        model = FMT20.OptimalMergerPolicy()
311        visualizer = FMT20.MergerPoliciesAssetRange(m)
312        fig, ax = visualizer.plot()
313        # use the figure and axes as you wish, for example:
314        fig.show()
315        ```
316
317        Parameters
318        ----------
319        **kwargs
320            Options for further customization of the plots.
321            - title(str): Title for plot<br>
322            - x_label(str): Title for x-axis.<br>
323            - y_label(str): Title for y-axis.<br>
324            - legend(bool): If true, a secondary legend is shown.<br>
325            - thresholds(bool): If true, the essential thresholds are shown.<br>
326            - optimal_policy(bool): If true, the optimal policy is shown.
327            - y_offset(int): Moves the threshold legend vertically.
328
329        Returns
330        -------
331        Figure
332            Containing the axes with the plots (use Figure.show() to display).
333        Axes
334            Containing the plots (arrange custom summary).
335        """
336        merger_policies_summaries = self._get_summaries()
337        assert merger_policies_summaries is not None
338        self._clear_legend_list()
339        bar_height, spacing, y_labels = self._draw_all_bars(
340            merger_policies_summaries, **kwargs
341        )
342        self._set_asset_range_legends(**kwargs)
343        self._draw_vertical_lines(self._thresholds)
344        self._set_x_axis(**kwargs)
345        self._set_y_axis(bar_height, spacing, y_labels, **kwargs)
346        self.ax.set_title(kwargs.get("title", "Outcome dependent on Start-up Assets"))
347        self._set_tight_layout(y_spacing=spacing)
348        return self.fig, self.ax

Plots the outcome of a model over a range of assets.

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.
    • title(str): Title for plot
    • x_label(str): Title for x-axis.
    • y_label(str): Title for y-axis.
    • legend(bool): If true, a secondary legend is shown.
    • thresholds(bool): If true, the essential thresholds are shown.
    • optimal_policy(bool): If true, the optimal policy is shown.
    • y_offset(int): Moves the threshold legend vertically.
Returns
  • Figure: Containing the axes with the plots (use Figure.show() to display).
  • Axes: Containing the plots (arrange custom summary).
class MergerPoliciesAssetRange(AssetRange):
468class MergerPoliciesAssetRange(AssetRange):
469    def _get_outcomes_different_merger_policies(
470        self,
471    ) -> list[list[Models.OptimalMergerPolicySummary]]:
472        original_policy = self.model.merger_policy
473        outcomes: list[list[Models.OptimalMergerPolicySummary]] = []
474        for merger_policy in Models.MergerPolicies:
475            try:
476                self.model.merger_policy = merger_policy
477                outcomes.append(self._get_outcomes_asset_range())
478            except Models.Exceptions.MergerPolicyNotAvailable:
479                pass
480        self.model.merger_policy = original_policy
481        return outcomes
482
483    def _get_summaries(self) -> list[list[Models.OptimalMergerPolicySummary]]:
484        return self._get_outcomes_different_merger_policies()

Visualizes the outcomes over an assets range for a specific model.

class MergerPoliciesAssetRangePerfectInformation(MergerPoliciesAssetRange):
487class MergerPoliciesAssetRangePerfectInformation(MergerPoliciesAssetRange):
488    def __init__(self, model: Models.PerfectInformation, **kwargs):
489        """
490        Uses a Fumagalli_Motta_Tarantino_2020.Models.BaseExtended.PerfectInformation for the visualization. See
491        Fumagalli_Motta_Tarantino_2020.Models.Base.CoreModel for other parameters.
492
493        Parameters
494        ----------
495        model: Fumagalli_Motta_Tarantino_2020.Models.BaseExtended.PerfectInformation
496            Model to create the visualization from.
497        """
498        super(MergerPoliciesAssetRangePerfectInformation, self).__init__(
499            model, **kwargs
500        )
501
502    def _get_available_thresholds(self) -> list[Models.ThresholdItem]:
503        return [
504            Models.ThresholdItem("$0$", self._get_x_min(), include=True),
505            Models.ThresholdItem("$K$", self._get_x_max(), include=True),
506            Models.ThresholdItem("$\\bar{A}$", self.model.asset_threshold),
507            Models.ThresholdItem(
508                "$\\bar{A}^T$", self.model.asset_threshold_late_takeover
509            ),
510        ]
511
512    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
513        """
514        Plots the visual representation for the object.
515
516        Example
517        -------
518        ```
519        import Fumagalli_Motta_Tarantino_2020 as FMT20
520
521        model = FMT20.PerfectInformation()
522        visualizer = FMT20.MergerPoliciesAssetRangePerfectInformation(m)
523        fig, ax = visualizer.plot()
524        # use the figure and axes as you wish, for example:
525        fig.show()
526        ```
527
528        Parameters
529        ----------
530        **kwargs
531            Options for further customization of the plots (see Fumagalli_Motta_Tarantino_2020.Visualizations.VisualizeRanges.AssetRange.plot).
532
533        Returns
534        -------
535        Figure
536            Containing the axes with the plots (use Figure.show() to display).
537        Axes
538            Containing the plots (arrange custom summary).
539        """
540        kwargs["x_label"] = kwargs.get("x_label", "Start-up Assets $A$")
541        return super(MergerPoliciesAssetRangePerfectInformation, self).plot(**kwargs)
542
543    def _set_model_startup_assets(
544        self,
545        lower_threshold: Models.ThresholdItem,
546        upper_threshold: Models.ThresholdItem,
547    ) -> None:
548        self.model.startup_assets = (lower_threshold.value + upper_threshold.value) / 2
549
550    @staticmethod
551    def _get_y_max() -> float:
552        return 0.55
553
554    def _get_x_max(self) -> float:
555        return self.model.development_costs
556
557    def _get_x_min(self) -> float:
558        return 0
559
560    def _get_model_characteristics_thresholds(
561        self, separator: str, newline: str
562    ) -> str:
563        return (
564            f"$K = {self._round_floats(self.model.development_costs)}${separator}"
565            f"$\\bar{{A}} = {self._round_floats(self.model.asset_threshold)}${separator}"
566            f"$\\bar{{A}}^T = {self._round_floats(self.model.asset_threshold_late_takeover)}${separator}"
567        )

Visualizes the outcomes over an assets range for a specific model.

MergerPoliciesAssetRangePerfectInformation( model: Fumagalli_Motta_Tarantino_2020.Models.BaseExtended.PerfectInformation, **kwargs)
488    def __init__(self, model: Models.PerfectInformation, **kwargs):
489        """
490        Uses a Fumagalli_Motta_Tarantino_2020.Models.BaseExtended.PerfectInformation for the visualization. See
491        Fumagalli_Motta_Tarantino_2020.Models.Base.CoreModel for other parameters.
492
493        Parameters
494        ----------
495        model: Fumagalli_Motta_Tarantino_2020.Models.BaseExtended.PerfectInformation
496            Model to create the visualization from.
497        """
498        super(MergerPoliciesAssetRangePerfectInformation, self).__init__(
499            model, **kwargs
500        )
def plot( self, **kwargs) -> (<class 'matplotlib.figure.Figure'>, <class 'matplotlib.axes._axes.Axes'>):
512    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
513        """
514        Plots the visual representation for the object.
515
516        Example
517        -------
518        ```
519        import Fumagalli_Motta_Tarantino_2020 as FMT20
520
521        model = FMT20.PerfectInformation()
522        visualizer = FMT20.MergerPoliciesAssetRangePerfectInformation(m)
523        fig, ax = visualizer.plot()
524        # use the figure and axes as you wish, for example:
525        fig.show()
526        ```
527
528        Parameters
529        ----------
530        **kwargs
531            Options for further customization of the plots (see Fumagalli_Motta_Tarantino_2020.Visualizations.VisualizeRanges.AssetRange.plot).
532
533        Returns
534        -------
535        Figure
536            Containing the axes with the plots (use Figure.show() to display).
537        Axes
538            Containing the plots (arrange custom summary).
539        """
540        kwargs["x_label"] = kwargs.get("x_label", "Start-up Assets $A$")
541        return super(MergerPoliciesAssetRangePerfectInformation, self).plot(**kwargs)

Plots the visual representation for the object.

Example
import Fumagalli_Motta_Tarantino_2020 as FMT20

model = FMT20.PerfectInformation()
visualizer = FMT20.MergerPoliciesAssetRangePerfectInformation(m)
fig, ax = visualizer.plot()
# use the figure and axes as you wish, for example:
fig.show()
Parameters
Returns
  • Figure: Containing the axes with the plots (use Figure.show() to display).
  • Axes: Containing the plots (arrange custom summary).
570class Overview(IVisualize):
571    """
572    Combines Fumagalli_Motta_Tarantino_2020.Visualizations.Visualize.Timeline, Fumagalli_Motta_Tarantino_2020.Visualizations.Visualize.Payoffs,
573    Fumagalli_Motta_Tarantino_2020.Visualizations.VisualizeRanges.MergerPoliciesAssetRange as well as a legend for the
574    model characteristics.
575    """
576
577    def __init__(self, model: Models.OptimalMergerPolicy, figsize=(14, 10), **kwargs):
578        super().__init__(model, figsize=figsize, constrained_layout=True, **kwargs)
579        self.timeline: Optional[IVisualize] = None
580        self.payoffs: Optional[IVisualize] = None
581        self.range: Optional[IVisualize] = None
582        self.kwargs = kwargs
583        self._clear_main_axes()
584
585    def set_model(self, model: Models.OptimalMergerPolicy) -> None:
586        assert (
587            self.timeline is not None
588            and self.payoffs is not None
589            and self.range is not None
590        )
591        super(Overview, self).set_model(model)
592        self.timeline.set_model(model)
593        self.payoffs.set_model(model)
594        self.range.set_model(model)
595        self.fig.clear()
596
597    @staticmethod
598    def _clear_main_axes() -> None:
599        plt.axis("off")
600
601    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
602        """
603        Plots the visual representation for the object.
604
605        Example
606        -------
607        ```
608        import Fumagalli_Motta_Tarantino_2020 as FMT20
609
610        model = FMT20.OptimalMergerPolicy()
611        visualizer = FMT20.Overview(m)
612        fig, ax = visualizer.plot()
613        # use the figure and axes as you wish, for example:
614        fig.show()
615        ```
616
617        Parameters
618        ----------
619        **kwargs
620            Options for further customization of the plots (Note: all subplots use the same kwargs).
621            - figure_title(str): Title for plot.<br>
622            - fontsize(int): Fontsize for model characteristics.<br>
623            - model_thresholds(bool): If true, the essential thresholds are shown in model characteristics.<br>
624            $\\Rightarrow$ see the included visualizations for further arguments.
625
626        Returns
627        -------
628        Figure
629            Containing the axes with the plots (use Figure.show() to display).
630        Axes
631            Containing the plots (arrange custom summary).
632        """
633        spec = self.fig.add_gridspec(ncols=2, nrows=2)
634        self._set_fig_title(**kwargs)
635        self.timeline = self._generate_visualizer(spec[1, 0], Timeline, **kwargs)
636        self.payoffs = self._generate_visualizer(spec[0, 1], Payoffs, **kwargs)
637        self.range = self._generate_visualizer(
638            spec[1, 1], self._get_merger_policy_asset_range_type(), **kwargs
639        )
640        self._generate_characteristics_ax(spec[0, 0], **kwargs)
641        return self.fig, self.ax
642
643    def _set_fig_title(self, **kwargs):
644        self.fig.suptitle(
645            kwargs.get("figure_title", "Model Overview"),
646            fontsize=18,
647        )
648
649    def _get_merger_policy_asset_range_type(self) -> Callable:
650        return (
651            MergerPoliciesAssetRangePerfectInformation
652            if type(self.model) is Models.PerfectInformation
653            else MergerPoliciesAssetRange
654        )
655
656    def _generate_characteristics_ax(
657        self, coordinates: matplotlib.gridspec.GridSpec, **kwargs
658    ) -> None:
659        ax = self.fig.add_subplot(coordinates)
660        characteristics_kwargs = deepcopy(kwargs)
661        characteristics_kwargs["model_thresholds"] = characteristics_kwargs.get(
662            "model_thresholds", not characteristics_kwargs.get("thresholds", False)
663        )
664        characteristics_kwargs["optimal_policy"] = False
665        self._get_model_characteristics_ax(ax, **characteristics_kwargs)
666
667    def _generate_visualizer(
668        self, coordinates: matplotlib.gridspec.GridSpec, visualizer: Callable, **kwargs
669    ) -> IVisualize:
670        ax = self.fig.add_subplot(coordinates)
671        visualization: IVisualize = visualizer(self.model, ax=ax, **self.kwargs)
672        visualization.plot(legend=False, parameters=False, **kwargs)
673        return visualization
Overview( model: Fumagalli_Motta_Tarantino_2020.Models.Base.OptimalMergerPolicy, figsize=(14, 10), **kwargs)
577    def __init__(self, model: Models.OptimalMergerPolicy, figsize=(14, 10), **kwargs):
578        super().__init__(model, figsize=figsize, constrained_layout=True, **kwargs)
579        self.timeline: Optional[IVisualize] = None
580        self.payoffs: Optional[IVisualize] = None
581        self.range: Optional[IVisualize] = None
582        self.kwargs = kwargs
583        self._clear_main_axes()
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.
kwargs
def set_model( self, model: Fumagalli_Motta_Tarantino_2020.Models.Base.OptimalMergerPolicy) -> None:
585    def set_model(self, model: Models.OptimalMergerPolicy) -> None:
586        assert (
587            self.timeline is not None
588            and self.payoffs is not None
589            and self.range is not None
590        )
591        super(Overview, self).set_model(model)
592        self.timeline.set_model(model)
593        self.payoffs.set_model(model)
594        self.range.set_model(model)
595        self.fig.clear()

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.
def plot( self, **kwargs) -> (<class 'matplotlib.figure.Figure'>, <class 'matplotlib.axes._axes.Axes'>):
601    def plot(self, **kwargs) -> (plt.Figure, plt.Axes):
602        """
603        Plots the visual representation for the object.
604
605        Example
606        -------
607        ```
608        import Fumagalli_Motta_Tarantino_2020 as FMT20
609
610        model = FMT20.OptimalMergerPolicy()
611        visualizer = FMT20.Overview(m)
612        fig, ax = visualizer.plot()
613        # use the figure and axes as you wish, for example:
614        fig.show()
615        ```
616
617        Parameters
618        ----------
619        **kwargs
620            Options for further customization of the plots (Note: all subplots use the same kwargs).
621            - figure_title(str): Title for plot.<br>
622            - fontsize(int): Fontsize for model characteristics.<br>
623            - model_thresholds(bool): If true, the essential thresholds are shown in model characteristics.<br>
624            $\\Rightarrow$ see the included visualizations for further arguments.
625
626        Returns
627        -------
628        Figure
629            Containing the axes with the plots (use Figure.show() to display).
630        Axes
631            Containing the plots (arrange custom summary).
632        """
633        spec = self.fig.add_gridspec(ncols=2, nrows=2)
634        self._set_fig_title(**kwargs)
635        self.timeline = self._generate_visualizer(spec[1, 0], Timeline, **kwargs)
636        self.payoffs = self._generate_visualizer(spec[0, 1], Payoffs, **kwargs)
637        self.range = self._generate_visualizer(
638            spec[1, 1], self._get_merger_policy_asset_range_type(), **kwargs
639        )
640        self._generate_characteristics_ax(spec[0, 0], **kwargs)
641        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.Overview(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 (Note: all subplots use the same kwargs).
    • figure_title(str): Title for plot.
    • fontsize(int): Fontsize for model characteristics.
    • model_thresholds(bool): If true, the essential thresholds are shown in model characteristics.
      $\Rightarrow$ see the included visualizations for further arguments.
Returns
  • Figure: Containing the axes with the plots (use Figure.show() to display).
  • Axes: Containing the plots (arrange custom summary).