diff --git a/Shelegia_Motta_2021/IModel.py b/Shelegia_Motta_2021/IModel.py index 7e684ff..0fa1b61 100644 --- a/Shelegia_Motta_2021/IModel.py +++ b/Shelegia_Motta_2021/IModel.py @@ -9,8 +9,25 @@ class IModel: Interface for all models in Shelegia and Motta (2021). """ def __init__(self): - self.ENTRANT_CHOICES: Final[Dict[str, Dict[str, str]]] = {"product": {"complement": "C", "substitute": "S", "indifferent": "I"}, "development": {"success": "Y", "failure": "N"}} - self.INCUMBENT_CHOICES: Final[Dict[str, Dict[str, str]]] = {"copy_product": {"copy": "©", "refrain": "Ø"}} + self.ENTRANT_CHOICES: Final[Dict[str, str]] = {"complement": "C", "substitute": "S", "indifferent": "I"} + """ + Contains all the possible product choices of the entrant. + - complement (C) + - substitute (S) + - Indifferent (I) + """ + self.INCUMBENT_CHOICES: Final[Dict[str, str]] = {"copy": "©", "refrain": "Ø"} + """ + Contains all the possible answers of the incumbent to the choice of the entrant. + - copy (©) + - refrain (Ø) + """ + self.DEVELOPMENT_OUTCOME: Final[Dict[str, str]] = {"success": "Y", "failure": "N"} + """ + Contains all the possible outcomes of the development for the chosen product of the entrant or the merged entity. + - success (Y) + - failure (N) + """ @abc.abstractmethod def _calculate_copying_fixed_costs_values(self) -> Dict[str, float]: @@ -116,26 +133,34 @@ def get_utility_values(self) -> Dict[str, Dict[str, float]]: """ @abc.abstractmethod - def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: + def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: """ - Plots the best answers of the incumbent to all possible actions of the entrant. + Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant and fixed costs for copying of the incumbent. + + The output dictionary will contain the following details: + + - "entrant": choice of the entrant (possible choices listed in Shelegia_Motta_2021.IModel.IModel.ENTRANT_CHOICES)) + - "incumbent": choice of the incumbent (possible choices listed in Shelegia_Motta_2021.IModel.IModel.INCUMBENT_CHOICES) + - "development": outcome of the development (possible outcomes listed in Shelegia_Motta_2021.IModel.IModel.DEVELOPMENT_OUTCOME) Parameters ---------- - axis : matplotlib.axes.Axes - Axis to draw the plot on. (optional) + A : float + Assets of the entrant. + F : float + Fixed costs for copying of the incumbent. Returns ------- - matplotlib.axes.Axes - Axis containing the plot. + Dict[str, str] + Optimal choice of the entrant, the incumbent and the outcome of the development. """ pass @abc.abstractmethod - def plot_equilibrium(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: + def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: """ - Plots the equilibrium path based on the choices of the entrant and incumbent. + Plots the best answers of the incumbent to all possible actions of the entrant. Parameters ---------- @@ -150,9 +175,9 @@ def plot_equilibrium(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes pass @abc.abstractmethod - def plot_utilities(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: + def plot_equilibrium(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: """ - Plots the utilities for different market configurations. + Plots the equilibrium path based on the choices of the entrant and incumbent. Parameters ---------- @@ -167,20 +192,19 @@ def plot_utilities(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.A pass @abc.abstractmethod - def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: + def plot_utilities(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: """ - Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant ann fixed costs for copying of the incumbent. + Plots the utilities for different market configurations. Parameters ---------- - A : float - Assets of the entrant. - F : float - Fixed costs for copying of the incumbent. + axis : matplotlib.axes.Axes + Axis to draw the plot on. (optional) Returns ------- - Optimal choice of the entrant and the incumbent. + matplotlib.axes.Axes + Axis containing the plot. """ pass diff --git a/Shelegia_Motta_2021/ModelTest.py b/Shelegia_Motta_2021/ModelTest.py new file mode 100644 index 0000000..e495e60 --- /dev/null +++ b/Shelegia_Motta_2021/ModelTest.py @@ -0,0 +1,48 @@ +import unittest +from typing import Dict + +from Shelegia_Motta_2021.IModel import IModel +from Shelegia_Motta_2021.Models import BaseModel + + +class BaseModelTest(unittest.TestCase): + @staticmethod + def setUpModel() -> IModel: + return BaseModel() + + def setUp(self) -> None: + self.model: IModel = self.setUpModel() + self.copying_fixed_costs: Dict[str, float] = self.model.get_copying_fixed_costs_values() + self.assets: Dict[str, float] = self.model.get_asset_values() + self.utility: Dict[str, Dict[str, float]] = self.model.get_utility_values() + + def test_invalid_A1b(self): + self.assertRaises(AssertionError, BaseModel, small_delta=0.2) + self.assertRaises(AssertionError, BaseModel, delta=0.2) + + def test_invalid_A2(self): + self.assertRaises(AssertionError, BaseModel, K=0.3) + + def test_path_indifferent_copy(self): + choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*0.9, F=self.copying_fixed_costs["F(YN)c"]*0.9) + self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["indifferent"]) + self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["copy"]) + self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["failure"]) + + def test_path_kill_zone(self): + choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*0.9, F=self.copying_fixed_costs["F(YN)c"]*1.1) + self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["complement"]) + self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["refrain"]) + self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["success"]) + + def test_path_substitute_refrain(self): + choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*1.1, F=self.copying_fixed_costs["F(YN)c"]*1.1) + self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["substitute"]) + self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["refrain"]) + self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["success"]) + + def test_path_substitute_copy(self): + choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*1.1, F=self.copying_fixed_costs["F(YN)c"]*0.9) + self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["substitute"]) + self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["copy"]) + self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["success"]) diff --git a/Shelegia_Motta_2021/Models.py b/Shelegia_Motta_2021/Models.py index f395a22..c939b08 100644 --- a/Shelegia_Motta_2021/Models.py +++ b/Shelegia_Motta_2021/Models.py @@ -137,20 +137,20 @@ def get_utility_values(self) -> Dict[str, Dict[str, float]]: def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: result: Dict[str, str] = {"entrant": "", "incumbent": "", "development": ""} if self._copying_fixed_costs["F(YN)c"] <= F <= self._copying_fixed_costs["F(YN)s"] and A < self._assets["A-s"]: - result.update({"entrant": self.ENTRANT_CHOICES["product"]["complement"]}) - result.update({"incumbent": self.INCUMBENT_CHOICES["copy_product"]["refrain"]}) - result.update({"development": self.ENTRANT_CHOICES["development"]["success"]}) + result.update({"entrant": self.ENTRANT_CHOICES["complement"]}) + result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) + result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) elif F <= self._copying_fixed_costs["F(YN)c"] and A < self._assets["A-s"]: - result.update({"entrant": self.ENTRANT_CHOICES["product"]["indifferent"]}) - result.update({"incumbent": self.INCUMBENT_CHOICES["copy_product"]["copy"]}) - result.update({"development": self.ENTRANT_CHOICES["development"]["failure"]}) + result.update({"entrant": self.ENTRANT_CHOICES["indifferent"]}) + result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) + result.update({"development": self.DEVELOPMENT_OUTCOME["failure"]}) else: - result.update({"entrant": self.ENTRANT_CHOICES["product"]["substitute"]}) - result.update({"development": self.ENTRANT_CHOICES["development"]["success"]}) + result.update({"entrant": self.ENTRANT_CHOICES["substitute"]}) + result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) if F <= self._copying_fixed_costs["F(YY)s"]: - result.update({"incumbent": self.INCUMBENT_CHOICES["copy_product"]["copy"]}) + result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) else: - result.update({"incumbent": self.INCUMBENT_CHOICES["copy_product"]["refrain"]}) + result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) return result def _plot(self, coordinates: List[List[Tuple[float, float]]], labels: List[str], @@ -171,7 +171,7 @@ def _plot(self, coordinates: List[List[Tuple[float, float]]], labels: List[str], """ if axis is None: fig, axis = plt.subplots() - self._draw_horizontal_lines(axis) + self._draw_thresholds(axis) for i, coordinates in enumerate(coordinates): poly = plt.Polygon(coordinates, ec="k", color=self._get_color(i), label=labels[i]) @@ -191,8 +191,8 @@ def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None) -> matp def _create_choice_answer_label(self, entrant: Literal["complement", "substitute", "indifferent"], incumbent: Literal["copy", "refrain"], development: Literal["success", "failure"]) -> str: - return self.ENTRANT_CHOICES["product"][entrant] + " $\\rightarrow$ " + self.INCUMBENT_CHOICES["copy_product"][ - incumbent] + " $\\rightarrow$ " + self.ENTRANT_CHOICES["development"][development] + return self.ENTRANT_CHOICES[entrant] + " $\\rightarrow$ " + self.INCUMBENT_CHOICES[ + incumbent] + " $\\rightarrow$ " + self.DEVELOPMENT_OUTCOME[development] def _get_incumbent_best_answer_labels(self) -> List[str]: return [ @@ -297,7 +297,7 @@ def plot_utilities(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.A color='w', hatch='///', edgecolor=self._get_color(counter), - label=utility_type) + label=self._convert_utility_label(utility_type)) max_indices: List[int] = list( filter(lambda x: utility_values[x] == max(utility_values), range(len(utility_values)))) for max_index in max_indices: @@ -307,20 +307,46 @@ def plot_utilities(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.A axis.set_ylabel('Utility') axis.set_title('Utility levels for different Market Configurations') axis.set_xticks(index + 1.5 * (bar_width + spacing)) - axis.set_xticklabels(tuple(self._utility.keys())) - axis.legend() + axis.set_xticklabels(tuple([self._convert_market_configuration_label(i) for i in self._utility.keys()])) + axis.legend(bbox_to_anchor=(0, -0.3), loc="lower left", ncol=4) + axis.text(max(index) + 2 + 1.5 * (bar_width + spacing), self._utility["E(P)"]["W"]*0.5, self._get_market_configuration_annotations()) - BaseModel._set_axis(axis) + # BaseModel._set_axis(axis) plt.show() return axis + @staticmethod + def _get_market_configuration_annotations() -> str: + return "$I_P$: Primary product sold by the incumbent\n" \ + "$I_C$: Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$\n" \ + "$E_P$: Perfect substitute to $I_P$ potentially sold by the entrant\n" \ + "$E_C$: Complementary product to $I_P$ currently sold by the entrant\n" \ + "$\\tilde{E}_C$: Complementary product to $I_P$ potentially sold by the entrant\n" + + @staticmethod + def _convert_utility_label(raw_label: str) -> str: + label: str = raw_label.replace("pi", "$\pi$") + label = label.replace("CS", "Consumer Surplus") + label = label.replace("W", "Welfare") + return label + + @staticmethod + def _convert_market_configuration_label(raw_label: str) -> str: + labels: Dict[str] = {"basic": "$I_P;E_C$", + "I(C)": "$I_P+I_C;E_C$", + "E(P)": "$I_P;E_C+E_P$", + "I(C)E(P)": "$I_P+I_C;E_C+E_P$", + "E(C)": "$I_P;E_C+\\tilde{E}_C$", + "I(C)E(C)": "$I_P+I_C;E_C+\\tilde{E}_C$"} + return labels.get(raw_label, 'No valid market configuration') + def _get_x_max(self) -> float: return round(self._assets['A-c'] * 1.3, 1) def _get_y_max(self) -> float: return round(self._copying_fixed_costs['F(YN)s'] * 1.3, 1) - def _draw_horizontal_lines(self, axis: matplotlib.axes.Axes) -> None: + def _draw_thresholds(self, axis: matplotlib.axes.Axes) -> None: horizontal_line_x: float = self._get_x_max() + 0.05 vertical_line_y: float = self._get_y_max() + 0.15 axis.axhline(self._copying_fixed_costs['F(YN)s'], linestyle='--', color='k') diff --git a/docs/Shelegia_Motta_2021/IModel.html b/docs/Shelegia_Motta_2021/IModel.html index fa5e236..6f9fd15 100644 --- a/docs/Shelegia_Motta_2021/IModel.html +++ b/docs/Shelegia_Motta_2021/IModel.html @@ -53,6 +53,15 @@

API Documentation

  • IModel
  • +
  • + ENTRANT_CHOICES +
  • +
  • + INCUMBENT_CHOICES +
  • +
  • + DEVELOPMENT_OUTCOME +
  • get_asset_values
  • @@ -62,6 +71,9 @@

    API Documentation

  • get_utility_values
  • +
  • + get_optimal_choice +
  • plot_incumbent_best_answers
  • @@ -71,9 +83,6 @@

    API Documentation

  • plot_utilities
  • -
  • - get_optimal_choice -
  • @@ -97,7 +106,7 @@

    View Source
    import abc
    -from typing import Dict
    +from typing import Dict, Final
     
     import matplotlib.axes
     
    @@ -106,6 +115,27 @@ 

    """ Interface for all models in Shelegia and Motta (2021). """ + def __init__(self): + self.ENTRANT_CHOICES: Final[Dict[str, str]] = {"complement": "C", "substitute": "S", "indifferent": "I"} + """ + Contains all the possible product choices of the entrant. + - complement (C) + - substitute (S) + - Indifferent (I) + """ + self.INCUMBENT_CHOICES: Final[Dict[str, str]] = {"copy": "©", "refrain": "Ø"} + """ + Contains all the possible answers of the incumbent to the choice of the entrant. + - copy (©) + - refrain (Ø) + """ + self.DEVELOPMENT_OUTCOME: Final[Dict[str, str]] = {"success": "Y", "failure": "N"} + """ + Contains all the possible outcomes of the development for the chosen product of the entrant or the merged entity. + - success (Y) + - failure (N) + """ + @abc.abstractmethod def _calculate_copying_fixed_costs_values(self) -> Dict[str, float]: """ @@ -210,26 +240,34 @@

    """ @abc.abstractmethod - def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: + def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: """ - Plots the best answers of the incumbent to all possible actions of the entrant. + Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant and fixed costs for copying of the incumbent. + + The output dictionary will contain the following details: + + - "entrant": choice of the entrant (possible choices listed in Shelegia_Motta_2021.IModel.IModel.ENTRANT_CHOICES)) + - "incumbent": choice of the incumbent (possible choices listed in Shelegia_Motta_2021.IModel.IModel.INCUMBENT_CHOICES) + - "development": outcome of the development (possible outcomes listed in Shelegia_Motta_2021.IModel.IModel.DEVELOPMENT_OUTCOME) Parameters ---------- - axis : matplotlib.axes.Axes - Axis to draw the plot on. (optional) + A : float + Assets of the entrant. + F : float + Fixed costs for copying of the incumbent. Returns ------- - matplotlib.axes.Axes - Axis containing the plot. + Dict[str, str] + Optimal choice of the entrant, the incumbent and the outcome of the development. """ pass @abc.abstractmethod - def plot_equilibrium(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: + def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: """ - Plots the equilibrium path based on the choices of the entrant and incumbent. + Plots the best answers of the incumbent to all possible actions of the entrant. Parameters ---------- @@ -244,9 +282,9 @@

    pass @abc.abstractmethod - def plot_utilities(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: + def plot_equilibrium(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: """ - Plots the utilities for different market configurations. + Plots the equilibrium path based on the choices of the entrant and incumbent. Parameters ---------- @@ -261,20 +299,19 @@

    pass @abc.abstractmethod - def get_optimal_choice(self, A: float, F: float): + def plot_utilities(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: """ - Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant ann fixed costs for copying of the incumbent. + Plots the utilities for different market configurations. Parameters ---------- - A : float - Assets of the entrant. - F : float - Fixed costs for copying of the incumbent. + axis : matplotlib.axes.Axes + Axis to draw the plot on. (optional) Returns ------- - Optimal choice of the entrant and the incumbent. + matplotlib.axes.Axes + Axis containing the plot. """ pass @@ -314,6 +351,27 @@

    """ Interface for all models in Shelegia and Motta (2021). """ + def __init__(self): + self.ENTRANT_CHOICES: Final[Dict[str, str]] = {"complement": "C", "substitute": "S", "indifferent": "I"} + """ + Contains all the possible product choices of the entrant. + - complement (C) + - substitute (S) + - Indifferent (I) + """ + self.INCUMBENT_CHOICES: Final[Dict[str, str]] = {"copy": "©", "refrain": "Ø"} + """ + Contains all the possible answers of the incumbent to the choice of the entrant. + - copy (©) + - refrain (Ø) + """ + self.DEVELOPMENT_OUTCOME: Final[Dict[str, str]] = {"success": "Y", "failure": "N"} + """ + Contains all the possible outcomes of the development for the chosen product of the entrant or the merged entity. + - success (Y) + - failure (N) + """ + @abc.abstractmethod def _calculate_copying_fixed_costs_values(self) -> Dict[str, float]: """ @@ -418,26 +476,34 @@

    """ @abc.abstractmethod - def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: + def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: """ - Plots the best answers of the incumbent to all possible actions of the entrant. + Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant and fixed costs for copying of the incumbent. + + The output dictionary will contain the following details: + + - "entrant": choice of the entrant (possible choices listed in Shelegia_Motta_2021.IModel.IModel.ENTRANT_CHOICES)) + - "incumbent": choice of the incumbent (possible choices listed in Shelegia_Motta_2021.IModel.IModel.INCUMBENT_CHOICES) + - "development": outcome of the development (possible outcomes listed in Shelegia_Motta_2021.IModel.IModel.DEVELOPMENT_OUTCOME) Parameters ---------- - axis : matplotlib.axes.Axes - Axis to draw the plot on. (optional) + A : float + Assets of the entrant. + F : float + Fixed costs for copying of the incumbent. Returns ------- - matplotlib.axes.Axes - Axis containing the plot. + Dict[str, str] + Optimal choice of the entrant, the incumbent and the outcome of the development. """ pass @abc.abstractmethod - def plot_equilibrium(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: + def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: """ - Plots the equilibrium path based on the choices of the entrant and incumbent. + Plots the best answers of the incumbent to all possible actions of the entrant. Parameters ---------- @@ -452,9 +518,9 @@

    pass @abc.abstractmethod - def plot_utilities(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: + def plot_equilibrium(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: """ - Plots the utilities for different market configurations. + Plots the equilibrium path based on the choices of the entrant and incumbent. Parameters ---------- @@ -469,20 +535,19 @@

    pass @abc.abstractmethod - def get_optimal_choice(self, A: float, F: float): + def plot_utilities(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: """ - Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant ann fixed costs for copying of the incumbent. + Plots the utilities for different market configurations. Parameters ---------- - A : float - Assets of the entrant. - F : float - Fixed costs for copying of the incumbent. + axis : matplotlib.axes.Axes + Axis to draw the plot on. (optional) Returns ------- - Optimal choice of the entrant and the incumbent. + matplotlib.axes.Axes + Axis containing the plot. """ pass @@ -517,9 +582,83 @@

    IModel()

    - +
    + View Source +
        def __init__(self):
    +        self.ENTRANT_CHOICES: Final[Dict[str, str]] = {"complement": "C", "substitute": "S", "indifferent": "I"}
    +        """
    +        Contains all the possible product choices of the entrant.
    +        - complement (C)
    +        - substitute (S)
    +        - Indifferent (I)
    +        """
    +        self.INCUMBENT_CHOICES: Final[Dict[str, str]] = {"copy": "©", "refrain": "Ø"}
    +        """
    +        Contains all the possible answers of the incumbent to the choice of the entrant.
    +        - copy (©)
    +        - refrain (Ø)
    +        """
    +        self.DEVELOPMENT_OUTCOME: Final[Dict[str, str]] = {"success": "Y", "failure": "N"}
    +        """
    +        Contains all the possible outcomes of the development for the chosen product of the entrant or the merged entity.
    +        - success (Y)
    +        - failure (N)
    +        """
    +
    + +
    + + +
    +
    #   + + ENTRANT_CHOICES: Final[Dict[str, str]] +
    + +

    Contains all the possible product choices of the entrant.

    + +
      +
    • complement (C)
    • +
    • substitute (S)
    • +
    • Indifferent (I)
    • +
    +
    + + +
    +
    +
    #   + + INCUMBENT_CHOICES: Final[Dict[str, str]] +
    + +

    Contains all the possible answers of the incumbent to the choice of the entrant.

    + +
      +
    • copy (©)
    • +
    • refrain (Ø)
    • +
    +
    + + +
    +
    +
    #   + + DEVELOPMENT_OUTCOME: Final[Dict[str, str]] +
    + +

    Contains all the possible outcomes of the development for the chosen product of the entrant or the merged entity.

    + +
      +
    • success (Y)
    • +
    • failure (N)
    • +
    +
    + +
    #   @@ -728,65 +867,80 @@
    Returns
    -
    -
    #   +
    +
    #  
    @abc.abstractmethod
    def - plot_incumbent_best_answers( - self, - axis: matplotlib.axes._axes.Axes = None -) -> matplotlib.axes._axes.Axes: + get_optimal_choice(self, A: float, F: float) -> Dict[str, str]:
    View Source
        @abc.abstractmethod
    -    def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes:
    +    def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]:
             """
    -        Plots the best answers of the incumbent to all possible actions of the entrant.
    +        Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant and fixed costs for copying of the incumbent.
    +
    +        The output dictionary will contain the following details:
    +
    +        - "entrant": choice of the entrant (possible choices listed in Shelegia_Motta_2021.IModel.IModel.ENTRANT_CHOICES))
    +        - "incumbent": choice of the incumbent (possible choices listed in Shelegia_Motta_2021.IModel.IModel.INCUMBENT_CHOICES)
    +        - "development": outcome of the development (possible outcomes listed in Shelegia_Motta_2021.IModel.IModel.DEVELOPMENT_OUTCOME)
     
             Parameters
             ----------
    -        axis : matplotlib.axes.Axes
    -            Axis to draw the plot on. (optional)
    +        A : float
    +            Assets of the entrant.
    +        F : float
    +            Fixed costs for copying of the incumbent.
     
             Returns
             -------
    -        matplotlib.axes.Axes
    -            Axis containing the plot.
    +        Dict[str, str]
    +            Optimal choice of the entrant, the incumbent and the outcome of the development.
             """
             pass
     
    -

    Plots the best answers of the incumbent to all possible actions of the entrant.

    +

    Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant and fixed costs for copying of the incumbent.

    + +

    The output dictionary will contain the following details:

    + +
    Parameters
      -
    • axis (matplotlib.axes.Axes): -Axis to draw the plot on. (optional)
    • +
    • A (float): +Assets of the entrant.
    • +
    • F (float): +Fixed costs for copying of the incumbent.
    Returns
      -
    • matplotlib.axes.Axes: Axis containing the plot.
    • +
    • Dict[str, str]: Optimal choice of the entrant, the incumbent and the outcome of the development.
    -
    -
    #   +
    +
    #  
    @abc.abstractmethod
    def - plot_equilibrium( + plot_incumbent_best_answers( self, axis: matplotlib.axes._axes.Axes = None ) -> matplotlib.axes._axes.Axes: @@ -795,9 +949,9 @@
    Returns
    View Source
        @abc.abstractmethod
    -    def plot_equilibrium(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes:
    +    def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes:
             """
    -        Plots the equilibrium path based on the choices of the entrant and incumbent.
    +        Plots the best answers of the incumbent to all possible actions of the entrant.
     
             Parameters
             ----------
    @@ -814,7 +968,7 @@ 
    Returns
    -

    Plots the equilibrium path based on the choices of the entrant and incumbent.

    +

    Plots the best answers of the incumbent to all possible actions of the entrant.

    Parameters
    @@ -832,13 +986,13 @@
    Returns
    -
    -
    #   +
    +
    #  
    @abc.abstractmethod
    def - plot_utilities( + plot_equilibrium( self, axis: matplotlib.axes._axes.Axes = None ) -> matplotlib.axes._axes.Axes: @@ -847,9 +1001,9 @@
    Returns
    View Source
        @abc.abstractmethod
    -    def plot_utilities(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes:
    +    def plot_equilibrium(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes:
             """
    -        Plots the utilities for different market configurations.
    +        Plots the equilibrium path based on the choices of the entrant and incumbent.
     
             Parameters
             ----------
    @@ -866,7 +1020,7 @@ 
    Returns
    -

    Plots the utilities for different market configurations.

    +

    Plots the equilibrium path based on the choices of the entrant and incumbent.

    Parameters
    @@ -884,53 +1038,53 @@
    Returns
    -
    -
    #   +
    +
    #  
    @abc.abstractmethod
    def - get_optimal_choice(self, A: float, F: float): + plot_utilities( + self, + axis: matplotlib.axes._axes.Axes = None +) -> matplotlib.axes._axes.Axes:
    View Source
        @abc.abstractmethod
    -    def get_optimal_choice(self, A: float, F: float):
    +    def plot_utilities(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes:
             """
    -        Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant ann fixed costs for copying of the incumbent.
    +        Plots the utilities for different market configurations.
     
             Parameters
             ----------
    -        A : float
    -            Assets of the entrant.
    -        F : float
    -            Fixed costs for copying of the incumbent.
    +        axis : matplotlib.axes.Axes
    +            Axis to draw the plot on. (optional)
     
             Returns
             -------
    -        Optimal choice of the entrant and the incumbent.
    +        matplotlib.axes.Axes
    +            Axis containing the plot.
             """
             pass
     
    -

    Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant ann fixed costs for copying of the incumbent.

    +

    Plots the utilities for different market configurations.

    Parameters
      -
    • A (float): -Assets of the entrant.
    • -
    • F (float): -Fixed costs for copying of the incumbent.
    • +
    • axis (matplotlib.axes.Axes): +Axis to draw the plot on. (optional)
    Returns
      -
    • Optimal choice of the entrant and the incumbent.
    • +
    • matplotlib.axes.Axes: Axis containing the plot.
    diff --git a/docs/Shelegia_Motta_2021/ModelTest.html b/docs/Shelegia_Motta_2021/ModelTest.html new file mode 100644 index 0000000..9839d20 --- /dev/null +++ b/docs/Shelegia_Motta_2021/ModelTest.html @@ -0,0 +1,663 @@ + + + + + + + Shelegia_Motta_2021.ModelTest API documentation + + + + + + + + + + + +
    +
    +

    +Shelegia_Motta_2021.ModelTest

    + + +
    + View Source +
    import unittest
    +from typing import Dict
    +
    +from Shelegia_Motta_2021.IModel import IModel
    +from Shelegia_Motta_2021.Models import BaseModel
    +
    +
    +class BaseModelTest(unittest.TestCase):
    +    @staticmethod
    +    def setUpModel() -> IModel:
    +        return BaseModel()
    +
    +    def setUp(self) -> None:
    +        self.model: IModel = self.setUpModel()
    +        self.copying_fixed_costs: Dict[str, float] = self.model.get_copying_fixed_costs_values()
    +        self.assets: Dict[str, float] = self.model.get_asset_values()
    +        self.utility: Dict[str, Dict[str, float]] = self.model.get_utility_values()
    +
    +    def test_invalid_A1b(self):
    +        self.assertRaises(AssertionError, BaseModel, small_delta=0.2)
    +        self.assertRaises(AssertionError, BaseModel, delta=0.2)
    +
    +    def test_invalid_A2(self):
    +        self.assertRaises(AssertionError, BaseModel, K=0.3)
    +
    +    def test_path_indifferent_copy(self):
    +        choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*0.9, F=self.copying_fixed_costs["F(YN)c"]*0.9)
    +        self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["indifferent"])
    +        self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["copy"])
    +        self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["failure"])
    +
    +    def test_path_kill_zone(self):
    +        choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*0.9, F=self.copying_fixed_costs["F(YN)c"]*1.1)
    +        self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["complement"])
    +        self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["refrain"])
    +        self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["success"])
    +
    +    def test_path_substitute_refrain(self):
    +        choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*1.1, F=self.copying_fixed_costs["F(YN)c"]*1.1)
    +        self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["substitute"])
    +        self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["refrain"])
    +        self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["success"])
    +
    +    def test_path_substitute_copy(self):
    +        choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*1.1, F=self.copying_fixed_costs["F(YN)c"]*0.9)
    +        self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["substitute"])
    +        self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["copy"])
    +        self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["success"])
    +
    + +
    + +
    +
    +
    + #   + + + class + BaseModelTest(unittest.case.TestCase): +
    + +
    + View Source +
    class BaseModelTest(unittest.TestCase):
    +    @staticmethod
    +    def setUpModel() -> IModel:
    +        return BaseModel()
    +
    +    def setUp(self) -> None:
    +        self.model: IModel = self.setUpModel()
    +        self.copying_fixed_costs: Dict[str, float] = self.model.get_copying_fixed_costs_values()
    +        self.assets: Dict[str, float] = self.model.get_asset_values()
    +        self.utility: Dict[str, Dict[str, float]] = self.model.get_utility_values()
    +
    +    def test_invalid_A1b(self):
    +        self.assertRaises(AssertionError, BaseModel, small_delta=0.2)
    +        self.assertRaises(AssertionError, BaseModel, delta=0.2)
    +
    +    def test_invalid_A2(self):
    +        self.assertRaises(AssertionError, BaseModel, K=0.3)
    +
    +    def test_path_indifferent_copy(self):
    +        choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*0.9, F=self.copying_fixed_costs["F(YN)c"]*0.9)
    +        self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["indifferent"])
    +        self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["copy"])
    +        self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["failure"])
    +
    +    def test_path_kill_zone(self):
    +        choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*0.9, F=self.copying_fixed_costs["F(YN)c"]*1.1)
    +        self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["complement"])
    +        self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["refrain"])
    +        self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["success"])
    +
    +    def test_path_substitute_refrain(self):
    +        choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*1.1, F=self.copying_fixed_costs["F(YN)c"]*1.1)
    +        self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["substitute"])
    +        self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["refrain"])
    +        self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["success"])
    +
    +    def test_path_substitute_copy(self):
    +        choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*1.1, F=self.copying_fixed_costs["F(YN)c"]*0.9)
    +        self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["substitute"])
    +        self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["copy"])
    +        self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["success"])
    +
    + +
    + +

    A class whose instances are single test cases.

    + +

    By default, the test code itself should be placed in a method named +'runTest'.

    + +

    If the fixture may be used for many test cases, create as +many test methods as are needed. When instantiating such a TestCase +subclass, specify in the constructor arguments the name of the test method +that the instance is to execute.

    + +

    Test authors should subclass TestCase for their own tests. Construction +and deconstruction of the test's environment ('fixture') can be +implemented by overriding the 'setUp' and 'tearDown' methods respectively.

    + +

    If it is necessary to override the __init__ method, the base class +__init__ method must always be called. It is important that subclasses +should not change the signature of their __init__ method, since instances +of the classes are instantiated automatically by parts of the framework +in order to be run.

    + +

    When subclassing TestCase, you can set these attributes:

    + +
      +
    • failureException: determines which exception will be raised when +the instance's assertion methods fail; test methods raising this +exception will be deemed to have 'failed' rather than 'errored'.
    • +
    • longMessage: determines whether long messages (including repr of +objects used in assert methods) will be printed on failure in addition +to any explicit message passed.
    • +
    • maxDiff: sets the maximum length of a diff in failure messages +by assert methods using difflib. It is looked up as an instance +attribute so can be configured by individual tests if required.
    • +
    +
    + + +
    +
    #   + +
    @staticmethod
    + + def + setUpModel() -> Shelegia_Motta_2021.IModel.IModel: +
    + +
    + View Source +
        @staticmethod
    +    def setUpModel() -> IModel:
    +        return BaseModel()
    +
    + +
    + + + +
    +
    +
    #   + + + def + setUp(self) -> None: +
    + +
    + View Source +
        def setUp(self) -> None:
    +        self.model: IModel = self.setUpModel()
    +        self.copying_fixed_costs: Dict[str, float] = self.model.get_copying_fixed_costs_values()
    +        self.assets: Dict[str, float] = self.model.get_asset_values()
    +        self.utility: Dict[str, Dict[str, float]] = self.model.get_utility_values()
    +
    + +
    + +

    Hook method for setting up the test fixture before exercising it.

    +
    + + +
    +
    +
    #   + + + def + test_invalid_A1b(self): +
    + +
    + View Source +
        def test_invalid_A1b(self):
    +        self.assertRaises(AssertionError, BaseModel, small_delta=0.2)
    +        self.assertRaises(AssertionError, BaseModel, delta=0.2)
    +
    + +
    + + + +
    +
    +
    #   + + + def + test_invalid_A2(self): +
    + +
    + View Source +
        def test_invalid_A2(self):
    +        self.assertRaises(AssertionError, BaseModel, K=0.3)
    +
    + +
    + + + +
    +
    +
    #   + + + def + test_path_indifferent_copy(self): +
    + +
    + View Source +
        def test_path_indifferent_copy(self):
    +        choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*0.9, F=self.copying_fixed_costs["F(YN)c"]*0.9)
    +        self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["indifferent"])
    +        self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["copy"])
    +        self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["failure"])
    +
    + +
    + + + +
    +
    +
    #   + + + def + test_path_kill_zone(self): +
    + +
    + View Source +
        def test_path_kill_zone(self):
    +        choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*0.9, F=self.copying_fixed_costs["F(YN)c"]*1.1)
    +        self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["complement"])
    +        self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["refrain"])
    +        self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["success"])
    +
    + +
    + + + +
    +
    +
    #   + + + def + test_path_substitute_refrain(self): +
    + +
    + View Source +
        def test_path_substitute_refrain(self):
    +        choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*1.1, F=self.copying_fixed_costs["F(YN)c"]*1.1)
    +        self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["substitute"])
    +        self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["refrain"])
    +        self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["success"])
    +
    + +
    + + + +
    +
    +
    #   + + + def + test_path_substitute_copy(self): +
    + +
    + View Source +
        def test_path_substitute_copy(self):
    +        choice: Dict[str, str] = self.model.get_optimal_choice(A=self.assets["A-s"]*1.1, F=self.copying_fixed_costs["F(YN)c"]*0.9)
    +        self.assertEqual(choice["entrant"], self.model.ENTRANT_CHOICES["substitute"])
    +        self.assertEqual(choice["incumbent"], self.model.INCUMBENT_CHOICES["copy"])
    +        self.assertEqual(choice["development"], self.model.DEVELOPMENT_OUTCOME["success"])
    +
    + +
    + + + +
    +
    +
    Inherited Members
    +
    +
    unittest.case.TestCase
    +
    TestCase
    +
    failureException
    +
    longMessage
    +
    maxDiff
    +
    addTypeEqualityFunc
    +
    addCleanup
    +
    addClassCleanup
    +
    tearDown
    +
    setUpClass
    +
    tearDownClass
    +
    countTestCases
    +
    defaultTestResult
    +
    shortDescription
    +
    id
    +
    subTest
    +
    run
    +
    doCleanups
    +
    doClassCleanups
    +
    debug
    +
    skipTest
    +
    fail
    +
    assertFalse
    +
    assertTrue
    +
    assertRaises
    +
    assertWarns
    +
    assertLogs
    +
    assertEqual
    +
    assertNotEqual
    +
    assertAlmostEqual
    +
    assertNotAlmostEqual
    +
    assertSequenceEqual
    +
    assertListEqual
    +
    assertTupleEqual
    +
    assertSetEqual
    +
    assertIn
    +
    assertNotIn
    +
    assertIs
    +
    assertIsNot
    +
    assertDictEqual
    +
    assertDictContainsSubset
    +
    assertCountEqual
    +
    assertMultiLineEqual
    +
    assertLess
    +
    assertLessEqual
    +
    assertGreater
    +
    assertGreaterEqual
    +
    assertIsNone
    +
    assertIsNotNone
    +
    assertIsInstance
    +
    assertNotIsInstance
    +
    assertRaisesRegex
    +
    assertWarnsRegex
    +
    assertRegex
    +
    assertNotRegex
    +
    failUnlessRaises
    +
    failIf
    +
    assertRaisesRegexp
    +
    assertRegexpMatches
    +
    assertNotRegexpMatches
    +
    failUnlessEqual
    +
    assertEquals
    +
    failIfEqual
    +
    assertNotEquals
    +
    failUnlessAlmostEqual
    +
    assertAlmostEquals
    +
    failIfAlmostEqual
    +
    assertNotAlmostEquals
    +
    failUnless
    +
    assert_
    + +
    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/docs/Shelegia_Motta_2021/Models.html b/docs/Shelegia_Motta_2021/Models.html index 9800fcf..6d1e75f 100644 --- a/docs/Shelegia_Motta_2021/Models.html +++ b/docs/Shelegia_Motta_2021/Models.html @@ -120,10 +120,11 @@

    View Source -
    from typing import Dict, List, Tuple
    +            
    from typing import Dict, List, Tuple, Literal
     
     import matplotlib.axes
     import matplotlib.pyplot as plt
    +plt.rcParams["font.family"] = "monospace"
     from numpy import arange
     
     import Shelegia_Motta_2021
    @@ -159,10 +160,11 @@ 

    small_delta : float ($\delta$) Additional utility gained from from a complement combined with a primary product. delta : float - ($\Delta$) Additional utility gained from the primary product of the entrant compared to the primary product of the incumbent. + ($\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent. K : float - Investment costs to develop a second product for the entrant. + Investment costs for the entrant to develop a second product. """ + super(BaseModel, self).__init__() assert small_delta / 2 < delta < 3 * small_delta / 2, "(A1b) not satisfied." assert K < small_delta / 2, "(A2) not satisfied." self._u: float = u @@ -254,10 +256,27 @@

    def get_utility_values(self) -> Dict[str, Dict[str, float]]: return self._utility - def get_optimal_choice(self, A: float, F: float): - pass - - def _plot(self, coordinates: List[List[Tuple[float, float]]], axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: + def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: + result: Dict[str, str] = {"entrant": "", "incumbent": "", "development": ""} + if self._copying_fixed_costs["F(YN)c"] <= F <= self._copying_fixed_costs["F(YN)s"] and A < self._assets["A-s"]: + result.update({"entrant": self.ENTRANT_CHOICES["complement"]}) + result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) + result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) + elif F <= self._copying_fixed_costs["F(YN)c"] and A < self._assets["A-s"]: + result.update({"entrant": self.ENTRANT_CHOICES["indifferent"]}) + result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) + result.update({"development": self.DEVELOPMENT_OUTCOME["failure"]}) + else: + result.update({"entrant": self.ENTRANT_CHOICES["substitute"]}) + result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) + if F <= self._copying_fixed_costs["F(YY)s"]: + result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) + else: + result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) + return result + + def _plot(self, coordinates: List[List[Tuple[float, float]]], labels: List[str], + axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: """ Plots polygons containing the optimal choices and answers into a coordinate system. @@ -274,36 +293,72 @@

    """ if axis is None: fig, axis = plt.subplots() - self._draw_horizontal_lines(axis) + self._draw_thresholds(axis) for i, coordinates in enumerate(coordinates): - poly = plt.Polygon(coordinates, ec="k", color=self._get_color(i)) + poly = plt.Polygon(coordinates, ec="k", color=self._get_color(i), label=labels[i]) axis.add_patch(poly) + axis.legend(bbox_to_anchor=(1.15, 1), loc="upper left") BaseModel._set_axis(axis) plt.show() return axis def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: poly_coordinates: List[List[Tuple[float, float]]] = self._get_incumbent_best_answer_coordinates() - axis: matplotlib.axes.Axes = self._plot(coordinates=poly_coordinates, axis=axis) + poly_labels: List[str] = self._get_incumbent_best_answer_labels() + axis: matplotlib.axes.Axes = self._plot(coordinates=poly_coordinates, labels=poly_labels, axis=axis) return axis + def _create_choice_answer_label(self, entrant: Literal["complement", "substitute", "indifferent"], + incumbent: Literal["copy", "refrain"], + development: Literal["success", "failure"]) -> str: + return self.ENTRANT_CHOICES[entrant] + " $\\rightarrow$ " + self.INCUMBENT_CHOICES[ + incumbent] + " $\\rightarrow$ " + self.DEVELOPMENT_OUTCOME[development] + + def _get_incumbent_best_answer_labels(self) -> List[str]: + return [ + # Square 1 + self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="failure") + " \n" + + self._create_choice_answer_label(entrant="complement", incumbent="copy", development="failure"), + # Square 2 + self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="failure") + " \n" + + self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success"), + # Square 3 + self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success") + " \n" + + self._create_choice_answer_label(entrant="complement", incumbent="copy", development="failure"), + # Square 4 + self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success") + " \n" + + self._create_choice_answer_label(entrant="complement", incumbent="copy", development="success"), + # Square 5 + self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") + " \n" + + self._create_choice_answer_label(entrant="complement", incumbent="copy", development="success"), + # Square 6 + self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") + " \n" + + self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success"), + ] + def _get_incumbent_best_answer_coordinates(self) -> List[List[Tuple[float, float]]]: y_max: float = self._get_y_max() x_max: float = self._get_x_max() return [ + # Square 1 [(0, 0), (self._assets['A-s'], 0), (self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (0, self._copying_fixed_costs['F(YY)s'])], + # Square 2 [(0, self._copying_fixed_costs['F(YY)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YN)s']), (0, self._copying_fixed_costs['F(YN)s'])], + # Square 3 [(self._assets['A-s'], 0), (self._assets['A-c'], 0), (self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YY)s'])], + # Square 4 [(self._assets['A-c'], 0), (x_max, 0), (x_max, self._copying_fixed_costs['F(YY)s']), (self._assets['A-c'], self._copying_fixed_costs['F(YY)s'])], + # Square 5 [(self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), (x_max, self._copying_fixed_costs['F(YY)s']), (x_max, self._copying_fixed_costs['F(YY)c']), (self._assets['A-c'], self._copying_fixed_costs['F(YY)c'])], + # Square 6 [(self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), (self._assets['A-c'], self._copying_fixed_costs['F(YY)c']), (x_max, self._copying_fixed_costs['F(YY)c']), @@ -312,21 +367,36 @@

    def plot_equilibrium(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: poly_coordinates: List[List[Tuple[float, float]]] = self._get_equilibrium_coordinates() - axis: matplotlib.axes.Axes = self._plot(coordinates=poly_coordinates, axis=axis) + poly_labels: List[str] = self._get_equilibrium_labels() + axis: matplotlib.axes.Axes = self._plot(coordinates=poly_coordinates, labels=poly_labels, axis=axis) return axis + def _get_equilibrium_labels(self) -> List[str]: + return [ + # Square 1 + self._create_choice_answer_label(entrant="indifferent", incumbent="copy", development="failure"), + # Square 2 + self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success"), + # Square 3 + self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success"), + # Square 4 + self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") + ] + def _get_equilibrium_coordinates(self) -> List[List[Tuple[float, float]]]: y_max: float = self._get_y_max() x_max: float = self._get_x_max() return [ + # Square 1 [(0, 0), (self._assets['A-s'], 0), (self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (0, self._copying_fixed_costs['F(YY)s'])], + # Square 2 [(0, self._copying_fixed_costs['F(YY)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YN)s']), (0, self._copying_fixed_costs['F(YN)s'])], + # Square 3 [(self._assets['A-s'], 0), (x_max, 0), (x_max, self._copying_fixed_costs['F(YY)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YY)s'])], - [(self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), (x_max, self._copying_fixed_costs['F(YY)s']), - (x_max, self._copying_fixed_costs['F(YY)c']), (self._assets['A-c'], self._copying_fixed_costs['F(YY)c'])], + # Square 4 [(self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (x_max, self._copying_fixed_costs['F(YY)s']), (x_max, y_max), (0, y_max), (0, self._copying_fixed_costs['F(YN)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YN)s'])]] @@ -347,8 +417,9 @@

    bars = axis.bar(index + counter * (bar_width + spacing), utility_values, bar_width, alpha=opacity, color='w', + hatch='///', edgecolor=self._get_color(counter), - label=utility_type) + label=self._convert_utility_label(utility_type)) max_indices: List[int] = list( filter(lambda x: utility_values[x] == max(utility_values), range(len(utility_values)))) for max_index in max_indices: @@ -358,22 +429,48 @@

    axis.set_ylabel('Utility') axis.set_title('Utility levels for different Market Configurations') axis.set_xticks(index + 1.5 * (bar_width + spacing)) - axis.set_xticklabels(tuple(self._utility.keys())) - axis.legend() + axis.set_xticklabels(tuple([self._convert_market_configuration_label(i) for i in self._utility.keys()])) + axis.legend(bbox_to_anchor=(0, -0.3), loc="lower left", ncol=4) + axis.text(max(index) + 2 + 1.5 * (bar_width + spacing), self._utility["E(P)"]["W"]*0.5, self._get_market_configuration_annotations()) - BaseModel._set_axis(axis) + # BaseModel._set_axis(axis) plt.show() return axis + @staticmethod + def _get_market_configuration_annotations() -> str: + return "$I_P$: Primary product sold by the incumbent\n" \ + "$I_C$: Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$\n" \ + "$E_P$: Perfect substitute to $I_P$ potentially sold by the entrant\n" \ + "$E_C$: Complementary product to $I_P$ currently sold by the entrant\n" \ + "$\\tilde{E}_C$: Complementary product to $I_P$ potentially sold by the entrant\n" + + @staticmethod + def _convert_utility_label(raw_label: str) -> str: + label: str = raw_label.replace("pi", "$\pi$") + label = label.replace("CS", "Consumer Surplus") + label = label.replace("W", "Welfare") + return label + + @staticmethod + def _convert_market_configuration_label(raw_label: str) -> str: + labels: Dict[str] = {"basic": "$I_P;E_C$", + "I(C)": "$I_P+I_C;E_C$", + "E(P)": "$I_P;E_C+E_P$", + "I(C)E(P)": "$I_P+I_C;E_C+E_P$", + "E(C)": "$I_P;E_C+\\tilde{E}_C$", + "I(C)E(C)": "$I_P+I_C;E_C+\\tilde{E}_C$"} + return labels.get(raw_label, 'No valid market configuration') + def _get_x_max(self) -> float: return round(self._assets['A-c'] * 1.3, 1) def _get_y_max(self) -> float: return round(self._copying_fixed_costs['F(YN)s'] * 1.3, 1) - def _draw_horizontal_lines(self, axis: matplotlib.axes.Axes) -> None: - horizontal_line_x: float = self._get_x_max() + 0.15 - vertical_line_y: float = self._get_y_max() + 0.25 + def _draw_thresholds(self, axis: matplotlib.axes.Axes) -> None: + horizontal_line_x: float = self._get_x_max() + 0.05 + vertical_line_y: float = self._get_y_max() + 0.15 axis.axhline(self._copying_fixed_costs['F(YN)s'], linestyle='--', color='k') axis.text(horizontal_line_x, self._copying_fixed_costs['F(YN)s'], r'$F^{YN}_S$') axis.axhline(self._copying_fixed_costs['F(YY)s'], linestyle='--', color='k') @@ -391,9 +488,8 @@

    @staticmethod def _set_axis(axis: matplotlib.axes.Axes): - axis.margins(x=0.2, y=0.1) - axis.relim() axis.autoscale_view() + axis.figure.tight_layout() def __str__(self) -> str: str_representation: str = 'Assets:' @@ -446,6 +542,7 @@

    base_model = BaseModel() base_model.plot_equilibrium() base_model.plot_incumbent_best_answers() + base_model.plot_utilities() print(base_model)

    @@ -493,10 +590,11 @@

    small_delta : float ($\delta$) Additional utility gained from from a complement combined with a primary product. delta : float - ($\Delta$) Additional utility gained from the primary product of the entrant compared to the primary product of the incumbent. + ($\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent. K : float - Investment costs to develop a second product for the entrant. + Investment costs for the entrant to develop a second product. """ + super(BaseModel, self).__init__() assert small_delta / 2 < delta < 3 * small_delta / 2, "(A1b) not satisfied." assert K < small_delta / 2, "(A2) not satisfied." self._u: float = u @@ -588,10 +686,27 @@

    def get_utility_values(self) -> Dict[str, Dict[str, float]]: return self._utility - def get_optimal_choice(self, A: float, F: float): - pass - - def _plot(self, coordinates: List[List[Tuple[float, float]]], axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: + def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: + result: Dict[str, str] = {"entrant": "", "incumbent": "", "development": ""} + if self._copying_fixed_costs["F(YN)c"] <= F <= self._copying_fixed_costs["F(YN)s"] and A < self._assets["A-s"]: + result.update({"entrant": self.ENTRANT_CHOICES["complement"]}) + result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) + result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) + elif F <= self._copying_fixed_costs["F(YN)c"] and A < self._assets["A-s"]: + result.update({"entrant": self.ENTRANT_CHOICES["indifferent"]}) + result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) + result.update({"development": self.DEVELOPMENT_OUTCOME["failure"]}) + else: + result.update({"entrant": self.ENTRANT_CHOICES["substitute"]}) + result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) + if F <= self._copying_fixed_costs["F(YY)s"]: + result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) + else: + result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) + return result + + def _plot(self, coordinates: List[List[Tuple[float, float]]], labels: List[str], + axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: """ Plots polygons containing the optimal choices and answers into a coordinate system. @@ -608,36 +723,72 @@

    """ if axis is None: fig, axis = plt.subplots() - self._draw_horizontal_lines(axis) + self._draw_thresholds(axis) for i, coordinates in enumerate(coordinates): - poly = plt.Polygon(coordinates, ec="k", color=self._get_color(i)) + poly = plt.Polygon(coordinates, ec="k", color=self._get_color(i), label=labels[i]) axis.add_patch(poly) + axis.legend(bbox_to_anchor=(1.15, 1), loc="upper left") BaseModel._set_axis(axis) plt.show() return axis def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: poly_coordinates: List[List[Tuple[float, float]]] = self._get_incumbent_best_answer_coordinates() - axis: matplotlib.axes.Axes = self._plot(coordinates=poly_coordinates, axis=axis) + poly_labels: List[str] = self._get_incumbent_best_answer_labels() + axis: matplotlib.axes.Axes = self._plot(coordinates=poly_coordinates, labels=poly_labels, axis=axis) return axis + def _create_choice_answer_label(self, entrant: Literal["complement", "substitute", "indifferent"], + incumbent: Literal["copy", "refrain"], + development: Literal["success", "failure"]) -> str: + return self.ENTRANT_CHOICES[entrant] + " $\\rightarrow$ " + self.INCUMBENT_CHOICES[ + incumbent] + " $\\rightarrow$ " + self.DEVELOPMENT_OUTCOME[development] + + def _get_incumbent_best_answer_labels(self) -> List[str]: + return [ + # Square 1 + self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="failure") + " \n" + + self._create_choice_answer_label(entrant="complement", incumbent="copy", development="failure"), + # Square 2 + self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="failure") + " \n" + + self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success"), + # Square 3 + self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success") + " \n" + + self._create_choice_answer_label(entrant="complement", incumbent="copy", development="failure"), + # Square 4 + self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success") + " \n" + + self._create_choice_answer_label(entrant="complement", incumbent="copy", development="success"), + # Square 5 + self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") + " \n" + + self._create_choice_answer_label(entrant="complement", incumbent="copy", development="success"), + # Square 6 + self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") + " \n" + + self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success"), + ] + def _get_incumbent_best_answer_coordinates(self) -> List[List[Tuple[float, float]]]: y_max: float = self._get_y_max() x_max: float = self._get_x_max() return [ + # Square 1 [(0, 0), (self._assets['A-s'], 0), (self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (0, self._copying_fixed_costs['F(YY)s'])], + # Square 2 [(0, self._copying_fixed_costs['F(YY)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YN)s']), (0, self._copying_fixed_costs['F(YN)s'])], + # Square 3 [(self._assets['A-s'], 0), (self._assets['A-c'], 0), (self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YY)s'])], + # Square 4 [(self._assets['A-c'], 0), (x_max, 0), (x_max, self._copying_fixed_costs['F(YY)s']), (self._assets['A-c'], self._copying_fixed_costs['F(YY)s'])], + # Square 5 [(self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), (x_max, self._copying_fixed_costs['F(YY)s']), (x_max, self._copying_fixed_costs['F(YY)c']), (self._assets['A-c'], self._copying_fixed_costs['F(YY)c'])], + # Square 6 [(self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), (self._assets['A-c'], self._copying_fixed_costs['F(YY)c']), (x_max, self._copying_fixed_costs['F(YY)c']), @@ -646,21 +797,36 @@

    def plot_equilibrium(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes: poly_coordinates: List[List[Tuple[float, float]]] = self._get_equilibrium_coordinates() - axis: matplotlib.axes.Axes = self._plot(coordinates=poly_coordinates, axis=axis) + poly_labels: List[str] = self._get_equilibrium_labels() + axis: matplotlib.axes.Axes = self._plot(coordinates=poly_coordinates, labels=poly_labels, axis=axis) return axis + def _get_equilibrium_labels(self) -> List[str]: + return [ + # Square 1 + self._create_choice_answer_label(entrant="indifferent", incumbent="copy", development="failure"), + # Square 2 + self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success"), + # Square 3 + self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success"), + # Square 4 + self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") + ] + def _get_equilibrium_coordinates(self) -> List[List[Tuple[float, float]]]: y_max: float = self._get_y_max() x_max: float = self._get_x_max() return [ + # Square 1 [(0, 0), (self._assets['A-s'], 0), (self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (0, self._copying_fixed_costs['F(YY)s'])], + # Square 2 [(0, self._copying_fixed_costs['F(YY)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YN)s']), (0, self._copying_fixed_costs['F(YN)s'])], + # Square 3 [(self._assets['A-s'], 0), (x_max, 0), (x_max, self._copying_fixed_costs['F(YY)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YY)s'])], - [(self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), (x_max, self._copying_fixed_costs['F(YY)s']), - (x_max, self._copying_fixed_costs['F(YY)c']), (self._assets['A-c'], self._copying_fixed_costs['F(YY)c'])], + # Square 4 [(self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (x_max, self._copying_fixed_costs['F(YY)s']), (x_max, y_max), (0, y_max), (0, self._copying_fixed_costs['F(YN)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YN)s'])]] @@ -681,8 +847,9 @@

    bars = axis.bar(index + counter * (bar_width + spacing), utility_values, bar_width, alpha=opacity, color='w', + hatch='///', edgecolor=self._get_color(counter), - label=utility_type) + label=self._convert_utility_label(utility_type)) max_indices: List[int] = list( filter(lambda x: utility_values[x] == max(utility_values), range(len(utility_values)))) for max_index in max_indices: @@ -692,22 +859,48 @@

    axis.set_ylabel('Utility') axis.set_title('Utility levels for different Market Configurations') axis.set_xticks(index + 1.5 * (bar_width + spacing)) - axis.set_xticklabels(tuple(self._utility.keys())) - axis.legend() + axis.set_xticklabels(tuple([self._convert_market_configuration_label(i) for i in self._utility.keys()])) + axis.legend(bbox_to_anchor=(0, -0.3), loc="lower left", ncol=4) + axis.text(max(index) + 2 + 1.5 * (bar_width + spacing), self._utility["E(P)"]["W"]*0.5, self._get_market_configuration_annotations()) - BaseModel._set_axis(axis) + # BaseModel._set_axis(axis) plt.show() return axis + @staticmethod + def _get_market_configuration_annotations() -> str: + return "$I_P$: Primary product sold by the incumbent\n" \ + "$I_C$: Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$\n" \ + "$E_P$: Perfect substitute to $I_P$ potentially sold by the entrant\n" \ + "$E_C$: Complementary product to $I_P$ currently sold by the entrant\n" \ + "$\\tilde{E}_C$: Complementary product to $I_P$ potentially sold by the entrant\n" + + @staticmethod + def _convert_utility_label(raw_label: str) -> str: + label: str = raw_label.replace("pi", "$\pi$") + label = label.replace("CS", "Consumer Surplus") + label = label.replace("W", "Welfare") + return label + + @staticmethod + def _convert_market_configuration_label(raw_label: str) -> str: + labels: Dict[str] = {"basic": "$I_P;E_C$", + "I(C)": "$I_P+I_C;E_C$", + "E(P)": "$I_P;E_C+E_P$", + "I(C)E(P)": "$I_P+I_C;E_C+E_P$", + "E(C)": "$I_P;E_C+\\tilde{E}_C$", + "I(C)E(C)": "$I_P+I_C;E_C+\\tilde{E}_C$"} + return labels.get(raw_label, 'No valid market configuration') + def _get_x_max(self) -> float: return round(self._assets['A-c'] * 1.3, 1) def _get_y_max(self) -> float: return round(self._copying_fixed_costs['F(YN)s'] * 1.3, 1) - def _draw_horizontal_lines(self, axis: matplotlib.axes.Axes) -> None: - horizontal_line_x: float = self._get_x_max() + 0.15 - vertical_line_y: float = self._get_y_max() + 0.25 + def _draw_thresholds(self, axis: matplotlib.axes.Axes) -> None: + horizontal_line_x: float = self._get_x_max() + 0.05 + vertical_line_y: float = self._get_y_max() + 0.15 axis.axhline(self._copying_fixed_costs['F(YN)s'], linestyle='--', color='k') axis.text(horizontal_line_x, self._copying_fixed_costs['F(YN)s'], r'$F^{YN}_S$') axis.axhline(self._copying_fixed_costs['F(YY)s'], linestyle='--', color='k') @@ -725,9 +918,8 @@

    @staticmethod def _set_axis(axis: matplotlib.axes.Axes): - axis.margins(x=0.2, y=0.1) - axis.relim() axis.autoscale_view() + axis.figure.tight_layout() def __str__(self) -> str: str_representation: str = 'Assets:' @@ -796,10 +988,11 @@

    small_delta : float ($\delta$) Additional utility gained from from a complement combined with a primary product. delta : float - ($\Delta$) Additional utility gained from the primary product of the entrant compared to the primary product of the incumbent. + ($\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent. K : float - Investment costs to develop a second product for the entrant. + Investment costs for the entrant to develop a second product. """ + super(BaseModel, self).__init__() assert small_delta / 2 < delta < 3 * small_delta / 2, "(A1b) not satisfied." assert K < small_delta / 2, "(A2) not satisfied." self._u: float = u @@ -833,9 +1026,9 @@

    Parameters
  • small_delta (float): ($\delta$) Additional utility gained from from a complement combined with a primary product.
  • delta (float): -($\Delta$) Additional utility gained from the primary product of the entrant compared to the primary product of the incumbent.
  • +($\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent.
  • K (float): -Investment costs to develop a second product for the entrant.
  • +Investment costs for the entrant to develop a second product.
    @@ -1001,18 +1194,42 @@
    Returns
    def - get_optimal_choice(self, A: float, F: float): + get_optimal_choice(self, A: float, F: float) -> Dict[str, str]:

    View Source -
        def get_optimal_choice(self, A: float, F: float):
    -        pass
    +            
        def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]:
    +        result: Dict[str, str] = {"entrant": "", "incumbent": "", "development": ""}
    +        if self._copying_fixed_costs["F(YN)c"] <= F <= self._copying_fixed_costs["F(YN)s"] and A < self._assets["A-s"]:
    +            result.update({"entrant": self.ENTRANT_CHOICES["complement"]})
    +            result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]})
    +            result.update({"development": self.DEVELOPMENT_OUTCOME["success"]})
    +        elif F <= self._copying_fixed_costs["F(YN)c"] and A < self._assets["A-s"]:
    +            result.update({"entrant": self.ENTRANT_CHOICES["indifferent"]})
    +            result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]})
    +            result.update({"development": self.DEVELOPMENT_OUTCOME["failure"]})
    +        else:
    +            result.update({"entrant": self.ENTRANT_CHOICES["substitute"]})
    +            result.update({"development": self.DEVELOPMENT_OUTCOME["success"]})
    +            if F <= self._copying_fixed_costs["F(YY)s"]:
    +                result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]})
    +            else:
    +                result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]})
    +        return result
     
    -

    Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant ann fixed costs for copying of the incumbent.

    +

    Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant and fixed costs for copying of the incumbent.

    + +

    The output dictionary will contain the following details:

    + +
    Parameters
    @@ -1026,7 +1243,7 @@
    Parameters
    Returns
      -
    • Optimal choice of the entrant and the incumbent.
    • +
    • Dict[str, str]: Optimal choice of the entrant, the incumbent and the outcome of the development.
    @@ -1047,7 +1264,8 @@
    Returns
    View Source
        def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes:
             poly_coordinates: List[List[Tuple[float, float]]] = self._get_incumbent_best_answer_coordinates()
    -        axis: matplotlib.axes.Axes = self._plot(coordinates=poly_coordinates, axis=axis)
    +        poly_labels: List[str] = self._get_incumbent_best_answer_labels()
    +        axis: matplotlib.axes.Axes = self._plot(coordinates=poly_coordinates, labels=poly_labels, axis=axis)
             return axis
     
    @@ -1086,7 +1304,8 @@
    Returns
    View Source
        def plot_equilibrium(self, axis: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes:
             poly_coordinates: List[List[Tuple[float, float]]] = self._get_equilibrium_coordinates()
    -        axis: matplotlib.axes.Axes = self._plot(coordinates=poly_coordinates, axis=axis)
    +        poly_labels: List[str] = self._get_equilibrium_labels()
    +        axis: matplotlib.axes.Axes = self._plot(coordinates=poly_coordinates, labels=poly_labels, axis=axis)
             return axis
     
    @@ -1139,8 +1358,9 @@
    Returns
    bars = axis.bar(index + counter * (bar_width + spacing), utility_values, bar_width, alpha=opacity, color='w', + hatch='///', edgecolor=self._get_color(counter), - label=utility_type) + label=self._convert_utility_label(utility_type)) max_indices: List[int] = list( filter(lambda x: utility_values[x] == max(utility_values), range(len(utility_values)))) for max_index in max_indices: @@ -1150,10 +1370,11 @@
    Returns
    axis.set_ylabel('Utility') axis.set_title('Utility levels for different Market Configurations') axis.set_xticks(index + 1.5 * (bar_width + spacing)) - axis.set_xticklabels(tuple(self._utility.keys())) - axis.legend() + axis.set_xticklabels(tuple([self._convert_market_configuration_label(i) for i in self._utility.keys()])) + axis.legend(bbox_to_anchor=(0, -0.3), loc="lower left", ncol=4) + axis.text(max(index) + 2 + 1.5 * (bar_width + spacing), self._utility["E(P)"]["W"]*0.5, self._get_market_configuration_annotations()) - BaseModel._set_axis(axis) + # BaseModel._set_axis(axis) plt.show() return axis
    @@ -1178,6 +1399,17 @@
    Returns
    +
    @@ -1220,6 +1452,12 @@
    Inherited Members
    plot_equilibrium
    plot_utilities
    +
    +
    @@ -1303,9 +1541,9 @@
    Parameters
  • small_delta (float): ($\delta$) Additional utility gained from from a complement combined with a primary product.
  • delta (float): -($\Delta$) Additional utility gained from the primary product of the entrant compared to the primary product of the incumbent.
  • +($\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent.
  • K (float): -Investment costs to develop a second product for the entrant.
  • +Investment costs for the entrant to develop a second product.
    @@ -1323,6 +1561,12 @@
    Inherited Members
    plot_equilibrium
    plot_utilities
    +
    +
    @@ -1405,9 +1649,9 @@
    Parameters
  • small_delta (float): ($\delta$) Additional utility gained from from a complement combined with a primary product.
  • delta (float): -($\Delta$) Additional utility gained from the primary product of the entrant compared to the primary product of the incumbent.
  • +($\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent.
  • K (float): -Investment costs to develop a second product for the entrant.
  • +Investment costs for the entrant to develop a second product.
    @@ -1425,6 +1669,12 @@
    Inherited Members
    plot_equilibrium
    plot_utilities
    +
    +
    diff --git a/docs/search.js b/docs/search.js index b8736a7..83a7afa 100644 --- a/docs/search.js +++ b/docs/search.js @@ -1,6 +1,6 @@ window.pdocSearch = (function(){ /** elasticlunr - http://weixsong.github.io * Copyright (C) 2017 Oliver Nightingale * Copyright (C) 2017 Wei Song * MIT Licensed */!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();oThis package implements the models of Shelegia and Motta (2021).

    \n\n

    \"GitHub\"\n\"PyPI\n\"GitHub\n\"GitHub\n\"PyPi\"\n\"PyPI\"\n\"PyPI\n\"GitHub\n\"GitHub

    \n\n

    Installation

    \n\n

    Installation over PyPI:

    \n\n
    pip install Shelegia-Motta-2021\n
    \n\n

    Or clone the repository via GitHub:

    \n\n
    git clone manuelbieri/shelegia_motta_2021\n
    \n\n

    Introduction

    \n\n

    Since all models implement the Shelegia_Motta_2021.IModel.IModel - Interface, therefore all models provide the same functionality (public methods), even though the results may change substantially.

    \n\n

    For all models add the following import statement:

    \n\n
    import Shelegia_Motta_2021.Models\n
    \n\n

    Models

    \n\n

    Base Model

    \n\n
    base_model = Shelegia_Motta_2021.Models.BaseModel()\n
    \n\n

    Unobservable Choices Model

    \n\n
    unobservable_model = Shelegia_Motta_2021.Models.UnobservableModel()\n
    \n\n

    Acquisition Model

    \n\n
    acquisition_model = Shelegia_Motta_2021.Models.AcquisitionModel()\n
    \n\n

    Two-sided Market Model

    \n\n
    two_sided_market_model = Shelegia_Motta_2021.Models.TwoSidedMarketModel()\n
    \n\n

    Basic usage

    \n\n
    # every model type can be plugged in\nmodel: Shelegia_Motta_2021.IModel.IModel = Shelegia_Motta_2021.Models.BaseModel()\n\n# print string representation of the model\nprint(model)\n\n# plot the best answers of the incumbent to the choice of the entrant\nmodel.plot_incumbent_best_answers()\n\n# plot the equilibrium path\nmodel.plot_equilibrium()\n
    \n\n

    Documentation

    \n\n

    For the latest version of the documentation open manuelbieri.github.io/shelegia_motta_2021 in your browser or call:

    \n\n
    import Shelegia_Motta_2021\n\nShelegia_Motta_2021.docs()\n
    \n\n

    Build Documentation

    \n\n

    Install the pdoc package:

    \n\n
    pip install pdoc\n
    \n\n

    Generate api-documentation with the following command:

    \n\n
    pdoc -o ./docs Shelegia_Motta_2021 --docformat \"numpy\" --math\n
    \n"}, {"fullname": "Shelegia_Motta_2021.docs", "modulename": "Shelegia_Motta_2021", "qualname": "docs", "type": "function", "doc": "

    Opens the latest published version of the documentation of this package.

    \n", "parameters": [], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "", "type": "module", "doc": "

    \n"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel", "type": "class", "doc": "

    Interface for all models in Shelegia and Motta (2021).

    \n"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.__init__", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.__init__", "type": "function", "doc": "

    \n", "parameters": [], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.get_asset_values", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.get_asset_values", "type": "function", "doc": "

    Returns the asset thresholds of the entrant.

    \n\n

    Number and type of the thresholds will be specific to the model.

    \n\n
    Returns
    \n\n
      \n
    • Dict[str, float]: Includes the thresholds for the assets of the entrant.
    • \n
    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.get_copying_fixed_costs_values", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.get_copying_fixed_costs_values", "type": "function", "doc": "

    Returns the fixed costs for copying thresholds of the incumbent.

    \n\n

    Number and type of the thresholds will be specific to the model.

    \n\n
    Returns
    \n\n
      \n
    • Dict[str, float]: Includes the thresholds for the fixed costs for copying of the incumbent.
    • \n
    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.get_utility_values", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.get_utility_values", "type": "function", "doc": "

    Returns the utility values for different market configurations.

    \n\n

    A market configuration can include:

    \n\n
      \n
    • $I_P$ : Primary product sold by the incumbent.
    • \n
    • $I_C$ : Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$.
    • \n
    • $E_P$ : Perfect substitute to $I_P$ potentially sold by the entrant.
    • \n
    • $E_C$ : Complementary product to $I_P$ currently sold by the entrant
    • \n
    • $\\tilde{E}_C$ : Complementary product to $I_P$ potentially sold by the entrant.\n
    • \n
    \n\n\n\n\n \n \n \n \n \n\n\n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n
    Market Config.$\\pi(I)$$\\pi(E)$CSW
    $I_P$ ; $E_C$N.A.N.A.N.A.N.A.
    $I_P + I_C$ ; $E_C$N.A.N.A.N.A.N.A.
    $I_P$ ; $E_P + E_C$N.A.N.A.N.A.N.A.
    $I_P + I_C$ ; $E_P + E_C$N.A.N.A.N.A.N.A.
    $I_P$ ; $E_C + \\tilde{E}_C$N.A.N.A.N.A.N.A.
    $I_P + I_C$ ; $E_C + \\tilde{E}_C$N.A.N.A.N.A.N.A.
    \n\n


    \nThe utility values are specific to the models.

    \n\n
    Returns
    \n\n
      \n
    • Dict[str, Dict[str, float]]: Contains the mentioned utilities for different market configurations.
    • \n
    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.plot_incumbent_best_answers", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.plot_incumbent_best_answers", "type": "function", "doc": "

    Plots the best answers of the incumbent to all possible actions of the entrant.

    \n\n
    Parameters
    \n\n
      \n
    • axis (matplotlib.axes.Axes):\nAxis to draw the plot on. (optional)
    • \n
    \n\n
    Returns
    \n\n
      \n
    • matplotlib.axes.Axes: Axis containing the plot.
    • \n
    \n", "parameters": ["self", "axis"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.plot_equilibrium", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.plot_equilibrium", "type": "function", "doc": "

    Plots the equilibrium path based on the choices of the entrant and incumbent.

    \n\n
    Parameters
    \n\n
      \n
    • axis (matplotlib.axes.Axes):\nAxis to draw the plot on. (optional)
    • \n
    \n\n
    Returns
    \n\n
      \n
    • matplotlib.axes.Axes: Axis containing the plot.
    • \n
    \n", "parameters": ["self", "axis"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.plot_utilities", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.plot_utilities", "type": "function", "doc": "

    Plots the utilities for different market configurations.

    \n\n
    Parameters
    \n\n
      \n
    • axis (matplotlib.axes.Axes):\nAxis to draw the plot on. (optional)
    • \n
    \n\n
    Returns
    \n\n
      \n
    • matplotlib.axes.Axes: Axis containing the plot.
    • \n
    \n", "parameters": ["self", "axis"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.get_optimal_choice", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.get_optimal_choice", "type": "function", "doc": "

    Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant ann fixed costs for copying of the incumbent.

    \n\n
    Parameters
    \n\n
      \n
    • A (float):\nAssets of the entrant.
    • \n
    • F (float):\nFixed costs for copying of the incumbent.
    • \n
    \n\n
    Returns
    \n\n
      \n
    • Optimal choice of the entrant and the incumbent.
    • \n
    \n", "parameters": ["self", "A", "F"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.ModelTest", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "", "type": "module", "doc": "

    \n"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest", "type": "class", "doc": "

    A class whose instances are single test cases.

    \n\n

    By default, the test code itself should be placed in a method named\n'runTest'.

    \n\n

    If the fixture may be used for many test cases, create as\nmany test methods as are needed. When instantiating such a TestCase\nsubclass, specify in the constructor arguments the name of the test method\nthat the instance is to execute.

    \n\n

    Test authors should subclass TestCase for their own tests. Construction\nand deconstruction of the test's environment ('fixture') can be\nimplemented by overriding the 'setUp' and 'tearDown' methods respectively.

    \n\n

    If it is necessary to override the __init__ method, the base class\n__init__ method must always be called. It is important that subclasses\nshould not change the signature of their __init__ method, since instances\nof the classes are instantiated automatically by parts of the framework\nin order to be run.

    \n\n

    When subclassing TestCase, you can set these attributes:

    \n\n
      \n
    • failureException: determines which exception will be raised when\nthe instance's assertion methods fail; test methods raising this\nexception will be deemed to have 'failed' rather than 'errored'.
    • \n
    • longMessage: determines whether long messages (including repr of\nobjects used in assert methods) will be printed on failure in addition\nto any explicit message passed.
    • \n
    • maxDiff: sets the maximum length of a diff in failure messages\nby assert methods using difflib. It is looked up as an instance\nattribute so can be configured by individual tests if required.
    • \n
    \n"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest.setUp", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest.setUp", "type": "function", "doc": "

    Hook method for setting up the test fixture before exercising it.

    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest.test_invalid_A1b", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest.test_invalid_A1b", "type": "function", "doc": "

    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest.test_invalid_A2", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest.test_invalid_A2", "type": "function", "doc": "

    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models", "modulename": "Shelegia_Motta_2021.Models", "qualname": "", "type": "module", "doc": "

    \n"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel", "type": "class", "doc": "

    There are two players in our base model: the Incumbent, which sells the primary product, denoted\nby Ip, and a start-up, that we call Entrant, which sells a product Ec complementary to Ip. (One may\nthink of Ip as a platform, and Ec as a service or product which can be accessed through the platform.)\nWe are interested in studying the choice of E between developing a substitute to Ip, denoted by\nEp, or another complement to Ip, denoted by E\u02dcc;23 and the choice of I between copying E\u2019s original\ncomplementary product Ec by creating a perfect substitute Ic, or not.24 Since E may not have enough\nassets to cover the development cost of its second product, copying its current product will a\u21b5ect E\u2019s\nability to obtain funding

    \n"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.__init__", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.__init__", "type": "function", "doc": "

    Initializes a valid BaseModel object.

    \n\n

    The following preconditions have to be satisfied:

    \n\n
      \n
    • (A1b) $\\delta$ / 2 < $\\Delta$ < 3 * $\\delta$ / 2
    • \n
    • (A2) K < $\\delta$ / 2
    • \n
    \n\n
    Parameters
    \n\n
      \n
    • u (float):\nUtility gained from consuming the primary product
    • \n
    • B (float):\nMinimal difference between the return in case of a success and the return in case of failure of E. B is called the private benefit of E in case of failure.
    • \n
    • small_delta (float):\n($\\delta$) Additional utility gained from from a complement combined with a primary product.
    • \n
    • delta (float):\n($\\Delta$) Additional utility gained from the primary product of the entrant compared to the primary product of the incumbent.
    • \n
    • K (float):\nInvestment costs to develop a second product for the entrant.
    • \n
    \n", "parameters": ["self", "u", "B", "small_delta", "delta", "K"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.get_asset_values", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.get_asset_values", "type": "function", "doc": "

    Returns the asset thresholds of the entrant.

    \n\n

    Number and type of the thresholds will be specific to the model.

    \n\n
    Returns
    \n\n
      \n
    • Dict[str, float]: Includes the thresholds for the assets of the entrant.
    • \n
    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.get_copying_fixed_costs_values", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.get_copying_fixed_costs_values", "type": "function", "doc": "

    Returns the fixed costs for copying thresholds of the incumbent.

    \n\n

    Number and type of the thresholds will be specific to the model.

    \n\n
    Returns
    \n\n
      \n
    • Dict[str, float]: Includes the thresholds for the fixed costs for copying of the incumbent.
    • \n
    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.get_utility_values", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.get_utility_values", "type": "function", "doc": "

    Returns the utility values for different market configurations.

    \n\n

    A market configuration can include:

    \n\n
      \n
    • $I_P$ : Primary product sold by the incumbent.
    • \n
    • $I_C$ : Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$.
    • \n
    • $E_P$ : Perfect substitute to $I_P$ potentially sold by the entrant.
    • \n
    • $E_C$ : Complementary product to $I_P$ currently sold by the entrant
    • \n
    • $\\tilde{E}_C$ : Complementary product to $I_P$ potentially sold by the entrant.\n
    • \n
    \n\n\n\n\n \n \n \n \n \n\n\n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n
    Market Config.$\\pi(I)$$\\pi(E)$CSW
    $I_P$ ; $E_C$N.A.N.A.N.A.N.A.
    $I_P + I_C$ ; $E_C$N.A.N.A.N.A.N.A.
    $I_P$ ; $E_P + E_C$N.A.N.A.N.A.N.A.
    $I_P + I_C$ ; $E_P + E_C$N.A.N.A.N.A.N.A.
    $I_P$ ; $E_C + \\tilde{E}_C$N.A.N.A.N.A.N.A.
    $I_P + I_C$ ; $E_C + \\tilde{E}_C$N.A.N.A.N.A.N.A.
    \n\n


    \nThe utility values are specific to the models.

    \n\n
    Returns
    \n\n
      \n
    • Dict[str, Dict[str, float]]: Contains the mentioned utilities for different market configurations.
    • \n
    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.get_optimal_choice", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.get_optimal_choice", "type": "function", "doc": "

    Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant ann fixed costs for copying of the incumbent.

    \n\n
    Parameters
    \n\n
      \n
    • A (float):\nAssets of the entrant.
    • \n
    • F (float):\nFixed costs for copying of the incumbent.
    • \n
    \n\n
    Returns
    \n\n
      \n
    • Optimal choice of the entrant and the incumbent.
    • \n
    \n", "parameters": ["self", "A", "F"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.plot_incumbent_best_answers", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.plot_incumbent_best_answers", "type": "function", "doc": "

    Plots the best answers of the incumbent to all possible actions of the entrant.

    \n\n
    Parameters
    \n\n
      \n
    • axis (matplotlib.axes.Axes):\nAxis to draw the plot on. (optional)
    • \n
    \n\n
    Returns
    \n\n
      \n
    • matplotlib.axes.Axes: Axis containing the plot.
    • \n
    \n", "parameters": ["self", "axis"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.plot_equilibrium", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.plot_equilibrium", "type": "function", "doc": "

    Plots the equilibrium path based on the choices of the entrant and incumbent.

    \n\n
    Parameters
    \n\n
      \n
    • axis (matplotlib.axes.Axes):\nAxis to draw the plot on. (optional)
    • \n
    \n\n
    Returns
    \n\n
      \n
    • matplotlib.axes.Axes: Axis containing the plot.
    • \n
    \n", "parameters": ["self", "axis"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.plot_utilities", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.plot_utilities", "type": "function", "doc": "

    Plots the utilities for different market configurations.

    \n\n
    Parameters
    \n\n
      \n
    • axis (matplotlib.axes.Axes):\nAxis to draw the plot on. (optional)
    • \n
    \n\n
    Returns
    \n\n
      \n
    • matplotlib.axes.Axes: Axis containing the plot.
    • \n
    \n", "parameters": ["self", "axis"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.UnobservableModel", "modulename": "Shelegia_Motta_2021.Models", "qualname": "UnobservableModel", "type": "class", "doc": "

    There are two players in our base model: the Incumbent, which sells the primary product, denoted\nby Ip, and a start-up, that we call Entrant, which sells a product Ec complementary to Ip. (One may\nthink of Ip as a platform, and Ec as a service or product which can be accessed through the platform.)\nWe are interested in studying the choice of E between developing a substitute to Ip, denoted by\nEp, or another complement to Ip, denoted by E\u02dcc;23 and the choice of I between copying E\u2019s original\ncomplementary product Ec by creating a perfect substitute Ic, or not.24 Since E may not have enough\nassets to cover the development cost of its second product, copying its current product will a\u21b5ect E\u2019s\nability to obtain funding

    \n"}, {"fullname": "Shelegia_Motta_2021.Models.AcquisitionModel", "modulename": "Shelegia_Motta_2021.Models", "qualname": "AcquisitionModel", "type": "class", "doc": "

    There are two players in our base model: the Incumbent, which sells the primary product, denoted\nby Ip, and a start-up, that we call Entrant, which sells a product Ec complementary to Ip. (One may\nthink of Ip as a platform, and Ec as a service or product which can be accessed through the platform.)\nWe are interested in studying the choice of E between developing a substitute to Ip, denoted by\nEp, or another complement to Ip, denoted by E\u02dcc;23 and the choice of I between copying E\u2019s original\ncomplementary product Ec by creating a perfect substitute Ic, or not.24 Since E may not have enough\nassets to cover the development cost of its second product, copying its current product will a\u21b5ect E\u2019s\nability to obtain funding

    \n"}, {"fullname": "Shelegia_Motta_2021.Models.AcquisitionModel.__init__", "modulename": "Shelegia_Motta_2021.Models", "qualname": "AcquisitionModel.__init__", "type": "function", "doc": "

    Initializes a valid BaseModel object.

    \n\n

    The following preconditions have to be satisfied:

    \n\n
      \n
    • (A1b) $\\delta$ / 2 < $\\Delta$ < 3 * $\\delta$ / 2
    • \n
    • (A2) K < $\\delta$ / 2
    • \n
    \n\n
    Parameters
    \n\n
      \n
    • u (float):\nUtility gained from consuming the primary product
    • \n
    • B (float):\nMinimal difference between the return in case of a success and the return in case of failure of E. B is called the private benefit of E in case of failure.
    • \n
    • small_delta (float):\n($\\delta$) Additional utility gained from from a complement combined with a primary product.
    • \n
    • delta (float):\n($\\Delta$) Additional utility gained from the primary product of the entrant compared to the primary product of the incumbent.
    • \n
    • K (float):\nInvestment costs to develop a second product for the entrant.
    • \n
    \n", "parameters": ["self", "u", "B", "small_delta", "delta", "K"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.TwoSidedMarketModel", "modulename": "Shelegia_Motta_2021.Models", "qualname": "TwoSidedMarketModel", "type": "class", "doc": "

    There are two players in our base model: the Incumbent, which sells the primary product, denoted\nby Ip, and a start-up, that we call Entrant, which sells a product Ec complementary to Ip. (One may\nthink of Ip as a platform, and Ec as a service or product which can be accessed through the platform.)\nWe are interested in studying the choice of E between developing a substitute to Ip, denoted by\nEp, or another complement to Ip, denoted by E\u02dcc;23 and the choice of I between copying E\u2019s original\ncomplementary product Ec by creating a perfect substitute Ic, or not.24 Since E may not have enough\nassets to cover the development cost of its second product, copying its current product will a\u21b5ect E\u2019s\nability to obtain funding

    \n"}, {"fullname": "Shelegia_Motta_2021.Models.TwoSidedMarketModel.__init__", "modulename": "Shelegia_Motta_2021.Models", "qualname": "TwoSidedMarketModel.__init__", "type": "function", "doc": "

    Initializes a valid BaseModel object.

    \n\n

    The following preconditions have to be satisfied:

    \n\n
      \n
    • (A1b) $\\delta$ / 2 < $\\Delta$ < 3 * $\\delta$ / 2
    • \n
    • (A2) K < $\\delta$ / 2
    • \n
    \n\n
    Parameters
    \n\n
      \n
    • u (float):\nUtility gained from consuming the primary product
    • \n
    • B (float):\nMinimal difference between the return in case of a success and the return in case of failure of E. B is called the private benefit of E in case of failure.
    • \n
    • small_delta (float):\n($\\delta$) Additional utility gained from from a complement combined with a primary product.
    • \n
    • delta (float):\n($\\Delta$) Additional utility gained from the primary product of the entrant compared to the primary product of the incumbent.
    • \n
    • K (float):\nInvestment costs to develop a second product for the entrant.
    • \n
    \n", "parameters": ["self", "u", "B", "small_delta", "delta", "K", "gamma"], "funcdef": "def"}]; + /** pdoc search index */const docs = [{"fullname": "Shelegia_Motta_2021", "modulename": "Shelegia_Motta_2021", "qualname": "", "type": "module", "doc": "

    This package implements the models of Shelegia and Motta (2021).

    \n\n

    \"GitHub\"\n\"PyPI\n\"GitHub\n\"GitHub\n\"PyPi\"\n\"PyPI\"\n\"PyPI\n\"GitHub\n\"GitHub

    \n\n

    Installation

    \n\n

    Installation over PyPI:

    \n\n
    pip install Shelegia-Motta-2021\n
    \n\n

    Or clone the repository via GitHub:

    \n\n
    git clone manuelbieri/shelegia_motta_2021\n
    \n\n

    Introduction

    \n\n

    Since all models implement the Shelegia_Motta_2021.IModel.IModel - Interface, therefore all models provide the same functionality (public methods), even though the results may change substantially.

    \n\n

    For all models add the following import statement:

    \n\n
    import Shelegia_Motta_2021.Models\n
    \n\n

    Models

    \n\n

    Base Model

    \n\n
    base_model = Shelegia_Motta_2021.Models.BaseModel()\n
    \n\n

    Unobservable Choices Model

    \n\n
    unobservable_model = Shelegia_Motta_2021.Models.UnobservableModel()\n
    \n\n

    Acquisition Model

    \n\n
    acquisition_model = Shelegia_Motta_2021.Models.AcquisitionModel()\n
    \n\n

    Two-sided Market Model

    \n\n
    two_sided_market_model = Shelegia_Motta_2021.Models.TwoSidedMarketModel()\n
    \n\n

    Basic usage

    \n\n
    # every model type can be plugged in\nmodel: Shelegia_Motta_2021.IModel.IModel = Shelegia_Motta_2021.Models.BaseModel()\n\n# print string representation of the model\nprint(model)\n\n# plot the best answers of the incumbent to the choice of the entrant\nmodel.plot_incumbent_best_answers()\n\n# plot the equilibrium path\nmodel.plot_equilibrium()\n
    \n\n

    Documentation

    \n\n

    For the latest version of the documentation open manuelbieri.github.io/shelegia_motta_2021 in your browser or call:

    \n\n
    import Shelegia_Motta_2021\n\nShelegia_Motta_2021.docs()\n
    \n\n

    Build Documentation

    \n\n

    Install the pdoc package:

    \n\n
    pip install pdoc\n
    \n\n

    Generate api-documentation with the following command:

    \n\n
    pdoc -o ./docs Shelegia_Motta_2021 --docformat \"numpy\" --math\n
    \n"}, {"fullname": "Shelegia_Motta_2021.docs", "modulename": "Shelegia_Motta_2021", "qualname": "docs", "type": "function", "doc": "

    Opens the latest published version of the documentation of this package.

    \n", "parameters": [], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "", "type": "module", "doc": "

    \n"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel", "type": "class", "doc": "

    Interface for all models in Shelegia and Motta (2021).

    \n"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.__init__", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.__init__", "type": "function", "doc": "

    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.ENTRANT_CHOICES", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.ENTRANT_CHOICES", "type": "variable", "doc": "

    Contains all the possible product choices of the entrant.

    \n\n
      \n
    • complement (C)
    • \n
    • substitute (S)
    • \n
    • Indifferent (I)
    • \n
    \n"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.INCUMBENT_CHOICES", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.INCUMBENT_CHOICES", "type": "variable", "doc": "

    Contains all the possible answers of the incumbent to the choice of the entrant.

    \n\n
      \n
    • copy (\u00a9)
    • \n
    • refrain (\u00d8)
    • \n
    \n"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.DEVELOPMENT_OUTCOME", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.DEVELOPMENT_OUTCOME", "type": "variable", "doc": "

    Contains all the possible outcomes of the development for the chosen product of the entrant or the merged entity.

    \n\n
      \n
    • success (Y)
    • \n
    • failure (N)
    • \n
    \n"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.get_asset_values", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.get_asset_values", "type": "function", "doc": "

    Returns the asset thresholds of the entrant.

    \n\n

    Number and type of the thresholds will be specific to the model.

    \n\n
    Returns
    \n\n
      \n
    • Dict[str, float]: Includes the thresholds for the assets of the entrant.
    • \n
    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.get_copying_fixed_costs_values", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.get_copying_fixed_costs_values", "type": "function", "doc": "

    Returns the fixed costs for copying thresholds of the incumbent.

    \n\n

    Number and type of the thresholds will be specific to the model.

    \n\n
    Returns
    \n\n
      \n
    • Dict[str, float]: Includes the thresholds for the fixed costs for copying of the incumbent.
    • \n
    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.get_utility_values", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.get_utility_values", "type": "function", "doc": "

    Returns the utility values for different market configurations.

    \n\n

    A market configuration can include:

    \n\n
      \n
    • $I_P$ : Primary product sold by the incumbent.
    • \n
    • $I_C$ : Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$.
    • \n
    • $E_P$ : Perfect substitute to $I_P$ potentially sold by the entrant.
    • \n
    • $E_C$ : Complementary product to $I_P$ currently sold by the entrant
    • \n
    • $\\tilde{E}_C$ : Complementary product to $I_P$ potentially sold by the entrant.\n
    • \n
    \n\n\n\n\n \n \n \n \n \n\n\n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n
    Market Config.$\\pi(I)$$\\pi(E)$CSW
    $I_P$ ; $E_C$N.A.N.A.N.A.N.A.
    $I_P + I_C$ ; $E_C$N.A.N.A.N.A.N.A.
    $I_P$ ; $E_P + E_C$N.A.N.A.N.A.N.A.
    $I_P + I_C$ ; $E_P + E_C$N.A.N.A.N.A.N.A.
    $I_P$ ; $E_C + \\tilde{E}_C$N.A.N.A.N.A.N.A.
    $I_P + I_C$ ; $E_C + \\tilde{E}_C$N.A.N.A.N.A.N.A.
    \n\n


    \nThe utility values are specific to the models.

    \n\n
    Returns
    \n\n
      \n
    • Dict[str, Dict[str, float]]: Contains the mentioned utilities for different market configurations.
    • \n
    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.get_optimal_choice", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.get_optimal_choice", "type": "function", "doc": "

    Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant and fixed costs for copying of the incumbent.

    \n\n

    The output dictionary will contain the following details:

    \n\n
      \n
    • \"entrant\": choice of the entrant (possible choices listed in Shelegia_Motta_2021.IModel.IModel.ENTRANT_CHOICES))
    • \n
    • \"incumbent\": choice of the incumbent (possible choices listed in Shelegia_Motta_2021.IModel.IModel.INCUMBENT_CHOICES)
    • \n
    • \"development\": outcome of the development (possible outcomes listed in Shelegia_Motta_2021.IModel.IModel.DEVELOPMENT_OUTCOME)
    • \n
    \n\n
    Parameters
    \n\n
      \n
    • A (float):\nAssets of the entrant.
    • \n
    • F (float):\nFixed costs for copying of the incumbent.
    • \n
    \n\n
    Returns
    \n\n
      \n
    • Dict[str, str]: Optimal choice of the entrant, the incumbent and the outcome of the development.
    • \n
    \n", "parameters": ["self", "A", "F"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.plot_incumbent_best_answers", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.plot_incumbent_best_answers", "type": "function", "doc": "

    Plots the best answers of the incumbent to all possible actions of the entrant.

    \n\n
    Parameters
    \n\n
      \n
    • axis (matplotlib.axes.Axes):\nAxis to draw the plot on. (optional)
    • \n
    \n\n
    Returns
    \n\n
      \n
    • matplotlib.axes.Axes: Axis containing the plot.
    • \n
    \n", "parameters": ["self", "axis"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.plot_equilibrium", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.plot_equilibrium", "type": "function", "doc": "

    Plots the equilibrium path based on the choices of the entrant and incumbent.

    \n\n
    Parameters
    \n\n
      \n
    • axis (matplotlib.axes.Axes):\nAxis to draw the plot on. (optional)
    • \n
    \n\n
    Returns
    \n\n
      \n
    • matplotlib.axes.Axes: Axis containing the plot.
    • \n
    \n", "parameters": ["self", "axis"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.IModel.IModel.plot_utilities", "modulename": "Shelegia_Motta_2021.IModel", "qualname": "IModel.plot_utilities", "type": "function", "doc": "

    Plots the utilities for different market configurations.

    \n\n
    Parameters
    \n\n
      \n
    • axis (matplotlib.axes.Axes):\nAxis to draw the plot on. (optional)
    • \n
    \n\n
    Returns
    \n\n
      \n
    • matplotlib.axes.Axes: Axis containing the plot.
    • \n
    \n", "parameters": ["self", "axis"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.ModelTest", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "", "type": "module", "doc": "

    \n"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest", "type": "class", "doc": "

    A class whose instances are single test cases.

    \n\n

    By default, the test code itself should be placed in a method named\n'runTest'.

    \n\n

    If the fixture may be used for many test cases, create as\nmany test methods as are needed. When instantiating such a TestCase\nsubclass, specify in the constructor arguments the name of the test method\nthat the instance is to execute.

    \n\n

    Test authors should subclass TestCase for their own tests. Construction\nand deconstruction of the test's environment ('fixture') can be\nimplemented by overriding the 'setUp' and 'tearDown' methods respectively.

    \n\n

    If it is necessary to override the __init__ method, the base class\n__init__ method must always be called. It is important that subclasses\nshould not change the signature of their __init__ method, since instances\nof the classes are instantiated automatically by parts of the framework\nin order to be run.

    \n\n

    When subclassing TestCase, you can set these attributes:

    \n\n
      \n
    • failureException: determines which exception will be raised when\nthe instance's assertion methods fail; test methods raising this\nexception will be deemed to have 'failed' rather than 'errored'.
    • \n
    • longMessage: determines whether long messages (including repr of\nobjects used in assert methods) will be printed on failure in addition\nto any explicit message passed.
    • \n
    • maxDiff: sets the maximum length of a diff in failure messages\nby assert methods using difflib. It is looked up as an instance\nattribute so can be configured by individual tests if required.
    • \n
    \n"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest.setUpModel", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest.setUpModel", "type": "function", "doc": "

    \n", "parameters": [], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest.setUp", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest.setUp", "type": "function", "doc": "

    Hook method for setting up the test fixture before exercising it.

    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest.test_invalid_A1b", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest.test_invalid_A1b", "type": "function", "doc": "

    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest.test_invalid_A2", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest.test_invalid_A2", "type": "function", "doc": "

    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest.test_path_indifferent_copy", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest.test_path_indifferent_copy", "type": "function", "doc": "

    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest.test_path_kill_zone", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest.test_path_kill_zone", "type": "function", "doc": "

    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest.test_path_substitute_refrain", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest.test_path_substitute_refrain", "type": "function", "doc": "

    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.ModelTest.BaseModelTest.test_path_substitute_copy", "modulename": "Shelegia_Motta_2021.ModelTest", "qualname": "BaseModelTest.test_path_substitute_copy", "type": "function", "doc": "

    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models", "modulename": "Shelegia_Motta_2021.Models", "qualname": "", "type": "module", "doc": "

    \n"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel", "type": "class", "doc": "

    There are two players in our base model: the Incumbent, which sells the primary product, denoted\nby Ip, and a start-up, that we call Entrant, which sells a product Ec complementary to Ip. (One may\nthink of Ip as a platform, and Ec as a service or product which can be accessed through the platform.)\nWe are interested in studying the choice of E between developing a substitute to Ip, denoted by\nEp, or another complement to Ip, denoted by E\u02dcc;23 and the choice of I between copying E\u2019s original\ncomplementary product Ec by creating a perfect substitute Ic, or not.24 Since E may not have enough\nassets to cover the development cost of its second product, copying its current product will a\u21b5ect E\u2019s\nability to obtain funding

    \n"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.__init__", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.__init__", "type": "function", "doc": "

    Initializes a valid BaseModel object.

    \n\n

    The following preconditions have to be satisfied:

    \n\n
      \n
    • (A1b) $\\delta$ / 2 < $\\Delta$ < 3 * $\\delta$ / 2
    • \n
    • (A2) K < $\\delta$ / 2
    • \n
    \n\n
    Parameters
    \n\n
      \n
    • u (float):\nUtility gained from consuming the primary product
    • \n
    • B (float):\nMinimal difference between the return in case of a success and the return in case of failure of E. B is called the private benefit of E in case of failure.
    • \n
    • small_delta (float):\n($\\delta$) Additional utility gained from from a complement combined with a primary product.
    • \n
    • delta (float):\n($\\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent.
    • \n
    • K (float):\nInvestment costs for the entrant to develop a second product.
    • \n
    \n", "parameters": ["self", "u", "B", "small_delta", "delta", "K"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.get_asset_values", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.get_asset_values", "type": "function", "doc": "

    Returns the asset thresholds of the entrant.

    \n\n

    Number and type of the thresholds will be specific to the model.

    \n\n
    Returns
    \n\n
      \n
    • Dict[str, float]: Includes the thresholds for the assets of the entrant.
    • \n
    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.get_copying_fixed_costs_values", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.get_copying_fixed_costs_values", "type": "function", "doc": "

    Returns the fixed costs for copying thresholds of the incumbent.

    \n\n

    Number and type of the thresholds will be specific to the model.

    \n\n
    Returns
    \n\n
      \n
    • Dict[str, float]: Includes the thresholds for the fixed costs for copying of the incumbent.
    • \n
    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.get_utility_values", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.get_utility_values", "type": "function", "doc": "

    Returns the utility values for different market configurations.

    \n\n

    A market configuration can include:

    \n\n
      \n
    • $I_P$ : Primary product sold by the incumbent.
    • \n
    • $I_C$ : Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$.
    • \n
    • $E_P$ : Perfect substitute to $I_P$ potentially sold by the entrant.
    • \n
    • $E_C$ : Complementary product to $I_P$ currently sold by the entrant
    • \n
    • $\\tilde{E}_C$ : Complementary product to $I_P$ potentially sold by the entrant.\n
    • \n
    \n\n\n\n\n \n \n \n \n \n\n\n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n \n \n \n \n \n\n\n
    Market Config.$\\pi(I)$$\\pi(E)$CSW
    $I_P$ ; $E_C$N.A.N.A.N.A.N.A.
    $I_P + I_C$ ; $E_C$N.A.N.A.N.A.N.A.
    $I_P$ ; $E_P + E_C$N.A.N.A.N.A.N.A.
    $I_P + I_C$ ; $E_P + E_C$N.A.N.A.N.A.N.A.
    $I_P$ ; $E_C + \\tilde{E}_C$N.A.N.A.N.A.N.A.
    $I_P + I_C$ ; $E_C + \\tilde{E}_C$N.A.N.A.N.A.N.A.
    \n\n


    \nThe utility values are specific to the models.

    \n\n
    Returns
    \n\n
      \n
    • Dict[str, Dict[str, float]]: Contains the mentioned utilities for different market configurations.
    • \n
    \n", "parameters": ["self"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.get_optimal_choice", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.get_optimal_choice", "type": "function", "doc": "

    Return the optimal choice of the entrant and the incumbent based on a pair of assets of the entrant and fixed costs for copying of the incumbent.

    \n\n

    The output dictionary will contain the following details:

    \n\n
      \n
    • \"entrant\": choice of the entrant (possible choices listed in Shelegia_Motta_2021.IModel.IModel.ENTRANT_CHOICES))
    • \n
    • \"incumbent\": choice of the incumbent (possible choices listed in Shelegia_Motta_2021.IModel.IModel.INCUMBENT_CHOICES)
    • \n
    • \"development\": outcome of the development (possible outcomes listed in Shelegia_Motta_2021.IModel.IModel.DEVELOPMENT_OUTCOME)
    • \n
    \n\n
    Parameters
    \n\n
      \n
    • A (float):\nAssets of the entrant.
    • \n
    • F (float):\nFixed costs for copying of the incumbent.
    • \n
    \n\n
    Returns
    \n\n
      \n
    • Dict[str, str]: Optimal choice of the entrant, the incumbent and the outcome of the development.
    • \n
    \n", "parameters": ["self", "A", "F"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.plot_incumbent_best_answers", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.plot_incumbent_best_answers", "type": "function", "doc": "

    Plots the best answers of the incumbent to all possible actions of the entrant.

    \n\n
    Parameters
    \n\n
      \n
    • axis (matplotlib.axes.Axes):\nAxis to draw the plot on. (optional)
    • \n
    \n\n
    Returns
    \n\n
      \n
    • matplotlib.axes.Axes: Axis containing the plot.
    • \n
    \n", "parameters": ["self", "axis"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.plot_equilibrium", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.plot_equilibrium", "type": "function", "doc": "

    Plots the equilibrium path based on the choices of the entrant and incumbent.

    \n\n
    Parameters
    \n\n
      \n
    • axis (matplotlib.axes.Axes):\nAxis to draw the plot on. (optional)
    • \n
    \n\n
    Returns
    \n\n
      \n
    • matplotlib.axes.Axes: Axis containing the plot.
    • \n
    \n", "parameters": ["self", "axis"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.BaseModel.plot_utilities", "modulename": "Shelegia_Motta_2021.Models", "qualname": "BaseModel.plot_utilities", "type": "function", "doc": "

    Plots the utilities for different market configurations.

    \n\n
    Parameters
    \n\n
      \n
    • axis (matplotlib.axes.Axes):\nAxis to draw the plot on. (optional)
    • \n
    \n\n
    Returns
    \n\n
      \n
    • matplotlib.axes.Axes: Axis containing the plot.
    • \n
    \n", "parameters": ["self", "axis"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.UnobservableModel", "modulename": "Shelegia_Motta_2021.Models", "qualname": "UnobservableModel", "type": "class", "doc": "

    There are two players in our base model: the Incumbent, which sells the primary product, denoted\nby Ip, and a start-up, that we call Entrant, which sells a product Ec complementary to Ip. (One may\nthink of Ip as a platform, and Ec as a service or product which can be accessed through the platform.)\nWe are interested in studying the choice of E between developing a substitute to Ip, denoted by\nEp, or another complement to Ip, denoted by E\u02dcc;23 and the choice of I between copying E\u2019s original\ncomplementary product Ec by creating a perfect substitute Ic, or not.24 Since E may not have enough\nassets to cover the development cost of its second product, copying its current product will a\u21b5ect E\u2019s\nability to obtain funding

    \n"}, {"fullname": "Shelegia_Motta_2021.Models.AcquisitionModel", "modulename": "Shelegia_Motta_2021.Models", "qualname": "AcquisitionModel", "type": "class", "doc": "

    There are two players in our base model: the Incumbent, which sells the primary product, denoted\nby Ip, and a start-up, that we call Entrant, which sells a product Ec complementary to Ip. (One may\nthink of Ip as a platform, and Ec as a service or product which can be accessed through the platform.)\nWe are interested in studying the choice of E between developing a substitute to Ip, denoted by\nEp, or another complement to Ip, denoted by E\u02dcc;23 and the choice of I between copying E\u2019s original\ncomplementary product Ec by creating a perfect substitute Ic, or not.24 Since E may not have enough\nassets to cover the development cost of its second product, copying its current product will a\u21b5ect E\u2019s\nability to obtain funding

    \n"}, {"fullname": "Shelegia_Motta_2021.Models.AcquisitionModel.__init__", "modulename": "Shelegia_Motta_2021.Models", "qualname": "AcquisitionModel.__init__", "type": "function", "doc": "

    Initializes a valid BaseModel object.

    \n\n

    The following preconditions have to be satisfied:

    \n\n
      \n
    • (A1b) $\\delta$ / 2 < $\\Delta$ < 3 * $\\delta$ / 2
    • \n
    • (A2) K < $\\delta$ / 2
    • \n
    \n\n
    Parameters
    \n\n
      \n
    • u (float):\nUtility gained from consuming the primary product
    • \n
    • B (float):\nMinimal difference between the return in case of a success and the return in case of failure of E. B is called the private benefit of E in case of failure.
    • \n
    • small_delta (float):\n($\\delta$) Additional utility gained from from a complement combined with a primary product.
    • \n
    • delta (float):\n($\\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent.
    • \n
    • K (float):\nInvestment costs for the entrant to develop a second product.
    • \n
    \n", "parameters": ["self", "u", "B", "small_delta", "delta", "K"], "funcdef": "def"}, {"fullname": "Shelegia_Motta_2021.Models.TwoSidedMarketModel", "modulename": "Shelegia_Motta_2021.Models", "qualname": "TwoSidedMarketModel", "type": "class", "doc": "

    There are two players in our base model: the Incumbent, which sells the primary product, denoted\nby Ip, and a start-up, that we call Entrant, which sells a product Ec complementary to Ip. (One may\nthink of Ip as a platform, and Ec as a service or product which can be accessed through the platform.)\nWe are interested in studying the choice of E between developing a substitute to Ip, denoted by\nEp, or another complement to Ip, denoted by E\u02dcc;23 and the choice of I between copying E\u2019s original\ncomplementary product Ec by creating a perfect substitute Ic, or not.24 Since E may not have enough\nassets to cover the development cost of its second product, copying its current product will a\u21b5ect E\u2019s\nability to obtain funding

    \n"}, {"fullname": "Shelegia_Motta_2021.Models.TwoSidedMarketModel.__init__", "modulename": "Shelegia_Motta_2021.Models", "qualname": "TwoSidedMarketModel.__init__", "type": "function", "doc": "

    Initializes a valid BaseModel object.

    \n\n

    The following preconditions have to be satisfied:

    \n\n
      \n
    • (A1b) $\\delta$ / 2 < $\\Delta$ < 3 * $\\delta$ / 2
    • \n
    • (A2) K < $\\delta$ / 2
    • \n
    \n\n
    Parameters
    \n\n
      \n
    • u (float):\nUtility gained from consuming the primary product
    • \n
    • B (float):\nMinimal difference between the return in case of a success and the return in case of failure of E. B is called the private benefit of E in case of failure.
    • \n
    • small_delta (float):\n($\\delta$) Additional utility gained from from a complement combined with a primary product.
    • \n
    • delta (float):\n($\\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent.
    • \n
    • K (float):\nInvestment costs for the entrant to develop a second product.
    • \n
    \n", "parameters": ["self", "u", "B", "small_delta", "delta", "K", "gamma"], "funcdef": "def"}]; // mirrored in build-search-index.js (part 1) // Also split on html tags. this is a cheap heuristic, but good enough. diff --git a/setup.py b/setup.py index d13de14..52660fb 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='Shelegia_Motta_2021', - packages=['matplotlib', 'numpy'], + packages=find_packages(), version='0.0.3', license='MIT', description='Implements the model presented in Shelegia and Motta (2021)', @@ -30,4 +30,8 @@ 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', ], + install_requires=[ + "matplotlib>=3.4.3", + "numpy>=1.17", + ], )