Shelegia_Motta_2021.Models
1import platform 2import re 3 4# add typing support for Python 3.5 - 3.7 5if re.match("3.[5-7].*", platform.python_version()) is None: 6 from typing import Dict, List, Tuple, Literal, Final 7else: 8 from typing import Dict, List, Tuple 9 from typing_extensions import Literal, Final 10 11import matplotlib.axes 12import matplotlib.pyplot as plt 13from numpy import arange, array 14 15import textwrap 16plt.rcParams["font.family"] = "monospace" 17 18import Shelegia_Motta_2021 19 20 21class BaseModel(Shelegia_Motta_2021.IModel): 22 """ 23 The base model of the project consists of two players: The incumbent, which sells the primary product, 24 and a start-up otherwise known as the entrant which sells a complementary product to the incumbent. 25 One way to visualize a real-world application of this model would be to think of the entrant as a product or service 26 that can be accessed through the platform of the incumbent, like a plug in that can be accessed through Google or a game on Facebook. 27 The aim of this model is to monitor the choice that the entrant has between developing a substitute to or 28 another compliment to the incumbent. The second aim is to observe the choice of the incumbent of whether 29 to copy the original complementary product of the entrant by creating a perfect substitute or not. 30 Seeing as the entrant may not have enough assets to fund a second product, the incumbent copying its first product 31 would inhibit the entrant’s ability to fund its projects. This report will illustrate how the incumbent has a strategic incentive to copy 32 the entrant if it is planning to compete and that it would refrain from copying if the entrant plans to develop a compliment. 33 The subsequent models included in this report will introduce additional factors but will all be based on the basic model. 34 35 The equilibrium path arguably supports the “kill zone” argument: due to the risk of an exclusionary strategy by the incumbent, 36 a potential entrant may prefer to avoid a market trajectory which would lead it to compete with the core product of a dominant incumbent 37 and would choose to develop another complementary product instead. 38 """ 39 40 TOLERANCE: Final[float] = 10 ** (-10) 41 """Tolerance for the comparison of two floating numbers.""" 42 43 def __init__(self, u: float = 1, B: float = 0.5, small_delta: float = 0.5, delta: float = 0.51, 44 K: float = 0.2) -> None: 45 """ 46 Initializes a valid BaseModel object. 47 48 The following preconditions have to be satisfied: 49 - (A1b) $\delta$ / 2 < $\Delta$ < 3 * $\delta$ / 2 50 - (A2) K < $\delta$ / 2 51 52 Parameters 53 ---------- 54 u : float 55 Utility gained from consuming the primary product. 56 B : float 57 Minimal 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 the entrant in case of failure. 58 small_delta : float 59 ($\delta$) Additional utility gained from a complement combined with a primary product. 60 delta : float 61 ($\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent. 62 K : float 63 Investment costs for the entrant to develop a second product. 64 """ 65 super(BaseModel, self).__init__() 66 assert small_delta / 2 < delta < 3 * small_delta / 2, "(A1b) not satisfied." 67 assert K < small_delta / 2, "(A2) not satisfied." 68 self._u: float = u 69 self._B: float = B 70 self._small_delta: float = small_delta 71 self._delta: float = delta 72 self._K: float = K 73 self._copying_fixed_costs: Dict[str, float] = self._calculate_copying_fixed_costs_values() 74 self._assets: Dict[str, float] = self._calculate_asset_values() 75 self._payoffs: Dict[str, Dict[str, float]] = self._calculate_payoffs() 76 77 def _calculate_payoffs(self) -> Dict[str, Dict[str, float]]: 78 """ 79 Calculates the payoffs for different market configurations with the formulas given in the paper. 80 81 The formulas are tabulated in BaseModel.get_payoffs. 82 83 Returns 84 ------- 85 Dict[str, Dict[str, float]] 86 Contains the mentioned payoffs for different market configurations. 87 """ 88 return {'basic': {'pi(I)': self._u + self._small_delta / 2, 89 'pi(E)': self._small_delta / 2, 90 'CS': 0, 91 'W': self._u + self._small_delta 92 }, 93 'I(C)': {'pi(I)': self._u + self._small_delta, 94 'pi(E)': 0, 95 'CS': 0, 96 'W': self._u + self._small_delta 97 }, 98 'E(P)': {'pi(I)': 0, 99 'pi(E)': self._delta + self._small_delta, 100 'CS': self._u, 101 'W': self._u + self._delta + self._small_delta 102 }, 103 'I(C)E(P)': {'pi(I)': 0, 104 'pi(E)': self._delta, 105 'CS': self._u + self._small_delta, 106 'W': self._u + self._delta + self._small_delta 107 }, 108 'E(C)': {'pi(I)': self._u + self._small_delta, 109 'pi(E)': self._small_delta, 110 'CS': 0, 111 'W': self._u + 2 * self._small_delta 112 }, 113 'I(C)E(C)': {'pi(I)': self._u + 3 / 2 * self._small_delta, 114 'pi(E)': self._small_delta / 2, 115 'CS': 0, 116 'W': self._u + 2 * self._small_delta 117 } 118 } 119 120 def _calculate_copying_fixed_costs_values(self) -> Dict[str, float]: 121 """ 122 Calculates the thresholds for the fixed costs of copying for the incumbent. 123 124 The formulas are tabulated in BaseModel.get_copying_fixed_costs_values. 125 126 Returns 127 ------- 128 Dict[str, float] 129 Includes the thresholds for the fixed costs for copying of the incumbent. 130 """ 131 return {'F(YY)s': self._small_delta / 2, 132 'F(YN)s': self._u + self._small_delta * 3 / 2, 133 'F(YY)c': self._small_delta, 134 'F(YN)c': self._small_delta / 2} 135 136 def _calculate_asset_values(self) -> Dict[str, float]: 137 """ 138 Calculates the thresholds for the assets of the entrant. 139 140 The formulas are tabulated in BaseModel.get_asset_values. 141 142 Returns 143 ------- 144 Dict[str, float] 145 Includes the thresholds for the assets of the entrant. 146 """ 147 return {'A_s': self._B - (self._delta + 3 / 2 * self._small_delta - self._K), 148 'A_c': self._B - (3 / 2 * self._small_delta - self._K), 149 'A-s': self._B - (self._delta - self._K), 150 'A-c': self._B - (1 / 2 * self._small_delta - self._K)} 151 152 def get_asset_values(self) -> Dict[str, float]: 153 """ 154 Returns the asset thresholds of the entrant. 155 156 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 157 |----------------|:----------|:-----------| 158 | $\\underline{A}_S$ | A_s | $(2)\: B + K - \Delta - 3\delta/2$ | 159 | $\\underline{A}_C$ | A_c | $(3)\: B + K - 3\delta/2$ | 160 | $\overline{A}_S$ | A-s | $(4)\: B + K - \Delta$ | 161 | $\overline{A}_C$ | A-c | $(5)\: B + K - \delta/2$ | 162 <br> 163 Returns 164 ------- 165 Dict[str, float] 166 Includes the thresholds for the assets of the entrant. 167 """ 168 return self._assets 169 170 def get_copying_fixed_costs_values(self) -> Dict[str, float]: 171 """ 172 Returns the fixed costs for copying thresholds of the incumbent. 173 174 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 175 |----------|:-------|:--------| 176 | $F^{YY}_S$ | F(YY)s | $(6)\: \delta/2$ | 177 | $F^{YN}_S$ | F(YN)s | $(6)\: u + 3\delta/2$ | 178 | $F^{YY}_C$ | F(YY)c | $(6)\: \delta$ | 179 | $F^{YN}_C$ | F(YN)c | $(6)\: \delta/2$ | 180 <br> 181 Returns 182 ------- 183 Dict[str, float] 184 Includes the thresholds for the fixed costs for copying of the incumbent. 185 """ 186 return self._copying_fixed_costs 187 188 def get_payoffs(self) -> Dict[str, Dict[str, float]]: 189 """ 190 Returns the payoffs for different market configurations. 191 192 A market configuration can include: 193 - $I_P$ : Primary product sold by the incumbent. 194 - $I_C$ : Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$. 195 - $E_P$ : Perfect substitute to $I_P$ potentially sold by the entrant. 196 - $E_C$ : Complementary product to $I_P$ currently sold by the entrant 197 - $\\tilde{E}_C$ : Complementary product to $I_P$ potentially sold by the entrant. 198 <br> 199 200 | Market Config. $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(I) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(E) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | CS $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | W $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 201 |-----------------------|:--------|:--------|:--|:-| 202 | $I_P$ ; $E_C$ | $u + \delta/2$ | $\delta/2$ | 0 | $u + \delta$ | 203 | $I_P + I_C$ ; $E_C$ | $u + \delta$ | 0 | 0 | $u + \delta$ | 204 | $I_P$ ; $E_P + E_C$ | 0 | $\Delta + \delta$ | $u$ | $u + \Delta + \delta$ | 205 | $I_P + I_C$ ; $E_P + E_C$ | 0 | $\Delta$ | $u + \delta$ | $u + \Delta + \delta$ | 206 | $I_P$ ; $E_C + \\tilde{E}_C$ | $u + \delta$ | $\delta$ | 0 | $u + 2\delta$ | 207 | $I_P + I_C$ ; $E_C + \\tilde{E}_C$ | $u + 3\delta/2$ | $\delta/2$ | 0 | $u + 2\delta$ | 208 <br> 209 210 Returns 211 ------- 212 Dict[str, Dict[str, float]] 213 Contains the mentioned payoffs for different market configurations. 214 """ 215 return self._payoffs 216 217 def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: 218 result: Dict[str, str] = {"entrant": "", "incumbent": "", "development": ""} 219 if self._copying_fixed_costs["F(YN)c"] <= F <= self._copying_fixed_costs["F(YN)s"] and A < self._assets["A-s"]: 220 result.update({"entrant": self.ENTRANT_CHOICES["complement"]}) 221 result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) 222 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 223 elif F <= self._copying_fixed_costs["F(YN)c"] and A < self._assets["A-s"]: 224 result.update({"entrant": self.ENTRANT_CHOICES["indifferent"]}) 225 result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) 226 result.update({"development": self.DEVELOPMENT_OUTCOME["failure"]}) 227 else: 228 result.update({"entrant": self.ENTRANT_CHOICES["substitute"]}) 229 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 230 if F <= self._copying_fixed_costs["F(YY)s"]: 231 result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) 232 else: 233 result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) 234 return result 235 236 def _plot(self, coordinates: List[List[Tuple[float, float]]], labels: List[str], 237 axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 238 """ 239 Plots the areas containing the optimal choices and answers into a coordinate system. 240 241 Parameters 242 ---------- 243 coordinates : List[List[Tuple[float, float]]] 244 List of all polygons (list of coordinates) to plot. 245 labels: List[str] 246 List containing all the labels for the areas. 247 axis : matplotlib.axes.Axes 248 Axis to draw the plot on. (optional) 249 **kwargs 250 Optional key word arguments for the plots.<br> 251 - title: title of the plot.<br> 252 - xlabel: label for the x - axis.<br> 253 - ylabel: label for the y - axis.<br> 254 - options_legend: If true, an additional legend, explaining the options of the entrant and the incumbent, will be added to the plot.<br> 255 - asset_legend: If true, an additional legend explaining the thresholds of the assets of the entrant will be added to the plot.<br> 256 - costs_legend: If true, an additional legend explaining the thresholds of the fixed costs of copying for the incumbent will be added to the plot.<br> 257 - legend_width : Maximum number of characters in one line in the legend (for adjustments to figure width).<br> 258 - x_max : Maximum number plotted on the x - axis.<br> 259 - y_max : Maximum number plotted on the y - axis.<br> 260 261 Returns 262 ------- 263 Axis containing the plot. 264 """ 265 if axis is None: 266 plot_fig, axis = plt.subplots() 267 self._draw_thresholds(axis, x_horizontal=kwargs.get("x_max", 0), y_vertical=kwargs.get("y_max", 0)) 268 269 for i, coordinates in enumerate(coordinates): 270 poly = plt.Polygon(coordinates, linewidth=0, color=self._get_color(i), label=labels[i]) 271 axis.add_patch(poly) 272 273 if kwargs.get("legend", True): 274 axis.legend(bbox_to_anchor=(1.3, 1), loc="upper left") 275 additional_legend: str = self._create_additional_legend(options_legend=kwargs.get('options_legend', False), 276 assets_thresholds_legend=kwargs.get('asset_legend', False), 277 costs_thresholds_legend=kwargs.get('costs_legend', False), 278 width=kwargs.get('legend_width', 60)) 279 if additional_legend != "": 280 axis.text(-0.1, -0.6, additional_legend, verticalalignment='top', linespacing=1, wrap=True) 281 282 BaseModel._set_axis_labels(axis, title=kwargs.get('title', ''), 283 x_label=kwargs.get('xlabel', 'Assets of the entrant'), 284 y_label=kwargs.get('ylabel', 'Fixed costs of copying for the incumbent')) 285 BaseModel._set_axis(axis) 286 return axis 287 288 def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 289 poly_coordinates: List[List[Tuple[float, float]]] = self._get_incumbent_best_answer_coordinates( 290 kwargs.get("x_max", 0), 291 kwargs.get("y_max", 0)) 292 poly_labels: List[str] = self._get_incumbent_best_answer_labels() 293 kwargs.update({'title': kwargs.get('title', "Best Answers of the incumbent to the choices of the entrant")}) 294 return self._plot(coordinates=poly_coordinates, labels=poly_labels, axis=axis, **kwargs) 295 296 def _create_choice_answer_label(self, entrant: Literal["complement", "substitute", "indifferent"], 297 incumbent: Literal["copy", "refrain"], 298 development: Literal["success", "failure"], 299 kill_zone: bool = False, acquisition: str = "") -> str: 300 """ 301 Creates a label for the legend based on the choice of the entrant, the incumbent, the development outcome and additionally on possible acquisition. 302 303 Parameters 304 ---------- 305 entrant: Literal["complement", "substitute", "indifferent"] 306 choice of the entrant. 307 incumbent: Literal["copy", "refrain"] 308 choice of the incumbent. 309 development: Literal["success", "failure"] 310 outcome of the development. 311 kill_zone: bool 312 If true, the label adds a "(Kill Zone)" tag. 313 acquisition: str 314 The entity, which develops the additional product chosen by the entrant. 315 316 Returns 317 ------- 318 str 319 label based on the parameters mentioned above. 320 """ 321 if acquisition != "": 322 acquisition = "_" + acquisition 323 return self.ENTRANT_CHOICES[entrant] + " $\\rightarrow$ " + self.INCUMBENT_CHOICES[ 324 incumbent] + " $\\rightarrow " + self.DEVELOPMENT_OUTCOME[development] + acquisition + "$" + ( 325 "\n(Kill Zone)" if kill_zone else "") 326 327 def _get_incumbent_best_answer_labels(self) -> List[str]: 328 """ 329 Returns a list containing the labels for the squares in the plot of the best answers of the incumbent to the choice of the entrant. 330 331 For the order of the labels refer to the file resources/dev_notes.md. 332 333 Returns 334 ------- 335 List containing the labels for the squares in the plot of the best answers of the incumbent to the choice of the entrant. 336 """ 337 return [ 338 # Area 1 339 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="failure") + " \n" + 340 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="failure"), 341 # Area 2 342 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success") + " \n" + 343 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="failure"), 344 # Area 3 345 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success") + " \n" + 346 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="success"), 347 # Area 4 348 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="failure") + " \n" + 349 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success"), 350 # Area 5 351 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") + " \n" + 352 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="success"), 353 # Area 6 354 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") + " \n" + 355 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success"), 356 ] 357 358 def _get_incumbent_best_answer_coordinates(self, x_max: float, y_max: float) -> List[List[Tuple[float, float]]]: 359 """ 360 Returns a list containing the coordinates for the areas in the plot of the best answers of the incumbent to the choice of the entrant. 361 362 For the order of the areas refer to the file resources/dev_notes.md. 363 364 Returns 365 ------- 366 List[List[Tuple[float, float]]] 367 List containing the coordinates for the areas in the plot of the best answers of the incumbent to the choice of the entrant. 368 """ 369 y_max = self._get_y_max(y_max) 370 x_max = self._get_x_max(x_max) 371 return [ 372 # Area 1 373 [(0, 0), (self._assets['A-s'], 0), (self._assets['A-s'], max(self._copying_fixed_costs['F(YN)c'], 0)), 374 (0, max(self._copying_fixed_costs['F(YN)c'], 0))], 375 # Area 2 376 [(self._assets['A-s'], 0), (self._assets['A-c'], 0), 377 (self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), 378 (self._assets['A-s'], self._copying_fixed_costs['F(YY)s'])], 379 # Area 3 380 [(self._assets['A-c'], 0), (x_max, 0), (x_max, self._copying_fixed_costs['F(YY)s']), 381 (self._assets['A-c'], self._copying_fixed_costs['F(YY)s'])], 382 # Area 4 383 [(0, max(self._copying_fixed_costs['F(YN)c'], 0)), 384 (self._assets['A-s'], max(self._copying_fixed_costs['F(YN)c'], 0)), 385 (self._assets['A-s'], self._copying_fixed_costs['F(YN)s']), (0, self._copying_fixed_costs['F(YN)s'])], 386 # Area 5 387 [(self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), (x_max, self._copying_fixed_costs['F(YY)s']), 388 (x_max, self._copying_fixed_costs['F(YY)c']), (self._assets['A-c'], self._copying_fixed_costs['F(YY)c'])], 389 # Area 6 390 [(self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), 391 (self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), 392 (self._assets['A-c'], self._copying_fixed_costs['F(YY)c']), (x_max, self._copying_fixed_costs['F(YY)c']), 393 (x_max, y_max), (0, y_max), 394 (0, self._copying_fixed_costs['F(YN)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YN)s'])]] 395 396 def plot_equilibrium(self, axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 397 poly_coordinates: List[List[Tuple[float, float]]] = self._get_equilibrium_coordinates(kwargs.get("x_max", 0), 398 kwargs.get("y_max", 0)) 399 poly_labels: List[str] = self._get_equilibrium_labels() 400 kwargs.update({'title': kwargs.get('title', 'Equilibrium Path')}) 401 return self._plot(coordinates=poly_coordinates, labels=poly_labels, axis=axis, **kwargs) 402 403 def _get_equilibrium_labels(self) -> List[str]: 404 """ 405 Returns a list containing the labels for the squares in the plot of the equilibrium path. 406 407 For the order of the squares refer to the file resources/dev_notes.md. 408 409 Returns 410 ------- 411 List[str] 412 List containing the labels for the squares in the plot of the best answers of the equilibrium path. 413 """ 414 return [ 415 # Area 1 416 self._create_choice_answer_label(entrant="indifferent", incumbent="copy", development="failure"), 417 # Area 2 418 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success"), 419 # Area 3 420 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success", 421 kill_zone=True), 422 # Area 4 423 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") 424 ] 425 426 def _get_equilibrium_coordinates(self, x_max: float, y_max: float) -> List[List[Tuple[float, float]]]: 427 """ 428 Returns a list containing the coordinates for the areas in the plot of the equilibrium path. 429 430 For the order of the areas refer to the file resources/dev_notes.md. 431 432 Returns 433 ------- 434 List[List[Tuple[float, float]]] 435 List containing the coordinates for the areas in the plot of the best answers of the equilibrium path. 436 """ 437 y_max = self._get_y_max(y_max) 438 x_max = self._get_x_max(x_max) 439 return [ 440 # Area 1 441 [(0, 0), (self._assets['A-s'], 0), (self._assets['A-s'], max(self._copying_fixed_costs['F(YN)c'], 0)), 442 (0, max(self._copying_fixed_costs['F(YN)c'], 0))], 443 # Area 2 444 [(self._assets['A-s'], 0), (x_max, 0), (x_max, self._copying_fixed_costs['F(YY)s']), 445 (self._assets['A-s'], self._copying_fixed_costs['F(YY)s'])], 446 # Area 3 447 [(0, max(self._copying_fixed_costs['F(YN)c'], 0)), 448 (self._assets['A-s'], max(self._copying_fixed_costs['F(YN)c'], 0)), 449 (self._assets['A-s'], self._copying_fixed_costs['F(YN)s']), (0, self._copying_fixed_costs['F(YN)s'])], 450 # Area 4 451 [(self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (x_max, self._copying_fixed_costs['F(YY)s']), 452 (x_max, y_max), (0, y_max), (0, self._copying_fixed_costs['F(YN)s']), 453 (self._assets['A-s'], self._copying_fixed_costs['F(YN)s'])]] 454 455 def plot_payoffs(self, axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 456 if axis is None: 457 plot_fig, axis = plt.subplots() 458 index = arange(0, len(self._payoffs) * 2, 2) 459 bar_width = 0.35 460 spacing = 0.05 461 462 self._plot_payoffs_bars(axis, bar_width, index, spacing, **kwargs) 463 464 axis.set_xlabel('Market Configurations') 465 axis.set_title('Payoffs for different Market Configurations') 466 self._set_payoffs_ticks(axis, bar_width, index, spacing) 467 if kwargs.get("legend", True): 468 self._set_payoff_legend(axis, kwargs.get("products_legend", False)) 469 self._set_payoffs_figure(axis) 470 return axis 471 472 def _plot_payoffs_bars(self, axis: matplotlib.axes.Axes, bar_width: float, index: array, spacing: float, 473 **kwargs) -> None: 474 """ 475 Plots the bars representing the payoffs for different market configurations of different stakeholders on the specified axis. 476 477 Parameters 478 ---------- 479 axis matplotlib.axes.Axes 480 To plot the bars on. 481 bar_width: float 482 Width of a bar in the plot. 483 index: np.array 484 Index of the different market configurations in the plot. 485 spacing: float 486 Spacing between the bars on the plot. 487 **kwargs 488 Optional key word arguments for the payoff plot.<br> 489 - opacity : Opacity of the not optimal payoffs.<br> 490 """ 491 for counter, utility_type in enumerate(self._payoffs[list(self._payoffs.keys())[0]].keys()): 492 utility_values: List[float] = [] 493 for market_configuration in self._payoffs: 494 utility_values.append(self._payoffs[market_configuration][utility_type]) 495 496 bars = axis.bar(index + counter * (bar_width + spacing), utility_values, bar_width, 497 alpha=kwargs.get("opacity", 0.2), 498 color=self._get_color(counter), 499 edgecolor=None, 500 label=self._convert_payoffs_label(utility_type)) 501 max_indices: List[int] = list( 502 filter(lambda x: utility_values[x] == max(utility_values), range(len(utility_values)))) 503 for max_index in max_indices: 504 bars[max_index].set_alpha(1) 505 506 def _set_payoff_legend(self, axis: matplotlib.axes.Axes, products_legend: bool = False) -> None: 507 """ 508 Creates the legend and an additional legend for the products of the entrant and the incumbent, 509 510 Parameters 511 ---------- 512 axis: matplotlib.axes.Axes 513 To set the legends for. 514 products_legend: bool 515 If true, an additional legend, containing all possible products of the entrant and the incumbent, will be created. 516 """ 517 axis.legend(bbox_to_anchor=(1.02, 1), loc='upper left', ncol=1) 518 if products_legend: 519 axis.text(-0.7, -0.8, self._get_market_configuration_annotations(), verticalalignment="top") 520 521 def _set_payoffs_ticks(self, axis: matplotlib.axes.Axes, bar_width: float, index: array, spacing: float) -> None: 522 """ 523 Sets the x - and y - ticks for the plot of the payoffs for different market configurations. 524 525 Parameters 526 ---------- 527 axis matplotlib.axes.Axes 528 To adjust the ticks on. 529 bar_width: float 530 Width of a bar in the plot. 531 index: np.array 532 Index of the different market configurations in the plot. 533 spacing: float 534 Spacing between the bars on the plot. 535 """ 536 axis.set(yticklabels=[]) 537 axis.tick_params(left=False) 538 axis.set_xticks(index + 1.5 * (bar_width + spacing)) 539 axis.set_xticklabels(tuple([self._convert_market_configuration_label(i) for i in self._payoffs.keys()])) 540 541 @staticmethod 542 def _set_payoffs_figure(axis: matplotlib.axes.Axes) -> None: 543 """ 544 Adjust the matplotlib figure to plot the payoffs for different market configurations. 545 546 Parameters 547 ---------- 548 axis: matplotlib.axes.Axes 549 To adjust for the payoff plot. 550 """ 551 axis.figure.set_size_inches(10, 5) 552 axis.figure.tight_layout() 553 554 @staticmethod 555 def _get_market_configuration_annotations() -> str: 556 """ 557 Returns a string containing all product options for the entrant and the incumbent. 558 559 Returns 560 ------- 561 str 562 Contains all product options for the entrant and the incumbent. 563 """ 564 return "$I_P$: Primary product sold by the incumbent\n" \ 565 "$I_C$: Copied complementary product to $I_P$ potentially sold by the incumbent\n" \ 566 "$E_P$: Perfect substitute to $I_P$ potentially sold by the entrant\n" \ 567 "$E_C$: Complementary product to $I_P$ currently sold by the entrant\n" \ 568 "$\\tilde{E}_C$: Complementary product to $I_P$ potentially sold by the entrant\n" \ 569 "\nThe bars representing the maximum payoff for a stakeholder are fully filled." 570 571 @staticmethod 572 def _convert_payoffs_label(raw_label: str) -> str: 573 """ 574 Converts keys of the payoffs dict to latex labels. 575 576 Parameters 577 ---------- 578 raw_label: str 579 As given as key in the payoffs dict. 580 581 Returns 582 ------- 583 str 584 Latex compatible pretty label. 585 """ 586 label: str = raw_label.replace("pi", "$\pi$") 587 label = label.replace("CS", "Consumer Surplus") 588 label = label.replace("W", "Welfare") 589 return label 590 591 @staticmethod 592 def _convert_market_configuration_label(raw_label: str) -> str: 593 """ 594 Returns the latex string for a specific market configuration. 595 596 Parameters 597 ---------- 598 raw_label 599 Of the market configuration as given as key in the payoffs dict. 600 601 Returns 602 ------- 603 str 604 Corresponding latex label for the market configuration as given as key in the payoffs dict. 605 """ 606 labels: Dict[str] = {"basic": "$I_P;E_C$", 607 "I(C)": "$I_P+I_C;E_C$", 608 "E(P)": "$I_P;E_C+E_P$", 609 "I(C)E(P)": "$I_P+I_C;E_C+E_P$", 610 "E(C)": "$I_P;E_C+\\tilde{E}_C$", 611 "I(C)E(C)": "$I_P+I_C;E_C+\\tilde{E}_C$"} 612 return labels.get(raw_label, 'No valid market configuration') 613 614 def _get_x_max(self, x_max: float = 0) -> float: 615 """ 616 Returns the maximum value to plot on the x - axis. 617 618 Parameters 619 ---------- 620 x_max: float 621 Preferred value for the maximum value on the x - axis. 622 623 Returns 624 ------- 625 float 626 Maximum value (which is feasible) to plot on the x - axis. 627 """ 628 auto_x_max: float = round(self._assets['A-c'] * 1.3, 1) 629 return x_max if x_max > self._assets['A-c'] else auto_x_max 630 631 def _get_y_max(self, y_max: float = 0) -> float: 632 """ 633 Returns the maximum value to plot on the y - axis. 634 635 Parameters 636 ---------- 637 y_max: float 638 Preferred value for the maximum value on the y - axis. 639 640 Returns 641 ------- 642 float 643 Maximum value (which is feasible) to plot on the y - axis. 644 """ 645 auto_y_max: float = round(self._copying_fixed_costs['F(YN)s'] * 1.3, 1) 646 return y_max if y_max > self._copying_fixed_costs['F(YN)s'] else auto_y_max 647 648 def _draw_thresholds(self, axis: matplotlib.axes.Axes, x_horizontal: float = 0, y_vertical: float = 0) -> None: 649 """ 650 Draws the thresholds and the corresponding labels on a given axis. 651 652 Parameters 653 ---------- 654 axis: matplotlib.axes.Axes 655 Axis to draw the thresholds on. 656 x_horizontal : float 657 X - coordinate for horizontal thresholds labels (fixed costs of copying). 658 y_vertical : float 659 Y - coordinate for vertical thresholds labels (assets of the entrant). 660 """ 661 # horizontal lines (fixed cost of copying thresholds) 662 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YN)s'], label="$F^{YN}_S$", 663 x=x_horizontal) 664 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YY)c'], label="$F^{YY}_C$", 665 x=x_horizontal) 666 if abs(self._copying_fixed_costs['F(YY)s'] - self._copying_fixed_costs['F(YN)c']) < BaseModel.TOLERANCE: 667 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YY)s'], 668 label="$F^{YY}_S=F^{YN}_C$", x=x_horizontal) 669 else: 670 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YY)s'], label="$F^{YY}_S$", 671 x=x_horizontal) 672 if self._copying_fixed_costs['F(YN)c'] >= 0: 673 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YN)c'], label="$F^{YN}_C$", 674 x=x_horizontal) 675 # vertical lines (asset thresholds) 676 self._draw_vertical_line_with_label(axis, x=self._assets['A-s'], label=r'$\bar{A}_S$', y=y_vertical) 677 self._draw_vertical_line_with_label(axis, x=self._assets['A-c'], label=r'$\bar{A}_C$', y=y_vertical) 678 679 def _draw_horizontal_line_with_label(self, axis: matplotlib.axes.Axes, y: float, **kwargs) -> None: 680 """ 681 Draws a horizontal line at a given y - coordinate and writes the corresponding label at the edge. 682 683 Parameters 684 ---------- 685 axis 686 To draw the horizontal line and label on. 687 y 688 Coordinate of the of the line on the y - axis. 689 **kwargs 690 Optional key word arguments for the equilibrium plot.<br> 691 - label: Label for the horizontal line written at the edge.<br> 692 - x: X - coordinate for horizontal thresholds labels (fixed costs of copying).<br> 693 """ 694 label_x: float = self._get_x_max(kwargs.get("x", 0)) + 0.05 695 axis.axhline(y, linestyle='--', color='k') 696 axis.text(label_x, y, kwargs.get("label", "")) 697 698 def _draw_vertical_line_with_label(self, axis: matplotlib.axes.Axes, x: float, **kwargs) -> None: 699 """ 700 Draws a vertical line at a given x - coordinate and writes the corresponding label at the edge. 701 702 Parameters 703 ---------- 704 axis 705 To draw the vertical line and label on. 706 x 707 Coordinate of the of the line on the x - axis. 708 **kwargs 709 Optional key word arguments for the equilibrium plot.<br> 710 - label: Label for the horizontal line written at the edge.<br> 711 - y: Y - coordinate for vertical thresholds labels (assets of the entrant).<br> 712 """ 713 label_y: float = self._get_y_max(kwargs.get("y", 0)) + 0.15 714 axis.axvline(x, linestyle='--', color='k') 715 axis.text(x, label_y, kwargs.get("label", "")) 716 717 def _create_additional_legend(self, options_legend: bool, assets_thresholds_legend: bool, costs_thresholds_legend: bool, width: int) -> str: 718 """ 719 Handles the creation of the additional legend for the options of the entrant and incumbent as well as the legend for the thresholds. 720 721 Parameters 722 ---------- 723 options_legend: bool 724 States all options of the entrant and the incumbent. 725 assets_thresholds_legend 726 States the thresholds for the assets of the entrant used in the plots. 727 costs_thresholds_legend 728 States the thresholds for the fixed costs of copying of the incumbent used in the plots. 729 730 Returns 731 ------- 732 str 733 Containing the legend for the options of the entrant and the incumbent as well as the legend for the thresholds. 734 """ 735 legend: str = "" 736 if options_legend: 737 legend += self._create_options_legend(width=width) 738 if assets_thresholds_legend: 739 legend += "\n\n" if options_legend else "" 740 legend += self._create_asset_thresholds_legend(width=width) 741 if costs_thresholds_legend: 742 legend += "\n\n" if options_legend or assets_thresholds_legend else "" 743 legend += self._create_cost_thresholds_legend(width=width) 744 return legend 745 746 def _create_options_legend(self, width: int) -> str: 747 """ 748 Creates a legend for the options of the entrant and the incumbent. 749 750 Returns 751 ------- 752 str 753 Containing the legend for the options of the entrant and the incumbent. 754 """ 755 return "Options of the entrant:\n" + \ 756 self._format_legend_line(self.ENTRANT_CHOICES['complement'] + ": Develop an additional complementary product to a primary product.", width=width) + "\n" + \ 757 self._format_legend_line(self.ENTRANT_CHOICES['substitute'] + ": Develop an substitute to the primary product of the incumbent.", width=width) + "\n" + \ 758 self._format_legend_line(self.ENTRANT_CHOICES['indifferent'] + " : Indifferent between the options mentioned above.", width=width) + "\n" + \ 759 "\nOptions of the incumbent:\n" + \ 760 self._format_legend_line(self.INCUMBENT_CHOICES['copy'] + " : Copy the original complement of the entrant.", width=width) + "\n" + \ 761 self._format_legend_line(self.INCUMBENT_CHOICES['refrain'] + " : Do not copy the original complement of the entrant.", width=width) + "\n" + \ 762 "\nOutcomes of the development:\n" + \ 763 self._format_legend_line(self.DEVELOPMENT_OUTCOME['success'] + " : The entrant has sufficient assets to develop the product.", width=width) + "\n" + \ 764 self._format_legend_line(self.DEVELOPMENT_OUTCOME['failure'] + " : The entrant has not sufficient assets to develop the product.", width=width) 765 766 @staticmethod 767 def _create_asset_thresholds_legend(width: int) -> str: 768 """ 769 Creates a legend for the asset of the entrant thresholds used in the plots. The legend is compatible with latex. 770 771 Returns 772 ------- 773 str 774 Containing the legend for the thresholds used in the plots. 775 """ 776 return "Thresholds for the assets of the entrant:\n" + \ 777 BaseModel._format_legend_line(r'$\bar{A}_S$' + ": Minimum level of assets to ensure a perfect substitute gets funded if the incumbent copies.", width=width) + "\n" + \ 778 BaseModel._format_legend_line(r'$\bar{A}_S$' + ": Minimum level of assets to ensure a perfect substitute gets funded if the incumbent copies.", width=width) + "\n" + \ 779 BaseModel._format_legend_line(r'$\bar{A}_C$' + ": Minimum level of assets to ensure another complement gets funded if the incumbent copies.", width=width) + "\n" + \ 780 BaseModel._format_legend_line("If the incumbent does not copy, the entrant will have sufficient assets.", width=width) 781 782 @staticmethod 783 def _create_cost_thresholds_legend(width: int) -> str: 784 """ 785 Creates a legend for the thresholds used in the plots. The legend is compatible with latex. 786 787 Returns 788 ------- 789 str 790 Containing the legend for the thresholds used in the plots. 791 """ 792 return "Thresholds for the fixed costs of copying for the incumbent:\n" + \ 793 BaseModel._format_legend_line(r'$F^{YY}_S$' + ": Maximum costs of copying that ensure that the incumbent copies the entrant if the entrant is guaranteed to invest in a perfect substitute.", width=width) + "\n" + \ 794 BaseModel._format_legend_line(r'$F^{YN}_S$' + ": Maximum costs of copying that ensure that the incumbent copies the entrant if the copying prevents the entrant from developing a perfect substitute.", width=width) + "\n" + \ 795 BaseModel._format_legend_line(r'$F^{YY}_C$' + ": Maximum costs of copying that ensure that the incumbent copies the entrant if the entrant is guaranteed to invest in another complement.", width=width) + "\n" + \ 796 BaseModel._format_legend_line(r'$F^{YN}_C$' + ": Maximum costs of copying that ensure that the incumbent copies the entrant if the copying prevents the entrant from developing another complement.", width=width) 797 798 @staticmethod 799 def _format_legend_line(line: str, width: int = 60, latex: bool = True) -> str: 800 space: str = "$\quad$" if latex else " " * 4 801 return textwrap.fill(line, width=width, initial_indent='', subsequent_indent=space * 3) 802 803 @staticmethod 804 def _get_color(i: int) -> str: 805 """ 806 Returns a string corresponding to a matplotlib - color for a given index. 807 808 The index helps to get different colors for different items, when iterating over list/dict/etc.. 809 810 Parameters 811 ---------- 812 i: int 813 Index of the color. 814 Returns 815 ------- 816 str 817 A string corresponding to a matplotlib - color for a given index. 818 """ 819 return ['salmon', 'khaki', 'limegreen', 'turquoise', 'powderblue', 'thistle', 'pink'][i] 820 821 @staticmethod 822 def _set_axis(axis: matplotlib.axes.Axes) -> None: 823 """ 824 Adjusts the axis to the given viewport. 825 826 Parameters 827 ---------- 828 axis: matplotlib.axes.Axes 829 To adjust to the given viewport. 830 """ 831 axis.autoscale_view() 832 axis.figure.tight_layout() 833 834 @staticmethod 835 def _set_axis_labels(axis: matplotlib.axes.Axes, title: str = "", x_label: str = "", y_label: str = "") -> None: 836 """ 837 Sets all the labels for a plot, containing the title, x - label and y - label. 838 839 Parameters 840 ---------- 841 axis 842 Axis to set the labels for. 843 title 844 Title of the axis. 845 x_label 846 Label of the x - axis. 847 y_label 848 Label of the y - axis. 849 """ 850 axis.set_title(title, loc='left', y=1.1) 851 axis.set_xlabel(x_label) 852 axis.set_ylabel(y_label) 853 854 def __str__(self) -> str: 855 str_representation = self._create_asset_str() 856 857 str_representation += "\n" + self._create_copying_costs_str() 858 859 str_representation += "\n" + self._create_payoff_str() 860 861 return str_representation 862 863 def _create_payoff_str(self): 864 """ 865 Creates a string representation for the payoffs of different stakeholder for different market configurations. 866 867 See Shelegia_Motta_2021.IModel.get_payoffs for the formulas of the payoffs. 868 869 Returns 870 ------- 871 str 872 String representation for the payoffs of different stakeholder for different market configurations 873 """ 874 market_configurations: List[str] = list(self._payoffs.keys()) 875 str_representation = 'Payoffs for different Market Configurations:\n\t' + ''.join( 876 ['{0: <14}'.format(item) for item in market_configurations]) 877 for utility_type in self._payoffs[market_configurations[0]].keys(): 878 str_representation += '\n\t' 879 for market_configuration in market_configurations: 880 str_representation += '-' + '{0: <4}'.format(utility_type).replace('pi', 'π') + ': ' + '{0: <5}'.format( 881 str(self._payoffs[market_configuration][utility_type])) + '| ' 882 return str_representation 883 884 def _create_copying_costs_str(self): 885 """ 886 Creates a string representation for the fixed costs of copying for the incumbent. 887 888 See Shelegia_Motta_2021.IModel.get_copying_fixed_costs_values for the formulas of the fixed costs of copying. 889 890 Returns 891 ------- 892 str 893 String representation for the fixed costs of copying for the incumbent. 894 """ 895 str_representation = 'Costs for copying:' 896 for key in self._copying_fixed_costs.keys(): 897 str_representation += '\n\t- ' + key + ':\t' + str(self._copying_fixed_costs[key]) 898 return str_representation 899 900 def _create_asset_str(self): 901 """ 902 Creates a string representation for the assets of the entrant. 903 904 See Shelegia_Motta_2021.IModel.get_asset_values for the formulas of the assets of the entrant. 905 906 Returns 907 ------- 908 str 909 String representation for the assets of the entrant. 910 """ 911 str_representation: str = 'Assets:' 912 for key in self._assets: 913 str_representation += '\n\t- ' + key + ':\t' + str(self._assets[key]) 914 return str_representation 915 916 def __call__(self, A: float, F: float) -> Dict[str, str]: 917 """ 918 Makes the object callable and will return the equilibrium for a given pair of copying fixed costs of the incumbent 919 and assets of the entrant. 920 921 See Shelegia_Motta_2021.IModel.get_optimal_choice for further documentation. 922 """ 923 return self.get_optimal_choice(A=A, F=F) 924 925 926class BargainingPowerModel(BaseModel): 927 """ 928 Besides the parameters used in the paper (and in the BaseModel), this class will introduce the parameter $\\beta$ in the models, called 929 the bargaining power of the incumbent. $\\beta$ describes how much of the profits from the complementary product of the entrant will go to the incumbent 930 In the paper the default value $\\beta=0.5$ is used to derive the results, which indicate an equal share of the profits. 931 """ 932 933 def __init__(self, u: float = 1, B: float = 0.5, small_delta: float = 0.5, delta: float = 0.51, 934 K: float = 0.2, beta: float = 0.5): 935 """ 936 Besides $\\beta$ the parameters in this model do not change compared to Shelegia_Motta_2021.Models.BaseModel. 937 938 Parameters 939 ---------- 940 beta: float 941 Bargaining power of the incumbent relative to the entrant ($0 < \\beta < 1$). 942 """ 943 assert 0 < beta < 1, 'Invalid bargaining power beta (has to be between 0 and 1).' 944 self._beta: float = beta 945 super(BargainingPowerModel, self).__init__(u=u, B=B, small_delta=small_delta, delta=delta, K=K) 946 947 def _calculate_payoffs(self) -> Dict[str, Dict[str, float]]: 948 """ 949 Calculates the payoffs for different market configurations with the formulas given in the paper. 950 951 The formulas are tabulated in BargainingPowerModel.get_payoffs, which are different to the BaseModel. 952 953 Returns 954 ------- 955 Dict[str, Dict[str, float]] 956 Contains the mentioned payoffs for different market configurations. 957 """ 958 payoffs: Dict[str, Dict[str, float]] = super()._calculate_payoffs() 959 # basic market. 960 payoffs['basic']['pi(I)'] = self._u + self._small_delta * self._beta 961 payoffs['basic']['pi(E)'] = self._small_delta * (1 - self._beta) 962 963 # additional complement of the entrant 964 payoffs['E(C)']['pi(I)'] = self._u + 2 * self._small_delta * self._beta 965 payoffs['E(C)']['pi(E)'] = 2 * self._small_delta * (1 - self._beta) 966 967 # additional complement of the incumbent and the entrant 968 payoffs['I(C)E(C)']['pi(I)'] = self._u + self._small_delta * (1 + self._beta) 969 payoffs['I(C)E(C)']['pi(E)'] = self._small_delta * (1 - self._beta) 970 971 return payoffs 972 973 def _calculate_copying_fixed_costs_values(self) -> Dict[str, float]: 974 """ 975 Calculates the thresholds for the fixed costs of copying for the incumbent. 976 977 The formulas are tabulated in BargainingPowerModel.get_copying_fixed_costs_values, which are different to the BaseModel. 978 979 Returns 980 ------- 981 Dict[str, float] 982 Includes the thresholds for the fixed costs for copying of the incumbent. 983 """ 984 return {'F(YY)s': self._small_delta * (1 - self._beta), 985 'F(YN)s': self._u + self._small_delta * (2 - self._beta), 986 'F(YY)c': 2 * self._small_delta * (1 - self._beta), 987 'F(YN)c': self._small_delta * (2 - 3 * self._beta)} 988 989 def _calculate_asset_values(self) -> Dict[str, float]: 990 """ 991 Calculates the thresholds for the assets of the entrant. 992 993 The formulas are tabulated in BargainingPowerModel.get_asset_values, which are different to the BaseModel. 994 995 Returns 996 ------- 997 Dict[str, float] 998 Includes the thresholds for the assets of the entrant. 999 """ 1000 return {'A_s': self._K + self._B - self._delta - self._small_delta * (2 - self._beta), 1001 'A_c': self._K + self._B - 3 * self._small_delta * (1 - self._beta), 1002 'A-s': self._K + self._B - self._delta, 1003 'A-c': self._K + self._B - self._small_delta * (1 - self._beta)} 1004 1005 def get_asset_values(self) -> Dict[str, float]: 1006 """ 1007 Returns the asset thresholds of the entrant. 1008 1009 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 1010 |----------------|:----------|:-----------| 1011 | $\\underline{A}_S$ | A_s | $B + K - \Delta - \delta(2 - \\beta)$ | 1012 | $\\underline{A}_C$ | A_c | $B + K - 3\delta(1 - \\beta)$ | 1013 | $\overline{A}_S$ | A-s | $B + K - \Delta$ | 1014 | $\overline{A}_C$ | A-c | $B + K - \delta(1 - \\beta)$ | 1015 <br> 1016 Returns 1017 ------- 1018 Dict[str, float] 1019 Includes the thresholds for the assets of the entrant. 1020 """ 1021 return self._assets 1022 1023 def get_copying_fixed_costs_values(self) -> Dict[str, float]: 1024 """ 1025 Returns the fixed costs for copying thresholds of the incumbent. 1026 1027 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 1028 |----------|:-------|:--------| 1029 | $F^{YY}_S$ | F(YY)s | $\delta(1 - \\beta)$ | 1030 | $F^{YN}_S$ | F(YN)s | $u + \delta(2 - \\beta)$ | 1031 | $F^{YY}_C$ | F(YY)c | $2\delta(1 - \\beta)$ | 1032 | $F^{YN}_C$ | F(YN)c | $\delta(2 - \\beta)$ | 1033 <br> 1034 Returns 1035 ------- 1036 Dict[str, float] 1037 Includes the thresholds for the fixed costs for copying of the incumbent. 1038 """ 1039 return self._copying_fixed_costs 1040 1041 def get_payoffs(self) -> Dict[str, Dict[str, float]]: 1042 """ 1043 Returns the payoffs for different market configurations. 1044 1045 A market configuration can include: 1046 - $I_P$ : Primary product sold by the incumbent. 1047 - $I_C$ : Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$. 1048 - $E_P$ : Perfect substitute to $I_P$ potentially sold by the entrant. 1049 - $E_C$ : Complementary product to $I_P$ currently sold by the entrant 1050 - $\\tilde{E}_C$ : Complementary product to $I_P$ potentially sold by the entrant. 1051 <br> 1052 1053 | Market Config. $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(I) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(E) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | CS $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | W $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 1054 |-----------------------|:--------|:--------|:--|:-| 1055 | $I_P$ ; $E_C$ | $u + \delta\\beta$ | $\delta(1 - \\beta)$ | 0 | $u + \delta$ | 1056 | $I_P + I_C$ ; $E_C$ | $u + \delta$ | 0 | 0 | $u + \delta$ | 1057 | $I_P$ ; $E_P + E_C$ | 0 | $\Delta + \delta$ | $u$ | $u + \Delta + \delta$ | 1058 | $I_P + I_C$ ; $E_P + E_C$ | 0 | $\Delta$ | $u + \delta$ | $u + \Delta + \delta$ | 1059 | $I_P$ ; $E_C + \\tilde{E}_C$ | $u + 2\delta\\beta$ | $2\delta(1 - \\beta)$ | 0 | $u + 2\delta$ | 1060 | $I_P + I_C$ ; $E_C + \\tilde{E}_C$ | $u + \delta(1 + \\beta)$ | $\delta(1 - \\beta)$ | 0 | $u + 2\delta$ | 1061 <br> 1062 1063 Returns 1064 ------- 1065 Dict[str, Dict[str, float]] 1066 Contains the mentioned payoffs for different market configurations. 1067 """ 1068 return self._payoffs 1069 1070 def _get_incumbent_best_answer_coordinates(self, x_max: float, y_max: float) -> List[List[Tuple[float, float]]]: 1071 coordinates: List[List[Tuple[float, float]]] = super(BargainingPowerModel, 1072 self)._get_incumbent_best_answer_coordinates(x_max=x_max, 1073 y_max=y_max) 1074 # add additional area 7 1075 if self._copying_fixed_costs["F(YY)s"] != self._copying_fixed_costs["F(YN)c"]: 1076 coordinates.append([(self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), 1077 (self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), 1078 (self._assets['A-c'], max(self._copying_fixed_costs['F(YN)c'], 0)), 1079 (self._assets['A-s'], max(self._copying_fixed_costs['F(YN)c'], 0))]) 1080 return coordinates 1081 1082 def _get_incumbent_best_answer_labels(self) -> List[str]: 1083 labels: List[str] = super(BargainingPowerModel, self)._get_incumbent_best_answer_labels() 1084 # add additional label for area 7 1085 if self._copying_fixed_costs["F(YY)s"] != self._copying_fixed_costs["F(YN)c"]: 1086 if self._copying_fixed_costs["F(YY)s"] > self._copying_fixed_costs["F(YN)c"]: 1087 labels.append( 1088 # Area 7 1089 self._create_choice_answer_label(entrant="substitute", incumbent="copy", 1090 development="success") + " \n" + 1091 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success"), 1092 ) 1093 else: 1094 labels.append( 1095 # Area 7 1096 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", 1097 development="success") + " \n" + 1098 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="failure"), 1099 ) 1100 return labels 1101 1102 1103class UnobservableModel(BargainingPowerModel): 1104 """ 1105 This model indicates that if the incumbent were not able to observe the entrant at the moment of choosing, 1106 the “kill zone” effect whereby the entrant stays away from the substitute in order to avoid being copied would not take place. 1107 Intuitively, in the game as we studied it so far, the only reason why the entrant is choosing a trajectory leading to another complement 1108 is that it anticipates that if it chose one leading to a substitute, the incumbent would copy, making it an inefficient strategy 1109 for entering the market. However, if the incumbent cannot observe the entrant’s choice of strategy, the entrant could not hope to strategically affect the decision 1110 of the incumbent. This would lead to the entrant having a host of new opportunities when entering the market makes the entrant competing with a large company much more attractive. 1111 1112 Although there may be situations where the entrant could commit to some actions (product design or marketing choices) 1113 which signals that it will not become a rival, and it would have all the incentive to commit to do so, 1114 then the game would be like the sequential moves game analyzed in the basic model. 1115 Otherwise, the entrant will never choose a complement just to avoid copying, and it will enter the “kill zone”. 1116 """ 1117 1118 def __init__(self, u: float = 1, B: float = 0.5, small_delta: float = 0.5, delta: float = 0.51, 1119 K: float = 0.2, beta: float = 0.5): 1120 """ 1121 The parameters do not change compared to Shelegia_Motta_2021.Models.BargainingPowerModel. 1122 """ 1123 super(UnobservableModel, self).__init__(u=u, B=B, small_delta=small_delta, delta=delta, K=K, beta=beta) 1124 1125 def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 1126 return self.plot_equilibrium(axis=axis, **kwargs) 1127 1128 def _create_choice_answer_label(self, entrant: Literal["complement", "substitute", "indifferent"], 1129 incumbent: Literal["copy", "refrain"], 1130 development: Literal["success", "failure"], 1131 kill_zone: bool = False, 1132 acquisition: str = "") -> str: 1133 return "{" + self.ENTRANT_CHOICES[entrant] + ", " + self.INCUMBENT_CHOICES[incumbent] + "} $\\rightarrow$ " + \ 1134 self.DEVELOPMENT_OUTCOME[development] 1135 1136 def _get_equilibrium_labels(self) -> List[str]: 1137 """ 1138 Returns a list containing the labels for the squares in the plot of the equilibrium path. 1139 1140 For the order of the squares refer to the file resources/dev_notes.md. 1141 1142 Returns 1143 ------- 1144 List containing the labels for the squares in the plot of the best answers of the equilibrium path. 1145 """ 1146 return [ 1147 # Area 1 1148 self._create_choice_answer_label(entrant="indifferent", incumbent="copy", development="failure"), 1149 # Area 2 1150 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success"), 1151 # Area 3 1152 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="failure"), 1153 # Area 4 1154 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") 1155 ] 1156 1157 def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: 1158 result: Dict = super().get_optimal_choice(A, F) 1159 # adjust the different choices in area three -> since the kill zone does not exist in this model. 1160 if result["entrant"] == self.ENTRANT_CHOICES["complement"]: 1161 result = {"entrant": self.ENTRANT_CHOICES["substitute"], "incumbent": self.INCUMBENT_CHOICES["copy"], 1162 "development": self.DEVELOPMENT_OUTCOME["failure"]} 1163 return result 1164 1165 1166class AcquisitionModel(BargainingPowerModel): 1167 """ 1168 In order to explore how acquisitions may modify the entrant’s and the incumbent’s strategic choices, we extend the base model 1169 in order to allow an acquisition to take place after the incumbent commits to copying the entrant’s original complementary product 1170 (between t=1 and t=2, see demo.ipynb "Timing of the game"). We assume that the incumbent and the entrant share the gains (if any) attained from the acquisition equally. 1171 1172 The “kill zone” still appears as a possible equilibrium outcome, however for a more reduced region of the parameter space. 1173 The prospect of getting some acquisition gains does tend to increase the profits gained from developing a substitute to the primary product, 1174 and this explains why part of the “kill zone” region where a complement was chosen without the acquisition, the entrant will now choose a substitute instead. 1175 """ 1176 1177 def __init__(self, u: float = 1, B: float = 0.5, small_delta: float = 0.5, delta: float = 0.51, 1178 K: float = 0.2, beta: float = 0.5) -> None: 1179 """ 1180 An additional constraint is added compared to Shelegia_Motta_2021.Models.BaseModel. Namely, $\Delta$ has to be bigger than $u$, 1181 meaning the innovation of the entrant is not too drastic compared with the primary products of the incumbent. 1182 1183 Meanwhile, the parameters do not change compared to Shelegia_Motta_2021.Models.BargainingPowerModel. 1184 """ 1185 assert delta < u, "Delta has to be smaller than u, meaning the innovation of the entrant is not too drastic." 1186 super(AcquisitionModel, self).__init__(u=u, B=B, small_delta=small_delta, delta=delta, K=K, beta=beta) 1187 self.ACQUISITION_OUTCOME: Final[Dict[str, str]] = {"merged": "M", "apart": "E"} 1188 """ 1189 Contains the options for an acquisition or not. 1190 - merged (M): The incumbent acquired the entrant. 1191 - apart (E): The incumbent did not acquired the entrant. 1192 """ 1193 1194 def _calculate_copying_fixed_costs_values(self) -> Dict[str, float]: 1195 copying_fixed_costs_values: Dict[str, float] = super()._calculate_copying_fixed_costs_values() 1196 copying_fixed_costs_values.update( 1197 {'F(ACQ)s': (self._u + self._delta - self._K) / 2 + self._small_delta * (2 - self._beta), 1198 'F(ACQ)c': self._small_delta * (2.5 - 3 * self._beta) - self._K / 2}) 1199 assert (abs(copying_fixed_costs_values["F(ACQ)c"] - copying_fixed_costs_values["F(YY)c"]) < self.TOLERANCE or 1200 copying_fixed_costs_values["F(ACQ)c"] < copying_fixed_costs_values["F(YY)c"]), "F(ACQ)c has to be smaller or equal than F(YY)c" 1201 return copying_fixed_costs_values 1202 1203 def get_copying_fixed_costs_values(self) -> Dict[str, float]: 1204 """ 1205 Returns the fixed costs for copying thresholds of the incumbent. 1206 1207 Additional thresholds for the fixed cost of copying of the incumbent compared to the Shelegia_Motta_2021.Models.BargainingModel: 1208 1209 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 1210 |----------|:-------|:--------| 1211 | $F^{ACQ}_S$ | F(ACQ)s | $\\frac{(u + \Delta - K)}{2} + \delta(2 - \\beta)$ | 1212 | $F^{ACQ}_C$ | F(ACQ)c | $\\frac{K}{2} + \delta(2.5 - 3\\beta)$ | 1213 <br> 1214 As an additional constraint, $F^{ACQ}_C$ has to be smaller or equal than $F^{YY}_C$, since the logic described in the paper may not apply anymore for the other cases. 1215 1216 Returns 1217 ------- 1218 Dict[str, float] 1219 Includes the thresholds for the fixed costs for copying of the incumbent. 1220 """ 1221 return self._copying_fixed_costs 1222 1223 def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: 1224 """ 1225 Returns 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. 1226 1227 The output dictionary will contain the following details: 1228 1229 - "entrant": choice of the entrant (possible choices listed in Shelegia_Motta_2021.IModel.IModel.ENTRANT_CHOICES)) 1230 - "incumbent": choice of the incumbent (possible choices listed in Shelegia_Motta_2021.IModel.IModel.INCUMBENT_CHOICES) 1231 - "development": outcome of the development (possible outcomes listed in Shelegia_Motta_2021.IModel.IModel.DEVELOPMENT_OUTCOME) 1232 - "acquisition": outcome of the acquisition (possible outcomes listed in Shelegia_Motta_2021.Models.AcquisitionModel.ACQUISITION_OUTCOME) 1233 1234 To understand the details of the logic implemented, consult the chapter in Shelegia and Motta (2021) corresponding to the model. 1235 1236 Parameters 1237 ---------- 1238 A : float 1239 Assets of the entrant. 1240 F : float 1241 Fixed costs for copying of the incumbent. 1242 1243 Returns 1244 ------- 1245 Dict[str, str] 1246 Optimal choice of the entrant, the incumbent and the outcome of the development. 1247 """ 1248 result: Dict[str, str] = {"entrant": "", "incumbent": "", "development": "", "acquisition": ""} 1249 if self._copying_fixed_costs["F(ACQ)c"] <= F <= self._copying_fixed_costs["F(ACQ)s"] and A < self._assets["A-s"]: 1250 result.update({"entrant": self.ENTRANT_CHOICES["complement"]}) 1251 result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) 1252 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 1253 result.update({"acquisition": self.ACQUISITION_OUTCOME["apart"]}) 1254 elif F < self._copying_fixed_costs["F(ACQ)c"] and A < self._assets["A-s"]: 1255 # to develop a substitute is the weakly dominant strategy of the entrant 1256 entrant_choice_area_1: Literal["substitute", "complement"] = "substitute" 1257 # if the payoff for a complement is higher than for a substitute, the entrant will choose the complement. 1258 if self._delta < self._small_delta: 1259 entrant_choice_area_1 = "complement" 1260 result.update({"entrant": self.ENTRANT_CHOICES[entrant_choice_area_1]}) 1261 result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) 1262 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 1263 result.update({"acquisition": self.ACQUISITION_OUTCOME["merged"]}) 1264 else: 1265 result.update({"entrant": self.ENTRANT_CHOICES["substitute"]}) 1266 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 1267 result.update({"acquisition": self.ACQUISITION_OUTCOME["merged"]}) 1268 if F <= self._copying_fixed_costs["F(YY)c"]: 1269 result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) 1270 else: 1271 result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) 1272 return result 1273 1274 def _get_incumbent_best_answer_coordinates(self, x_max: float, y_max: float) -> List[List[Tuple[float, float]]]: 1275 y_max: float = self._get_y_max(y_max) 1276 x_max: float = self._get_x_max(x_max) 1277 return [ 1278 # Area 1 1279 [(0, 0), (self._assets['A-c'], 0), (self._assets['A-c'], max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1280 (0, max(self._copying_fixed_costs['F(ACQ)c'], 0))], 1281 # Area 2 1282 [(self._assets['A-c'], 0), (x_max, 0), (x_max, self._copying_fixed_costs['F(YY)c']), 1283 (self._assets['A-c'], self._copying_fixed_costs['F(YY)c'])], 1284 # Area 3 1285 [(0, max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1286 (self._assets['A-s'], max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1287 (self._assets['A-s'], self._copying_fixed_costs['F(ACQ)s']), (0, self._copying_fixed_costs['F(ACQ)s'])], 1288 # Area 4 1289 [(self._assets['A-s'], max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1290 (self._assets['A-c'], max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1291 (self._assets['A-c'], self._copying_fixed_costs['F(YY)c']), 1292 (self._assets['A-s'], self._copying_fixed_costs['F(YY)c'])], 1293 # Area 5 1294 [(self._assets['A-s'], self._copying_fixed_costs['F(YY)c']), (x_max, self._copying_fixed_costs['F(YY)c']), 1295 (x_max, y_max), 1296 (0, y_max), (0, self._copying_fixed_costs['F(ACQ)s']), 1297 (self._assets['A-s'], self._copying_fixed_costs['F(ACQ)s'])]] 1298 1299 def _get_incumbent_best_answer_labels(self) -> List[str]: 1300 return [ 1301 # Area 1 1302 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success", 1303 acquisition=self.ACQUISITION_OUTCOME["merged"]) + " \n" + 1304 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="success", 1305 acquisition=self.ACQUISITION_OUTCOME["merged"]), 1306 # Area 2 1307 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success", 1308 acquisition=self.ACQUISITION_OUTCOME["merged"]) + " \n" + 1309 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="success", 1310 acquisition=self.ACQUISITION_OUTCOME["apart"]), 1311 # Area 3 1312 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success", 1313 acquisition=self.ACQUISITION_OUTCOME["merged"]) + " \n" + 1314 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success", 1315 acquisition=self.ACQUISITION_OUTCOME["apart"]), 1316 # Area 4 1317 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success", 1318 acquisition=self.ACQUISITION_OUTCOME["merged"]) + " \n" + 1319 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success", 1320 acquisition=self.ACQUISITION_OUTCOME["apart"]), 1321 # Area 5 1322 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success", 1323 acquisition=self.ACQUISITION_OUTCOME["merged"]) + " \n" + 1324 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success", 1325 acquisition=self.ACQUISITION_OUTCOME["apart"]), 1326 ] 1327 1328 def _get_equilibrium_coordinates(self, x_max: float, y_max: float) -> List[List[Tuple[float, float]]]: 1329 y_max: float = self._get_y_max(y_max) 1330 x_max: float = self._get_x_max(x_max) 1331 return [ 1332 # Area 1 1333 [(0, 0), (self._assets['A-s'], 0), (self._assets['A-s'], max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1334 (0, max(self._copying_fixed_costs['F(ACQ)c'], 0))], 1335 # Area 2 1336 [(self._assets['A-s'], 0), (x_max, 0), (x_max, self._copying_fixed_costs['F(YY)c']), 1337 (self._assets['A-s'], self._copying_fixed_costs['F(YY)c'])], 1338 # Area 3 1339 [(0, max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1340 (self._assets['A-s'], max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1341 (self._assets['A-s'], self._copying_fixed_costs['F(ACQ)s']), (0, self._copying_fixed_costs['F(ACQ)s'])], 1342 # Area 4 1343 [(self._assets['A-s'], self._copying_fixed_costs['F(YY)c']), (x_max, self._copying_fixed_costs['F(YY)c']), 1344 (x_max, y_max), (0, y_max), (0, self._copying_fixed_costs['F(ACQ)s']), 1345 (self._assets['A-s'], self._copying_fixed_costs['F(ACQ)s'])]] 1346 1347 def _get_equilibrium_labels(self) -> List[str]: 1348 # to develop a substitute is the weakly dominant strategy of the entrant 1349 entrant_choice_area_1: Literal["substitute", "complement"] = "substitute" 1350 # if the payoff for a complement is higher than for a substitute, the entrant will choose the complement. 1351 if self._delta < self._small_delta: 1352 entrant_choice_area_1 = "complement" 1353 return [ 1354 # Area 1 1355 self._create_choice_answer_label(entrant=entrant_choice_area_1, incumbent="copy", development="success", 1356 acquisition=self.ACQUISITION_OUTCOME["merged"]), 1357 # Area 2 1358 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success", 1359 acquisition=self.ACQUISITION_OUTCOME["merged"]), 1360 # Area 3 1361 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success", 1362 kill_zone=True, acquisition=self.ACQUISITION_OUTCOME["apart"]), 1363 # Area 4 1364 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success", 1365 acquisition=self.ACQUISITION_OUTCOME["merged"]) 1366 ] 1367 1368 def _draw_thresholds(self, axis: matplotlib.axes.Axes, x_horizontal: float = 0, y_vertical: float = 0) -> None: 1369 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(ACQ)s'], label="$F^{ACQ}_S$", 1370 x=x_horizontal) 1371 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YN)s'], label="$F^{YN}_S$", 1372 x=x_horizontal) 1373 1374 if abs(self._copying_fixed_costs['F(YY)c'] - self._copying_fixed_costs['F(ACQ)c']) < self.TOLERANCE: 1375 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(ACQ)c'], x=x_horizontal, 1376 label="$F^{ACQ}_C=F^{YY}_C$") 1377 else: 1378 if self._copying_fixed_costs['F(ACQ)c'] >= 0: 1379 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(ACQ)c'], x=x_horizontal, 1380 label="$F^{ACQ}_C$") 1381 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YY)c'], label="$F^{YY}_C$", 1382 x=x_horizontal) 1383 # vertical lines (asset thresholds) 1384 self._draw_vertical_line_with_label(axis, x=self._assets['A-s'], label=r'$\bar{A}_S$', y=y_vertical) 1385 self._draw_vertical_line_with_label(axis, x=self._assets['A-c'], label=r'$\bar{A}_C$', y=y_vertical) 1386 1387 @staticmethod 1388 def _create_cost_thresholds_legend(width: int) -> str: 1389 legend: str = super(AcquisitionModel, AcquisitionModel)._create_cost_thresholds_legend(width=width) 1390 return legend + "\n" + \ 1391 AcquisitionModel._format_legend_line(r'$F^{ACQ}_C$' + ": Maximum level of fixed costs that ensure that the incumbent acquires the entrant if the entrant develops a second complement.", width=width) + "\n" + \ 1392 AcquisitionModel._format_legend_line(r'$F^{ACQ}_S$' + ": Maximum level of fixed costs that ensure that the incumbent acquires the entrant if the entrant develops a perfect substitute.", width=width) 1393 1394 def _create_options_legend(self, width: int) -> str: 1395 legend: str = super(AcquisitionModel, self)._create_options_legend(width=width) 1396 # modify outcomes without acquisition 1397 legend = legend.replace(self.DEVELOPMENT_OUTCOME['success'], "$" + self.DEVELOPMENT_OUTCOME['success'] + "_" + self.ACQUISITION_OUTCOME["apart"] + "$") 1398 legend = legend.replace(self.DEVELOPMENT_OUTCOME['failure'], "$" + self.DEVELOPMENT_OUTCOME['failure'] + "_" + self.ACQUISITION_OUTCOME["apart"] + "$") 1399 1400 # add additional outcomes with acquisition 1401 return legend + "\n" + \ 1402 self._format_legend_line("$" + self.DEVELOPMENT_OUTCOME['success'] + "_" + self.ACQUISITION_OUTCOME["merged"] + "$ : The merged entity has sufficient assets to develop the product.", width=width) + "\n" + \ 1403 self._format_legend_line("$" + self.DEVELOPMENT_OUTCOME['failure'] + "_" + self.ACQUISITION_OUTCOME["merged"] + "$ : The merged entity has not sufficient assets to develop the product.", width=width) 1404 1405 1406if __name__ == '__main__': 1407 model: Shelegia_Motta_2021.IModel = Shelegia_Motta_2021.AcquisitionModel() 1408 print(model)
22class BaseModel(Shelegia_Motta_2021.IModel): 23 """ 24 The base model of the project consists of two players: The incumbent, which sells the primary product, 25 and a start-up otherwise known as the entrant which sells a complementary product to the incumbent. 26 One way to visualize a real-world application of this model would be to think of the entrant as a product or service 27 that can be accessed through the platform of the incumbent, like a plug in that can be accessed through Google or a game on Facebook. 28 The aim of this model is to monitor the choice that the entrant has between developing a substitute to or 29 another compliment to the incumbent. The second aim is to observe the choice of the incumbent of whether 30 to copy the original complementary product of the entrant by creating a perfect substitute or not. 31 Seeing as the entrant may not have enough assets to fund a second product, the incumbent copying its first product 32 would inhibit the entrant’s ability to fund its projects. This report will illustrate how the incumbent has a strategic incentive to copy 33 the entrant if it is planning to compete and that it would refrain from copying if the entrant plans to develop a compliment. 34 The subsequent models included in this report will introduce additional factors but will all be based on the basic model. 35 36 The equilibrium path arguably supports the “kill zone” argument: due to the risk of an exclusionary strategy by the incumbent, 37 a potential entrant may prefer to avoid a market trajectory which would lead it to compete with the core product of a dominant incumbent 38 and would choose to develop another complementary product instead. 39 """ 40 41 TOLERANCE: Final[float] = 10 ** (-10) 42 """Tolerance for the comparison of two floating numbers.""" 43 44 def __init__(self, u: float = 1, B: float = 0.5, small_delta: float = 0.5, delta: float = 0.51, 45 K: float = 0.2) -> None: 46 """ 47 Initializes a valid BaseModel object. 48 49 The following preconditions have to be satisfied: 50 - (A1b) $\delta$ / 2 < $\Delta$ < 3 * $\delta$ / 2 51 - (A2) K < $\delta$ / 2 52 53 Parameters 54 ---------- 55 u : float 56 Utility gained from consuming the primary product. 57 B : float 58 Minimal 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 the entrant in case of failure. 59 small_delta : float 60 ($\delta$) Additional utility gained from a complement combined with a primary product. 61 delta : float 62 ($\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent. 63 K : float 64 Investment costs for the entrant to develop a second product. 65 """ 66 super(BaseModel, self).__init__() 67 assert small_delta / 2 < delta < 3 * small_delta / 2, "(A1b) not satisfied." 68 assert K < small_delta / 2, "(A2) not satisfied." 69 self._u: float = u 70 self._B: float = B 71 self._small_delta: float = small_delta 72 self._delta: float = delta 73 self._K: float = K 74 self._copying_fixed_costs: Dict[str, float] = self._calculate_copying_fixed_costs_values() 75 self._assets: Dict[str, float] = self._calculate_asset_values() 76 self._payoffs: Dict[str, Dict[str, float]] = self._calculate_payoffs() 77 78 def _calculate_payoffs(self) -> Dict[str, Dict[str, float]]: 79 """ 80 Calculates the payoffs for different market configurations with the formulas given in the paper. 81 82 The formulas are tabulated in BaseModel.get_payoffs. 83 84 Returns 85 ------- 86 Dict[str, Dict[str, float]] 87 Contains the mentioned payoffs for different market configurations. 88 """ 89 return {'basic': {'pi(I)': self._u + self._small_delta / 2, 90 'pi(E)': self._small_delta / 2, 91 'CS': 0, 92 'W': self._u + self._small_delta 93 }, 94 'I(C)': {'pi(I)': self._u + self._small_delta, 95 'pi(E)': 0, 96 'CS': 0, 97 'W': self._u + self._small_delta 98 }, 99 'E(P)': {'pi(I)': 0, 100 'pi(E)': self._delta + self._small_delta, 101 'CS': self._u, 102 'W': self._u + self._delta + self._small_delta 103 }, 104 'I(C)E(P)': {'pi(I)': 0, 105 'pi(E)': self._delta, 106 'CS': self._u + self._small_delta, 107 'W': self._u + self._delta + self._small_delta 108 }, 109 'E(C)': {'pi(I)': self._u + self._small_delta, 110 'pi(E)': self._small_delta, 111 'CS': 0, 112 'W': self._u + 2 * self._small_delta 113 }, 114 'I(C)E(C)': {'pi(I)': self._u + 3 / 2 * self._small_delta, 115 'pi(E)': self._small_delta / 2, 116 'CS': 0, 117 'W': self._u + 2 * self._small_delta 118 } 119 } 120 121 def _calculate_copying_fixed_costs_values(self) -> Dict[str, float]: 122 """ 123 Calculates the thresholds for the fixed costs of copying for the incumbent. 124 125 The formulas are tabulated in BaseModel.get_copying_fixed_costs_values. 126 127 Returns 128 ------- 129 Dict[str, float] 130 Includes the thresholds for the fixed costs for copying of the incumbent. 131 """ 132 return {'F(YY)s': self._small_delta / 2, 133 'F(YN)s': self._u + self._small_delta * 3 / 2, 134 'F(YY)c': self._small_delta, 135 'F(YN)c': self._small_delta / 2} 136 137 def _calculate_asset_values(self) -> Dict[str, float]: 138 """ 139 Calculates the thresholds for the assets of the entrant. 140 141 The formulas are tabulated in BaseModel.get_asset_values. 142 143 Returns 144 ------- 145 Dict[str, float] 146 Includes the thresholds for the assets of the entrant. 147 """ 148 return {'A_s': self._B - (self._delta + 3 / 2 * self._small_delta - self._K), 149 'A_c': self._B - (3 / 2 * self._small_delta - self._K), 150 'A-s': self._B - (self._delta - self._K), 151 'A-c': self._B - (1 / 2 * self._small_delta - self._K)} 152 153 def get_asset_values(self) -> Dict[str, float]: 154 """ 155 Returns the asset thresholds of the entrant. 156 157 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 158 |----------------|:----------|:-----------| 159 | $\\underline{A}_S$ | A_s | $(2)\: B + K - \Delta - 3\delta/2$ | 160 | $\\underline{A}_C$ | A_c | $(3)\: B + K - 3\delta/2$ | 161 | $\overline{A}_S$ | A-s | $(4)\: B + K - \Delta$ | 162 | $\overline{A}_C$ | A-c | $(5)\: B + K - \delta/2$ | 163 <br> 164 Returns 165 ------- 166 Dict[str, float] 167 Includes the thresholds for the assets of the entrant. 168 """ 169 return self._assets 170 171 def get_copying_fixed_costs_values(self) -> Dict[str, float]: 172 """ 173 Returns the fixed costs for copying thresholds of the incumbent. 174 175 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 176 |----------|:-------|:--------| 177 | $F^{YY}_S$ | F(YY)s | $(6)\: \delta/2$ | 178 | $F^{YN}_S$ | F(YN)s | $(6)\: u + 3\delta/2$ | 179 | $F^{YY}_C$ | F(YY)c | $(6)\: \delta$ | 180 | $F^{YN}_C$ | F(YN)c | $(6)\: \delta/2$ | 181 <br> 182 Returns 183 ------- 184 Dict[str, float] 185 Includes the thresholds for the fixed costs for copying of the incumbent. 186 """ 187 return self._copying_fixed_costs 188 189 def get_payoffs(self) -> Dict[str, Dict[str, float]]: 190 """ 191 Returns the payoffs for different market configurations. 192 193 A market configuration can include: 194 - $I_P$ : Primary product sold by the incumbent. 195 - $I_C$ : Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$. 196 - $E_P$ : Perfect substitute to $I_P$ potentially sold by the entrant. 197 - $E_C$ : Complementary product to $I_P$ currently sold by the entrant 198 - $\\tilde{E}_C$ : Complementary product to $I_P$ potentially sold by the entrant. 199 <br> 200 201 | Market Config. $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(I) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(E) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | CS $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | W $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 202 |-----------------------|:--------|:--------|:--|:-| 203 | $I_P$ ; $E_C$ | $u + \delta/2$ | $\delta/2$ | 0 | $u + \delta$ | 204 | $I_P + I_C$ ; $E_C$ | $u + \delta$ | 0 | 0 | $u + \delta$ | 205 | $I_P$ ; $E_P + E_C$ | 0 | $\Delta + \delta$ | $u$ | $u + \Delta + \delta$ | 206 | $I_P + I_C$ ; $E_P + E_C$ | 0 | $\Delta$ | $u + \delta$ | $u + \Delta + \delta$ | 207 | $I_P$ ; $E_C + \\tilde{E}_C$ | $u + \delta$ | $\delta$ | 0 | $u + 2\delta$ | 208 | $I_P + I_C$ ; $E_C + \\tilde{E}_C$ | $u + 3\delta/2$ | $\delta/2$ | 0 | $u + 2\delta$ | 209 <br> 210 211 Returns 212 ------- 213 Dict[str, Dict[str, float]] 214 Contains the mentioned payoffs for different market configurations. 215 """ 216 return self._payoffs 217 218 def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: 219 result: Dict[str, str] = {"entrant": "", "incumbent": "", "development": ""} 220 if self._copying_fixed_costs["F(YN)c"] <= F <= self._copying_fixed_costs["F(YN)s"] and A < self._assets["A-s"]: 221 result.update({"entrant": self.ENTRANT_CHOICES["complement"]}) 222 result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) 223 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 224 elif F <= self._copying_fixed_costs["F(YN)c"] and A < self._assets["A-s"]: 225 result.update({"entrant": self.ENTRANT_CHOICES["indifferent"]}) 226 result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) 227 result.update({"development": self.DEVELOPMENT_OUTCOME["failure"]}) 228 else: 229 result.update({"entrant": self.ENTRANT_CHOICES["substitute"]}) 230 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 231 if F <= self._copying_fixed_costs["F(YY)s"]: 232 result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) 233 else: 234 result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) 235 return result 236 237 def _plot(self, coordinates: List[List[Tuple[float, float]]], labels: List[str], 238 axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 239 """ 240 Plots the areas containing the optimal choices and answers into a coordinate system. 241 242 Parameters 243 ---------- 244 coordinates : List[List[Tuple[float, float]]] 245 List of all polygons (list of coordinates) to plot. 246 labels: List[str] 247 List containing all the labels for the areas. 248 axis : matplotlib.axes.Axes 249 Axis to draw the plot on. (optional) 250 **kwargs 251 Optional key word arguments for the plots.<br> 252 - title: title of the plot.<br> 253 - xlabel: label for the x - axis.<br> 254 - ylabel: label for the y - axis.<br> 255 - options_legend: If true, an additional legend, explaining the options of the entrant and the incumbent, will be added to the plot.<br> 256 - asset_legend: If true, an additional legend explaining the thresholds of the assets of the entrant will be added to the plot.<br> 257 - costs_legend: If true, an additional legend explaining the thresholds of the fixed costs of copying for the incumbent will be added to the plot.<br> 258 - legend_width : Maximum number of characters in one line in the legend (for adjustments to figure width).<br> 259 - x_max : Maximum number plotted on the x - axis.<br> 260 - y_max : Maximum number plotted on the y - axis.<br> 261 262 Returns 263 ------- 264 Axis containing the plot. 265 """ 266 if axis is None: 267 plot_fig, axis = plt.subplots() 268 self._draw_thresholds(axis, x_horizontal=kwargs.get("x_max", 0), y_vertical=kwargs.get("y_max", 0)) 269 270 for i, coordinates in enumerate(coordinates): 271 poly = plt.Polygon(coordinates, linewidth=0, color=self._get_color(i), label=labels[i]) 272 axis.add_patch(poly) 273 274 if kwargs.get("legend", True): 275 axis.legend(bbox_to_anchor=(1.3, 1), loc="upper left") 276 additional_legend: str = self._create_additional_legend(options_legend=kwargs.get('options_legend', False), 277 assets_thresholds_legend=kwargs.get('asset_legend', False), 278 costs_thresholds_legend=kwargs.get('costs_legend', False), 279 width=kwargs.get('legend_width', 60)) 280 if additional_legend != "": 281 axis.text(-0.1, -0.6, additional_legend, verticalalignment='top', linespacing=1, wrap=True) 282 283 BaseModel._set_axis_labels(axis, title=kwargs.get('title', ''), 284 x_label=kwargs.get('xlabel', 'Assets of the entrant'), 285 y_label=kwargs.get('ylabel', 'Fixed costs of copying for the incumbent')) 286 BaseModel._set_axis(axis) 287 return axis 288 289 def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 290 poly_coordinates: List[List[Tuple[float, float]]] = self._get_incumbent_best_answer_coordinates( 291 kwargs.get("x_max", 0), 292 kwargs.get("y_max", 0)) 293 poly_labels: List[str] = self._get_incumbent_best_answer_labels() 294 kwargs.update({'title': kwargs.get('title', "Best Answers of the incumbent to the choices of the entrant")}) 295 return self._plot(coordinates=poly_coordinates, labels=poly_labels, axis=axis, **kwargs) 296 297 def _create_choice_answer_label(self, entrant: Literal["complement", "substitute", "indifferent"], 298 incumbent: Literal["copy", "refrain"], 299 development: Literal["success", "failure"], 300 kill_zone: bool = False, acquisition: str = "") -> str: 301 """ 302 Creates a label for the legend based on the choice of the entrant, the incumbent, the development outcome and additionally on possible acquisition. 303 304 Parameters 305 ---------- 306 entrant: Literal["complement", "substitute", "indifferent"] 307 choice of the entrant. 308 incumbent: Literal["copy", "refrain"] 309 choice of the incumbent. 310 development: Literal["success", "failure"] 311 outcome of the development. 312 kill_zone: bool 313 If true, the label adds a "(Kill Zone)" tag. 314 acquisition: str 315 The entity, which develops the additional product chosen by the entrant. 316 317 Returns 318 ------- 319 str 320 label based on the parameters mentioned above. 321 """ 322 if acquisition != "": 323 acquisition = "_" + acquisition 324 return self.ENTRANT_CHOICES[entrant] + " $\\rightarrow$ " + self.INCUMBENT_CHOICES[ 325 incumbent] + " $\\rightarrow " + self.DEVELOPMENT_OUTCOME[development] + acquisition + "$" + ( 326 "\n(Kill Zone)" if kill_zone else "") 327 328 def _get_incumbent_best_answer_labels(self) -> List[str]: 329 """ 330 Returns a list containing the labels for the squares in the plot of the best answers of the incumbent to the choice of the entrant. 331 332 For the order of the labels refer to the file resources/dev_notes.md. 333 334 Returns 335 ------- 336 List containing the labels for the squares in the plot of the best answers of the incumbent to the choice of the entrant. 337 """ 338 return [ 339 # Area 1 340 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="failure") + " \n" + 341 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="failure"), 342 # Area 2 343 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success") + " \n" + 344 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="failure"), 345 # Area 3 346 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success") + " \n" + 347 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="success"), 348 # Area 4 349 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="failure") + " \n" + 350 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success"), 351 # Area 5 352 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") + " \n" + 353 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="success"), 354 # Area 6 355 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") + " \n" + 356 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success"), 357 ] 358 359 def _get_incumbent_best_answer_coordinates(self, x_max: float, y_max: float) -> List[List[Tuple[float, float]]]: 360 """ 361 Returns a list containing the coordinates for the areas in the plot of the best answers of the incumbent to the choice of the entrant. 362 363 For the order of the areas refer to the file resources/dev_notes.md. 364 365 Returns 366 ------- 367 List[List[Tuple[float, float]]] 368 List containing the coordinates for the areas in the plot of the best answers of the incumbent to the choice of the entrant. 369 """ 370 y_max = self._get_y_max(y_max) 371 x_max = self._get_x_max(x_max) 372 return [ 373 # Area 1 374 [(0, 0), (self._assets['A-s'], 0), (self._assets['A-s'], max(self._copying_fixed_costs['F(YN)c'], 0)), 375 (0, max(self._copying_fixed_costs['F(YN)c'], 0))], 376 # Area 2 377 [(self._assets['A-s'], 0), (self._assets['A-c'], 0), 378 (self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), 379 (self._assets['A-s'], self._copying_fixed_costs['F(YY)s'])], 380 # Area 3 381 [(self._assets['A-c'], 0), (x_max, 0), (x_max, self._copying_fixed_costs['F(YY)s']), 382 (self._assets['A-c'], self._copying_fixed_costs['F(YY)s'])], 383 # Area 4 384 [(0, max(self._copying_fixed_costs['F(YN)c'], 0)), 385 (self._assets['A-s'], max(self._copying_fixed_costs['F(YN)c'], 0)), 386 (self._assets['A-s'], self._copying_fixed_costs['F(YN)s']), (0, self._copying_fixed_costs['F(YN)s'])], 387 # Area 5 388 [(self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), (x_max, self._copying_fixed_costs['F(YY)s']), 389 (x_max, self._copying_fixed_costs['F(YY)c']), (self._assets['A-c'], self._copying_fixed_costs['F(YY)c'])], 390 # Area 6 391 [(self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), 392 (self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), 393 (self._assets['A-c'], self._copying_fixed_costs['F(YY)c']), (x_max, self._copying_fixed_costs['F(YY)c']), 394 (x_max, y_max), (0, y_max), 395 (0, self._copying_fixed_costs['F(YN)s']), (self._assets['A-s'], self._copying_fixed_costs['F(YN)s'])]] 396 397 def plot_equilibrium(self, axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 398 poly_coordinates: List[List[Tuple[float, float]]] = self._get_equilibrium_coordinates(kwargs.get("x_max", 0), 399 kwargs.get("y_max", 0)) 400 poly_labels: List[str] = self._get_equilibrium_labels() 401 kwargs.update({'title': kwargs.get('title', 'Equilibrium Path')}) 402 return self._plot(coordinates=poly_coordinates, labels=poly_labels, axis=axis, **kwargs) 403 404 def _get_equilibrium_labels(self) -> List[str]: 405 """ 406 Returns a list containing the labels for the squares in the plot of the equilibrium path. 407 408 For the order of the squares refer to the file resources/dev_notes.md. 409 410 Returns 411 ------- 412 List[str] 413 List containing the labels for the squares in the plot of the best answers of the equilibrium path. 414 """ 415 return [ 416 # Area 1 417 self._create_choice_answer_label(entrant="indifferent", incumbent="copy", development="failure"), 418 # Area 2 419 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success"), 420 # Area 3 421 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success", 422 kill_zone=True), 423 # Area 4 424 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") 425 ] 426 427 def _get_equilibrium_coordinates(self, x_max: float, y_max: float) -> List[List[Tuple[float, float]]]: 428 """ 429 Returns a list containing the coordinates for the areas in the plot of the equilibrium path. 430 431 For the order of the areas refer to the file resources/dev_notes.md. 432 433 Returns 434 ------- 435 List[List[Tuple[float, float]]] 436 List containing the coordinates for the areas in the plot of the best answers of the equilibrium path. 437 """ 438 y_max = self._get_y_max(y_max) 439 x_max = self._get_x_max(x_max) 440 return [ 441 # Area 1 442 [(0, 0), (self._assets['A-s'], 0), (self._assets['A-s'], max(self._copying_fixed_costs['F(YN)c'], 0)), 443 (0, max(self._copying_fixed_costs['F(YN)c'], 0))], 444 # Area 2 445 [(self._assets['A-s'], 0), (x_max, 0), (x_max, self._copying_fixed_costs['F(YY)s']), 446 (self._assets['A-s'], self._copying_fixed_costs['F(YY)s'])], 447 # Area 3 448 [(0, max(self._copying_fixed_costs['F(YN)c'], 0)), 449 (self._assets['A-s'], max(self._copying_fixed_costs['F(YN)c'], 0)), 450 (self._assets['A-s'], self._copying_fixed_costs['F(YN)s']), (0, self._copying_fixed_costs['F(YN)s'])], 451 # Area 4 452 [(self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), (x_max, self._copying_fixed_costs['F(YY)s']), 453 (x_max, y_max), (0, y_max), (0, self._copying_fixed_costs['F(YN)s']), 454 (self._assets['A-s'], self._copying_fixed_costs['F(YN)s'])]] 455 456 def plot_payoffs(self, axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 457 if axis is None: 458 plot_fig, axis = plt.subplots() 459 index = arange(0, len(self._payoffs) * 2, 2) 460 bar_width = 0.35 461 spacing = 0.05 462 463 self._plot_payoffs_bars(axis, bar_width, index, spacing, **kwargs) 464 465 axis.set_xlabel('Market Configurations') 466 axis.set_title('Payoffs for different Market Configurations') 467 self._set_payoffs_ticks(axis, bar_width, index, spacing) 468 if kwargs.get("legend", True): 469 self._set_payoff_legend(axis, kwargs.get("products_legend", False)) 470 self._set_payoffs_figure(axis) 471 return axis 472 473 def _plot_payoffs_bars(self, axis: matplotlib.axes.Axes, bar_width: float, index: array, spacing: float, 474 **kwargs) -> None: 475 """ 476 Plots the bars representing the payoffs for different market configurations of different stakeholders on the specified axis. 477 478 Parameters 479 ---------- 480 axis matplotlib.axes.Axes 481 To plot the bars on. 482 bar_width: float 483 Width of a bar in the plot. 484 index: np.array 485 Index of the different market configurations in the plot. 486 spacing: float 487 Spacing between the bars on the plot. 488 **kwargs 489 Optional key word arguments for the payoff plot.<br> 490 - opacity : Opacity of the not optimal payoffs.<br> 491 """ 492 for counter, utility_type in enumerate(self._payoffs[list(self._payoffs.keys())[0]].keys()): 493 utility_values: List[float] = [] 494 for market_configuration in self._payoffs: 495 utility_values.append(self._payoffs[market_configuration][utility_type]) 496 497 bars = axis.bar(index + counter * (bar_width + spacing), utility_values, bar_width, 498 alpha=kwargs.get("opacity", 0.2), 499 color=self._get_color(counter), 500 edgecolor=None, 501 label=self._convert_payoffs_label(utility_type)) 502 max_indices: List[int] = list( 503 filter(lambda x: utility_values[x] == max(utility_values), range(len(utility_values)))) 504 for max_index in max_indices: 505 bars[max_index].set_alpha(1) 506 507 def _set_payoff_legend(self, axis: matplotlib.axes.Axes, products_legend: bool = False) -> None: 508 """ 509 Creates the legend and an additional legend for the products of the entrant and the incumbent, 510 511 Parameters 512 ---------- 513 axis: matplotlib.axes.Axes 514 To set the legends for. 515 products_legend: bool 516 If true, an additional legend, containing all possible products of the entrant and the incumbent, will be created. 517 """ 518 axis.legend(bbox_to_anchor=(1.02, 1), loc='upper left', ncol=1) 519 if products_legend: 520 axis.text(-0.7, -0.8, self._get_market_configuration_annotations(), verticalalignment="top") 521 522 def _set_payoffs_ticks(self, axis: matplotlib.axes.Axes, bar_width: float, index: array, spacing: float) -> None: 523 """ 524 Sets the x - and y - ticks for the plot of the payoffs for different market configurations. 525 526 Parameters 527 ---------- 528 axis matplotlib.axes.Axes 529 To adjust the ticks on. 530 bar_width: float 531 Width of a bar in the plot. 532 index: np.array 533 Index of the different market configurations in the plot. 534 spacing: float 535 Spacing between the bars on the plot. 536 """ 537 axis.set(yticklabels=[]) 538 axis.tick_params(left=False) 539 axis.set_xticks(index + 1.5 * (bar_width + spacing)) 540 axis.set_xticklabels(tuple([self._convert_market_configuration_label(i) for i in self._payoffs.keys()])) 541 542 @staticmethod 543 def _set_payoffs_figure(axis: matplotlib.axes.Axes) -> None: 544 """ 545 Adjust the matplotlib figure to plot the payoffs for different market configurations. 546 547 Parameters 548 ---------- 549 axis: matplotlib.axes.Axes 550 To adjust for the payoff plot. 551 """ 552 axis.figure.set_size_inches(10, 5) 553 axis.figure.tight_layout() 554 555 @staticmethod 556 def _get_market_configuration_annotations() -> str: 557 """ 558 Returns a string containing all product options for the entrant and the incumbent. 559 560 Returns 561 ------- 562 str 563 Contains all product options for the entrant and the incumbent. 564 """ 565 return "$I_P$: Primary product sold by the incumbent\n" \ 566 "$I_C$: Copied complementary product to $I_P$ potentially sold by the incumbent\n" \ 567 "$E_P$: Perfect substitute to $I_P$ potentially sold by the entrant\n" \ 568 "$E_C$: Complementary product to $I_P$ currently sold by the entrant\n" \ 569 "$\\tilde{E}_C$: Complementary product to $I_P$ potentially sold by the entrant\n" \ 570 "\nThe bars representing the maximum payoff for a stakeholder are fully filled." 571 572 @staticmethod 573 def _convert_payoffs_label(raw_label: str) -> str: 574 """ 575 Converts keys of the payoffs dict to latex labels. 576 577 Parameters 578 ---------- 579 raw_label: str 580 As given as key in the payoffs dict. 581 582 Returns 583 ------- 584 str 585 Latex compatible pretty label. 586 """ 587 label: str = raw_label.replace("pi", "$\pi$") 588 label = label.replace("CS", "Consumer Surplus") 589 label = label.replace("W", "Welfare") 590 return label 591 592 @staticmethod 593 def _convert_market_configuration_label(raw_label: str) -> str: 594 """ 595 Returns the latex string for a specific market configuration. 596 597 Parameters 598 ---------- 599 raw_label 600 Of the market configuration as given as key in the payoffs dict. 601 602 Returns 603 ------- 604 str 605 Corresponding latex label for the market configuration as given as key in the payoffs dict. 606 """ 607 labels: Dict[str] = {"basic": "$I_P;E_C$", 608 "I(C)": "$I_P+I_C;E_C$", 609 "E(P)": "$I_P;E_C+E_P$", 610 "I(C)E(P)": "$I_P+I_C;E_C+E_P$", 611 "E(C)": "$I_P;E_C+\\tilde{E}_C$", 612 "I(C)E(C)": "$I_P+I_C;E_C+\\tilde{E}_C$"} 613 return labels.get(raw_label, 'No valid market configuration') 614 615 def _get_x_max(self, x_max: float = 0) -> float: 616 """ 617 Returns the maximum value to plot on the x - axis. 618 619 Parameters 620 ---------- 621 x_max: float 622 Preferred value for the maximum value on the x - axis. 623 624 Returns 625 ------- 626 float 627 Maximum value (which is feasible) to plot on the x - axis. 628 """ 629 auto_x_max: float = round(self._assets['A-c'] * 1.3, 1) 630 return x_max if x_max > self._assets['A-c'] else auto_x_max 631 632 def _get_y_max(self, y_max: float = 0) -> float: 633 """ 634 Returns the maximum value to plot on the y - axis. 635 636 Parameters 637 ---------- 638 y_max: float 639 Preferred value for the maximum value on the y - axis. 640 641 Returns 642 ------- 643 float 644 Maximum value (which is feasible) to plot on the y - axis. 645 """ 646 auto_y_max: float = round(self._copying_fixed_costs['F(YN)s'] * 1.3, 1) 647 return y_max if y_max > self._copying_fixed_costs['F(YN)s'] else auto_y_max 648 649 def _draw_thresholds(self, axis: matplotlib.axes.Axes, x_horizontal: float = 0, y_vertical: float = 0) -> None: 650 """ 651 Draws the thresholds and the corresponding labels on a given axis. 652 653 Parameters 654 ---------- 655 axis: matplotlib.axes.Axes 656 Axis to draw the thresholds on. 657 x_horizontal : float 658 X - coordinate for horizontal thresholds labels (fixed costs of copying). 659 y_vertical : float 660 Y - coordinate for vertical thresholds labels (assets of the entrant). 661 """ 662 # horizontal lines (fixed cost of copying thresholds) 663 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YN)s'], label="$F^{YN}_S$", 664 x=x_horizontal) 665 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YY)c'], label="$F^{YY}_C$", 666 x=x_horizontal) 667 if abs(self._copying_fixed_costs['F(YY)s'] - self._copying_fixed_costs['F(YN)c']) < BaseModel.TOLERANCE: 668 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YY)s'], 669 label="$F^{YY}_S=F^{YN}_C$", x=x_horizontal) 670 else: 671 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YY)s'], label="$F^{YY}_S$", 672 x=x_horizontal) 673 if self._copying_fixed_costs['F(YN)c'] >= 0: 674 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YN)c'], label="$F^{YN}_C$", 675 x=x_horizontal) 676 # vertical lines (asset thresholds) 677 self._draw_vertical_line_with_label(axis, x=self._assets['A-s'], label=r'$\bar{A}_S$', y=y_vertical) 678 self._draw_vertical_line_with_label(axis, x=self._assets['A-c'], label=r'$\bar{A}_C$', y=y_vertical) 679 680 def _draw_horizontal_line_with_label(self, axis: matplotlib.axes.Axes, y: float, **kwargs) -> None: 681 """ 682 Draws a horizontal line at a given y - coordinate and writes the corresponding label at the edge. 683 684 Parameters 685 ---------- 686 axis 687 To draw the horizontal line and label on. 688 y 689 Coordinate of the of the line on the y - axis. 690 **kwargs 691 Optional key word arguments for the equilibrium plot.<br> 692 - label: Label for the horizontal line written at the edge.<br> 693 - x: X - coordinate for horizontal thresholds labels (fixed costs of copying).<br> 694 """ 695 label_x: float = self._get_x_max(kwargs.get("x", 0)) + 0.05 696 axis.axhline(y, linestyle='--', color='k') 697 axis.text(label_x, y, kwargs.get("label", "")) 698 699 def _draw_vertical_line_with_label(self, axis: matplotlib.axes.Axes, x: float, **kwargs) -> None: 700 """ 701 Draws a vertical line at a given x - coordinate and writes the corresponding label at the edge. 702 703 Parameters 704 ---------- 705 axis 706 To draw the vertical line and label on. 707 x 708 Coordinate of the of the line on the x - axis. 709 **kwargs 710 Optional key word arguments for the equilibrium plot.<br> 711 - label: Label for the horizontal line written at the edge.<br> 712 - y: Y - coordinate for vertical thresholds labels (assets of the entrant).<br> 713 """ 714 label_y: float = self._get_y_max(kwargs.get("y", 0)) + 0.15 715 axis.axvline(x, linestyle='--', color='k') 716 axis.text(x, label_y, kwargs.get("label", "")) 717 718 def _create_additional_legend(self, options_legend: bool, assets_thresholds_legend: bool, costs_thresholds_legend: bool, width: int) -> str: 719 """ 720 Handles the creation of the additional legend for the options of the entrant and incumbent as well as the legend for the thresholds. 721 722 Parameters 723 ---------- 724 options_legend: bool 725 States all options of the entrant and the incumbent. 726 assets_thresholds_legend 727 States the thresholds for the assets of the entrant used in the plots. 728 costs_thresholds_legend 729 States the thresholds for the fixed costs of copying of the incumbent used in the plots. 730 731 Returns 732 ------- 733 str 734 Containing the legend for the options of the entrant and the incumbent as well as the legend for the thresholds. 735 """ 736 legend: str = "" 737 if options_legend: 738 legend += self._create_options_legend(width=width) 739 if assets_thresholds_legend: 740 legend += "\n\n" if options_legend else "" 741 legend += self._create_asset_thresholds_legend(width=width) 742 if costs_thresholds_legend: 743 legend += "\n\n" if options_legend or assets_thresholds_legend else "" 744 legend += self._create_cost_thresholds_legend(width=width) 745 return legend 746 747 def _create_options_legend(self, width: int) -> str: 748 """ 749 Creates a legend for the options of the entrant and the incumbent. 750 751 Returns 752 ------- 753 str 754 Containing the legend for the options of the entrant and the incumbent. 755 """ 756 return "Options of the entrant:\n" + \ 757 self._format_legend_line(self.ENTRANT_CHOICES['complement'] + ": Develop an additional complementary product to a primary product.", width=width) + "\n" + \ 758 self._format_legend_line(self.ENTRANT_CHOICES['substitute'] + ": Develop an substitute to the primary product of the incumbent.", width=width) + "\n" + \ 759 self._format_legend_line(self.ENTRANT_CHOICES['indifferent'] + " : Indifferent between the options mentioned above.", width=width) + "\n" + \ 760 "\nOptions of the incumbent:\n" + \ 761 self._format_legend_line(self.INCUMBENT_CHOICES['copy'] + " : Copy the original complement of the entrant.", width=width) + "\n" + \ 762 self._format_legend_line(self.INCUMBENT_CHOICES['refrain'] + " : Do not copy the original complement of the entrant.", width=width) + "\n" + \ 763 "\nOutcomes of the development:\n" + \ 764 self._format_legend_line(self.DEVELOPMENT_OUTCOME['success'] + " : The entrant has sufficient assets to develop the product.", width=width) + "\n" + \ 765 self._format_legend_line(self.DEVELOPMENT_OUTCOME['failure'] + " : The entrant has not sufficient assets to develop the product.", width=width) 766 767 @staticmethod 768 def _create_asset_thresholds_legend(width: int) -> str: 769 """ 770 Creates a legend for the asset of the entrant thresholds used in the plots. The legend is compatible with latex. 771 772 Returns 773 ------- 774 str 775 Containing the legend for the thresholds used in the plots. 776 """ 777 return "Thresholds for the assets of the entrant:\n" + \ 778 BaseModel._format_legend_line(r'$\bar{A}_S$' + ": Minimum level of assets to ensure a perfect substitute gets funded if the incumbent copies.", width=width) + "\n" + \ 779 BaseModel._format_legend_line(r'$\bar{A}_S$' + ": Minimum level of assets to ensure a perfect substitute gets funded if the incumbent copies.", width=width) + "\n" + \ 780 BaseModel._format_legend_line(r'$\bar{A}_C$' + ": Minimum level of assets to ensure another complement gets funded if the incumbent copies.", width=width) + "\n" + \ 781 BaseModel._format_legend_line("If the incumbent does not copy, the entrant will have sufficient assets.", width=width) 782 783 @staticmethod 784 def _create_cost_thresholds_legend(width: int) -> str: 785 """ 786 Creates a legend for the thresholds used in the plots. The legend is compatible with latex. 787 788 Returns 789 ------- 790 str 791 Containing the legend for the thresholds used in the plots. 792 """ 793 return "Thresholds for the fixed costs of copying for the incumbent:\n" + \ 794 BaseModel._format_legend_line(r'$F^{YY}_S$' + ": Maximum costs of copying that ensure that the incumbent copies the entrant if the entrant is guaranteed to invest in a perfect substitute.", width=width) + "\n" + \ 795 BaseModel._format_legend_line(r'$F^{YN}_S$' + ": Maximum costs of copying that ensure that the incumbent copies the entrant if the copying prevents the entrant from developing a perfect substitute.", width=width) + "\n" + \ 796 BaseModel._format_legend_line(r'$F^{YY}_C$' + ": Maximum costs of copying that ensure that the incumbent copies the entrant if the entrant is guaranteed to invest in another complement.", width=width) + "\n" + \ 797 BaseModel._format_legend_line(r'$F^{YN}_C$' + ": Maximum costs of copying that ensure that the incumbent copies the entrant if the copying prevents the entrant from developing another complement.", width=width) 798 799 @staticmethod 800 def _format_legend_line(line: str, width: int = 60, latex: bool = True) -> str: 801 space: str = "$\quad$" if latex else " " * 4 802 return textwrap.fill(line, width=width, initial_indent='', subsequent_indent=space * 3) 803 804 @staticmethod 805 def _get_color(i: int) -> str: 806 """ 807 Returns a string corresponding to a matplotlib - color for a given index. 808 809 The index helps to get different colors for different items, when iterating over list/dict/etc.. 810 811 Parameters 812 ---------- 813 i: int 814 Index of the color. 815 Returns 816 ------- 817 str 818 A string corresponding to a matplotlib - color for a given index. 819 """ 820 return ['salmon', 'khaki', 'limegreen', 'turquoise', 'powderblue', 'thistle', 'pink'][i] 821 822 @staticmethod 823 def _set_axis(axis: matplotlib.axes.Axes) -> None: 824 """ 825 Adjusts the axis to the given viewport. 826 827 Parameters 828 ---------- 829 axis: matplotlib.axes.Axes 830 To adjust to the given viewport. 831 """ 832 axis.autoscale_view() 833 axis.figure.tight_layout() 834 835 @staticmethod 836 def _set_axis_labels(axis: matplotlib.axes.Axes, title: str = "", x_label: str = "", y_label: str = "") -> None: 837 """ 838 Sets all the labels for a plot, containing the title, x - label and y - label. 839 840 Parameters 841 ---------- 842 axis 843 Axis to set the labels for. 844 title 845 Title of the axis. 846 x_label 847 Label of the x - axis. 848 y_label 849 Label of the y - axis. 850 """ 851 axis.set_title(title, loc='left', y=1.1) 852 axis.set_xlabel(x_label) 853 axis.set_ylabel(y_label) 854 855 def __str__(self) -> str: 856 str_representation = self._create_asset_str() 857 858 str_representation += "\n" + self._create_copying_costs_str() 859 860 str_representation += "\n" + self._create_payoff_str() 861 862 return str_representation 863 864 def _create_payoff_str(self): 865 """ 866 Creates a string representation for the payoffs of different stakeholder for different market configurations. 867 868 See Shelegia_Motta_2021.IModel.get_payoffs for the formulas of the payoffs. 869 870 Returns 871 ------- 872 str 873 String representation for the payoffs of different stakeholder for different market configurations 874 """ 875 market_configurations: List[str] = list(self._payoffs.keys()) 876 str_representation = 'Payoffs for different Market Configurations:\n\t' + ''.join( 877 ['{0: <14}'.format(item) for item in market_configurations]) 878 for utility_type in self._payoffs[market_configurations[0]].keys(): 879 str_representation += '\n\t' 880 for market_configuration in market_configurations: 881 str_representation += '-' + '{0: <4}'.format(utility_type).replace('pi', 'π') + ': ' + '{0: <5}'.format( 882 str(self._payoffs[market_configuration][utility_type])) + '| ' 883 return str_representation 884 885 def _create_copying_costs_str(self): 886 """ 887 Creates a string representation for the fixed costs of copying for the incumbent. 888 889 See Shelegia_Motta_2021.IModel.get_copying_fixed_costs_values for the formulas of the fixed costs of copying. 890 891 Returns 892 ------- 893 str 894 String representation for the fixed costs of copying for the incumbent. 895 """ 896 str_representation = 'Costs for copying:' 897 for key in self._copying_fixed_costs.keys(): 898 str_representation += '\n\t- ' + key + ':\t' + str(self._copying_fixed_costs[key]) 899 return str_representation 900 901 def _create_asset_str(self): 902 """ 903 Creates a string representation for the assets of the entrant. 904 905 See Shelegia_Motta_2021.IModel.get_asset_values for the formulas of the assets of the entrant. 906 907 Returns 908 ------- 909 str 910 String representation for the assets of the entrant. 911 """ 912 str_representation: str = 'Assets:' 913 for key in self._assets: 914 str_representation += '\n\t- ' + key + ':\t' + str(self._assets[key]) 915 return str_representation 916 917 def __call__(self, A: float, F: float) -> Dict[str, str]: 918 """ 919 Makes the object callable and will return the equilibrium for a given pair of copying fixed costs of the incumbent 920 and assets of the entrant. 921 922 See Shelegia_Motta_2021.IModel.get_optimal_choice for further documentation. 923 """ 924 return self.get_optimal_choice(A=A, F=F)
The base model of the project consists of two players: The incumbent, which sells the primary product, and a start-up otherwise known as the entrant which sells a complementary product to the incumbent. One way to visualize a real-world application of this model would be to think of the entrant as a product or service that can be accessed through the platform of the incumbent, like a plug in that can be accessed through Google or a game on Facebook. The aim of this model is to monitor the choice that the entrant has between developing a substitute to or another compliment to the incumbent. The second aim is to observe the choice of the incumbent of whether to copy the original complementary product of the entrant by creating a perfect substitute or not. Seeing as the entrant may not have enough assets to fund a second product, the incumbent copying its first product would inhibit the entrant’s ability to fund its projects. This report will illustrate how the incumbent has a strategic incentive to copy the entrant if it is planning to compete and that it would refrain from copying if the entrant plans to develop a compliment. The subsequent models included in this report will introduce additional factors but will all be based on the basic model.
The equilibrium path arguably supports the “kill zone” argument: due to the risk of an exclusionary strategy by the incumbent, a potential entrant may prefer to avoid a market trajectory which would lead it to compete with the core product of a dominant incumbent and would choose to develop another complementary product instead.
44 def __init__(self, u: float = 1, B: float = 0.5, small_delta: float = 0.5, delta: float = 0.51, 45 K: float = 0.2) -> None: 46 """ 47 Initializes a valid BaseModel object. 48 49 The following preconditions have to be satisfied: 50 - (A1b) $\delta$ / 2 < $\Delta$ < 3 * $\delta$ / 2 51 - (A2) K < $\delta$ / 2 52 53 Parameters 54 ---------- 55 u : float 56 Utility gained from consuming the primary product. 57 B : float 58 Minimal 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 the entrant in case of failure. 59 small_delta : float 60 ($\delta$) Additional utility gained from a complement combined with a primary product. 61 delta : float 62 ($\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent. 63 K : float 64 Investment costs for the entrant to develop a second product. 65 """ 66 super(BaseModel, self).__init__() 67 assert small_delta / 2 < delta < 3 * small_delta / 2, "(A1b) not satisfied." 68 assert K < small_delta / 2, "(A2) not satisfied." 69 self._u: float = u 70 self._B: float = B 71 self._small_delta: float = small_delta 72 self._delta: float = delta 73 self._K: float = K 74 self._copying_fixed_costs: Dict[str, float] = self._calculate_copying_fixed_costs_values() 75 self._assets: Dict[str, float] = self._calculate_asset_values() 76 self._payoffs: Dict[str, Dict[str, float]] = self._calculate_payoffs()
Initializes a valid BaseModel object.
The following preconditions have to be satisfied:
- (A1b) $\delta$ / 2 < $\Delta$ < 3 * $\delta$ / 2
- (A2) K < $\delta$ / 2
Parameters
- u (float): Utility gained from consuming the primary product.
- B (float): Minimal 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 the entrant in case of failure.
- small_delta (float): ($\delta$) Additional utility gained from a complement combined with a primary product.
- delta (float): ($\Delta$) Additional utility gained from the substitute of the entrant compared to the primary product of the incumbent.
- K (float): Investment costs for the entrant to develop a second product.
153 def get_asset_values(self) -> Dict[str, float]: 154 """ 155 Returns the asset thresholds of the entrant. 156 157 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 158 |----------------|:----------|:-----------| 159 | $\\underline{A}_S$ | A_s | $(2)\: B + K - \Delta - 3\delta/2$ | 160 | $\\underline{A}_C$ | A_c | $(3)\: B + K - 3\delta/2$ | 161 | $\overline{A}_S$ | A-s | $(4)\: B + K - \Delta$ | 162 | $\overline{A}_C$ | A-c | $(5)\: B + K - \delta/2$ | 163 <br> 164 Returns 165 ------- 166 Dict[str, float] 167 Includes the thresholds for the assets of the entrant. 168 """ 169 return self._assets
Returns the asset thresholds of the entrant.
Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ |
---|---|---|
$\underline{A}_S$ | A_s | $(2)\: B + K - \Delta - 3\delta/2$ |
$\underline{A}_C$ | A_c | $(3)\: B + K - 3\delta/2$ |
$\overline{A}_S$ | A-s | $(4)\: B + K - \Delta$ |
$\overline{A}_C$ | A-c | $(5)\: B + K - \delta/2$ |
Returns
- Dict[str, float]: Includes the thresholds for the assets of the entrant.
171 def get_copying_fixed_costs_values(self) -> Dict[str, float]: 172 """ 173 Returns the fixed costs for copying thresholds of the incumbent. 174 175 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 176 |----------|:-------|:--------| 177 | $F^{YY}_S$ | F(YY)s | $(6)\: \delta/2$ | 178 | $F^{YN}_S$ | F(YN)s | $(6)\: u + 3\delta/2$ | 179 | $F^{YY}_C$ | F(YY)c | $(6)\: \delta$ | 180 | $F^{YN}_C$ | F(YN)c | $(6)\: \delta/2$ | 181 <br> 182 Returns 183 ------- 184 Dict[str, float] 185 Includes the thresholds for the fixed costs for copying of the incumbent. 186 """ 187 return self._copying_fixed_costs
Returns the fixed costs for copying thresholds of the incumbent.
Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ |
---|---|---|
$F^{YY}_S$ | F(YY)s | $(6)\: \delta/2$ |
$F^{YN}_S$ | F(YN)s | $(6)\: u + 3\delta/2$ |
$F^{YY}_C$ | F(YY)c | $(6)\: \delta$ |
$F^{YN}_C$ | F(YN)c | $(6)\: \delta/2$ |
Returns
- Dict[str, float]: Includes the thresholds for the fixed costs for copying of the incumbent.
189 def get_payoffs(self) -> Dict[str, Dict[str, float]]: 190 """ 191 Returns the payoffs for different market configurations. 192 193 A market configuration can include: 194 - $I_P$ : Primary product sold by the incumbent. 195 - $I_C$ : Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$. 196 - $E_P$ : Perfect substitute to $I_P$ potentially sold by the entrant. 197 - $E_C$ : Complementary product to $I_P$ currently sold by the entrant 198 - $\\tilde{E}_C$ : Complementary product to $I_P$ potentially sold by the entrant. 199 <br> 200 201 | Market Config. $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(I) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(E) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | CS $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | W $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 202 |-----------------------|:--------|:--------|:--|:-| 203 | $I_P$ ; $E_C$ | $u + \delta/2$ | $\delta/2$ | 0 | $u + \delta$ | 204 | $I_P + I_C$ ; $E_C$ | $u + \delta$ | 0 | 0 | $u + \delta$ | 205 | $I_P$ ; $E_P + E_C$ | 0 | $\Delta + \delta$ | $u$ | $u + \Delta + \delta$ | 206 | $I_P + I_C$ ; $E_P + E_C$ | 0 | $\Delta$ | $u + \delta$ | $u + \Delta + \delta$ | 207 | $I_P$ ; $E_C + \\tilde{E}_C$ | $u + \delta$ | $\delta$ | 0 | $u + 2\delta$ | 208 | $I_P + I_C$ ; $E_C + \\tilde{E}_C$ | $u + 3\delta/2$ | $\delta/2$ | 0 | $u + 2\delta$ | 209 <br> 210 211 Returns 212 ------- 213 Dict[str, Dict[str, float]] 214 Contains the mentioned payoffs for different market configurations. 215 """ 216 return self._payoffs
Returns the payoffs for different market configurations.
A market configuration can include:
- $I_P$ : Primary product sold by the incumbent.
- $I_C$ : Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$.
- $E_P$ : Perfect substitute to $I_P$ potentially sold by the entrant.
- $E_C$ : Complementary product to $I_P$ currently sold by the entrant
- $\tilde{E}_C$ : Complementary product to $I_P$ potentially sold by the entrant.
Market Config. $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(I) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(E) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | CS $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | W $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ |
---|---|---|---|---|
$I_P$ ; $E_C$ | $u + \delta/2$ | $\delta/2$ | 0 | $u + \delta$ |
$I_P + I_C$ ; $E_C$ | $u + \delta$ | 0 | 0 | $u + \delta$ |
$I_P$ ; $E_P + E_C$ | 0 | $\Delta + \delta$ | $u$ | $u + \Delta + \delta$ |
$I_P + I_C$ ; $E_P + E_C$ | 0 | $\Delta$ | $u + \delta$ | $u + \Delta + \delta$ |
$I_P$ ; $E_C + \tilde{E}_C$ | $u + \delta$ | $\delta$ | 0 | $u + 2\delta$ |
$I_P + I_C$ ; $E_C + \tilde{E}_C$ | $u + 3\delta/2$ | $\delta/2$ | 0 | $u + 2\delta$ |
Returns
- Dict[str, Dict[str, float]]: Contains the mentioned payoffs for different market configurations.
218 def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: 219 result: Dict[str, str] = {"entrant": "", "incumbent": "", "development": ""} 220 if self._copying_fixed_costs["F(YN)c"] <= F <= self._copying_fixed_costs["F(YN)s"] and A < self._assets["A-s"]: 221 result.update({"entrant": self.ENTRANT_CHOICES["complement"]}) 222 result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) 223 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 224 elif F <= self._copying_fixed_costs["F(YN)c"] and A < self._assets["A-s"]: 225 result.update({"entrant": self.ENTRANT_CHOICES["indifferent"]}) 226 result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) 227 result.update({"development": self.DEVELOPMENT_OUTCOME["failure"]}) 228 else: 229 result.update({"entrant": self.ENTRANT_CHOICES["substitute"]}) 230 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 231 if F <= self._copying_fixed_costs["F(YY)s"]: 232 result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) 233 else: 234 result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) 235 return result
Returns 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)
To understand the details of the logic implemented, consult the chapter in Shelegia and Motta (2021) corresponding to the model.
Parameters
- A (float): Assets of the entrant.
- F (float): Fixed costs for copying of the incumbent.
Returns
- Dict[str, str]: Optimal choice of the entrant, the incumbent and the outcome of the development.
289 def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 290 poly_coordinates: List[List[Tuple[float, float]]] = self._get_incumbent_best_answer_coordinates( 291 kwargs.get("x_max", 0), 292 kwargs.get("y_max", 0)) 293 poly_labels: List[str] = self._get_incumbent_best_answer_labels() 294 kwargs.update({'title': kwargs.get('title', "Best Answers of the incumbent to the choices of the entrant")}) 295 return self._plot(coordinates=poly_coordinates, labels=poly_labels, axis=axis, **kwargs)
Plots the best answers of the incumbent to all possible actions of the entrant.
Parameters
- axis (matplotlib.axes.Axes): Axis to draw the plot on. (optional)
- **kwargs: Optional key word arguments for the best answers plot.
- title: title on top of the plot, instead of the default title.
- legend: If false, all legends are turned off.
- options_legend: If true, an additional legend, explaining the options of the entrant and the incumbent, will be added to the plot.
- asset_legend: If true, an additional legend explaining the thresholds of the assets of the entrant will be added to the plot.
- costs_legend: If true, an additional legend explaining the thresholds of the fixed costs of copying for the incumbent will be added to the plot.
- legend_width : Maximum number of characters in one line in the legend (for adjustments to figure width).
- x_max : Maximum number plotted on the x - axis.
- y_max : Maximum number plotted on the y - axis.
- title: title on top of the plot, instead of the default title.
Returns
- matplotlib.axes.Axes: Axis containing the plot.
397 def plot_equilibrium(self, axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 398 poly_coordinates: List[List[Tuple[float, float]]] = self._get_equilibrium_coordinates(kwargs.get("x_max", 0), 399 kwargs.get("y_max", 0)) 400 poly_labels: List[str] = self._get_equilibrium_labels() 401 kwargs.update({'title': kwargs.get('title', 'Equilibrium Path')}) 402 return self._plot(coordinates=poly_coordinates, labels=poly_labels, axis=axis, **kwargs)
Plots the equilibrium path based on the choices of the entrant and incumbent.
Parameters
- axis (matplotlib.axes.Axes): Axis to draw the plot on. (optional)
- **kwargs: Optional key word arguments for the equilibrium plot.
- title: title on top of the plot, instead of the default title.
- legend: If false, all legends are turned off.
- options_legend: If true, an additional legend, explaining the options of the entrant and the incumbent, will be added to the plot.
- asset_legend: If true, an additional legend explaining the thresholds of the assets of the entrant will be added to the plot.
- costs_legend: If true, an additional legend explaining the thresholds of the fixed costs of copying for the incumbent will be added to the plot.
- legend_width : Maximum number of characters in one line in the legend (for adjustments to figure width).
- x_max : Maximum number plotted on the x - axis.
- y_max : Maximum number plotted on the y - axis.
- title: title on top of the plot, instead of the default title.
Returns
- matplotlib.axes.Axes: Axis containing the plot.
456 def plot_payoffs(self, axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 457 if axis is None: 458 plot_fig, axis = plt.subplots() 459 index = arange(0, len(self._payoffs) * 2, 2) 460 bar_width = 0.35 461 spacing = 0.05 462 463 self._plot_payoffs_bars(axis, bar_width, index, spacing, **kwargs) 464 465 axis.set_xlabel('Market Configurations') 466 axis.set_title('Payoffs for different Market Configurations') 467 self._set_payoffs_ticks(axis, bar_width, index, spacing) 468 if kwargs.get("legend", True): 469 self._set_payoff_legend(axis, kwargs.get("products_legend", False)) 470 self._set_payoffs_figure(axis) 471 return axis
Plots the payoffs for different market configurations.
Parameters
- axis (matplotlib.axes.Axes): Axis to draw the plot on. (optional)
- **kwargs: Optional key word arguments for the payoff plot.
- legend: If false, all legends are turned off.
- products_legend: If true, a legend, containing all possible products of the entrant and the incumbent, will be added to the plot.
- opacity : Opacity of the not optimal payoffs. (floating number between 0 and 1)
- legend: If false, all legends are turned off.
Returns
- matplotlib.axes.Axes: Axis containing the plot.
Inherited Members
927class BargainingPowerModel(BaseModel): 928 """ 929 Besides the parameters used in the paper (and in the BaseModel), this class will introduce the parameter $\\beta$ in the models, called 930 the bargaining power of the incumbent. $\\beta$ describes how much of the profits from the complementary product of the entrant will go to the incumbent 931 In the paper the default value $\\beta=0.5$ is used to derive the results, which indicate an equal share of the profits. 932 """ 933 934 def __init__(self, u: float = 1, B: float = 0.5, small_delta: float = 0.5, delta: float = 0.51, 935 K: float = 0.2, beta: float = 0.5): 936 """ 937 Besides $\\beta$ the parameters in this model do not change compared to Shelegia_Motta_2021.Models.BaseModel. 938 939 Parameters 940 ---------- 941 beta: float 942 Bargaining power of the incumbent relative to the entrant ($0 < \\beta < 1$). 943 """ 944 assert 0 < beta < 1, 'Invalid bargaining power beta (has to be between 0 and 1).' 945 self._beta: float = beta 946 super(BargainingPowerModel, self).__init__(u=u, B=B, small_delta=small_delta, delta=delta, K=K) 947 948 def _calculate_payoffs(self) -> Dict[str, Dict[str, float]]: 949 """ 950 Calculates the payoffs for different market configurations with the formulas given in the paper. 951 952 The formulas are tabulated in BargainingPowerModel.get_payoffs, which are different to the BaseModel. 953 954 Returns 955 ------- 956 Dict[str, Dict[str, float]] 957 Contains the mentioned payoffs for different market configurations. 958 """ 959 payoffs: Dict[str, Dict[str, float]] = super()._calculate_payoffs() 960 # basic market. 961 payoffs['basic']['pi(I)'] = self._u + self._small_delta * self._beta 962 payoffs['basic']['pi(E)'] = self._small_delta * (1 - self._beta) 963 964 # additional complement of the entrant 965 payoffs['E(C)']['pi(I)'] = self._u + 2 * self._small_delta * self._beta 966 payoffs['E(C)']['pi(E)'] = 2 * self._small_delta * (1 - self._beta) 967 968 # additional complement of the incumbent and the entrant 969 payoffs['I(C)E(C)']['pi(I)'] = self._u + self._small_delta * (1 + self._beta) 970 payoffs['I(C)E(C)']['pi(E)'] = self._small_delta * (1 - self._beta) 971 972 return payoffs 973 974 def _calculate_copying_fixed_costs_values(self) -> Dict[str, float]: 975 """ 976 Calculates the thresholds for the fixed costs of copying for the incumbent. 977 978 The formulas are tabulated in BargainingPowerModel.get_copying_fixed_costs_values, which are different to the BaseModel. 979 980 Returns 981 ------- 982 Dict[str, float] 983 Includes the thresholds for the fixed costs for copying of the incumbent. 984 """ 985 return {'F(YY)s': self._small_delta * (1 - self._beta), 986 'F(YN)s': self._u + self._small_delta * (2 - self._beta), 987 'F(YY)c': 2 * self._small_delta * (1 - self._beta), 988 'F(YN)c': self._small_delta * (2 - 3 * self._beta)} 989 990 def _calculate_asset_values(self) -> Dict[str, float]: 991 """ 992 Calculates the thresholds for the assets of the entrant. 993 994 The formulas are tabulated in BargainingPowerModel.get_asset_values, which are different to the BaseModel. 995 996 Returns 997 ------- 998 Dict[str, float] 999 Includes the thresholds for the assets of the entrant. 1000 """ 1001 return {'A_s': self._K + self._B - self._delta - self._small_delta * (2 - self._beta), 1002 'A_c': self._K + self._B - 3 * self._small_delta * (1 - self._beta), 1003 'A-s': self._K + self._B - self._delta, 1004 'A-c': self._K + self._B - self._small_delta * (1 - self._beta)} 1005 1006 def get_asset_values(self) -> Dict[str, float]: 1007 """ 1008 Returns the asset thresholds of the entrant. 1009 1010 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 1011 |----------------|:----------|:-----------| 1012 | $\\underline{A}_S$ | A_s | $B + K - \Delta - \delta(2 - \\beta)$ | 1013 | $\\underline{A}_C$ | A_c | $B + K - 3\delta(1 - \\beta)$ | 1014 | $\overline{A}_S$ | A-s | $B + K - \Delta$ | 1015 | $\overline{A}_C$ | A-c | $B + K - \delta(1 - \\beta)$ | 1016 <br> 1017 Returns 1018 ------- 1019 Dict[str, float] 1020 Includes the thresholds for the assets of the entrant. 1021 """ 1022 return self._assets 1023 1024 def get_copying_fixed_costs_values(self) -> Dict[str, float]: 1025 """ 1026 Returns the fixed costs for copying thresholds of the incumbent. 1027 1028 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 1029 |----------|:-------|:--------| 1030 | $F^{YY}_S$ | F(YY)s | $\delta(1 - \\beta)$ | 1031 | $F^{YN}_S$ | F(YN)s | $u + \delta(2 - \\beta)$ | 1032 | $F^{YY}_C$ | F(YY)c | $2\delta(1 - \\beta)$ | 1033 | $F^{YN}_C$ | F(YN)c | $\delta(2 - \\beta)$ | 1034 <br> 1035 Returns 1036 ------- 1037 Dict[str, float] 1038 Includes the thresholds for the fixed costs for copying of the incumbent. 1039 """ 1040 return self._copying_fixed_costs 1041 1042 def get_payoffs(self) -> Dict[str, Dict[str, float]]: 1043 """ 1044 Returns the payoffs for different market configurations. 1045 1046 A market configuration can include: 1047 - $I_P$ : Primary product sold by the incumbent. 1048 - $I_C$ : Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$. 1049 - $E_P$ : Perfect substitute to $I_P$ potentially sold by the entrant. 1050 - $E_C$ : Complementary product to $I_P$ currently sold by the entrant 1051 - $\\tilde{E}_C$ : Complementary product to $I_P$ potentially sold by the entrant. 1052 <br> 1053 1054 | Market Config. $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(I) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(E) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | CS $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | W $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 1055 |-----------------------|:--------|:--------|:--|:-| 1056 | $I_P$ ; $E_C$ | $u + \delta\\beta$ | $\delta(1 - \\beta)$ | 0 | $u + \delta$ | 1057 | $I_P + I_C$ ; $E_C$ | $u + \delta$ | 0 | 0 | $u + \delta$ | 1058 | $I_P$ ; $E_P + E_C$ | 0 | $\Delta + \delta$ | $u$ | $u + \Delta + \delta$ | 1059 | $I_P + I_C$ ; $E_P + E_C$ | 0 | $\Delta$ | $u + \delta$ | $u + \Delta + \delta$ | 1060 | $I_P$ ; $E_C + \\tilde{E}_C$ | $u + 2\delta\\beta$ | $2\delta(1 - \\beta)$ | 0 | $u + 2\delta$ | 1061 | $I_P + I_C$ ; $E_C + \\tilde{E}_C$ | $u + \delta(1 + \\beta)$ | $\delta(1 - \\beta)$ | 0 | $u + 2\delta$ | 1062 <br> 1063 1064 Returns 1065 ------- 1066 Dict[str, Dict[str, float]] 1067 Contains the mentioned payoffs for different market configurations. 1068 """ 1069 return self._payoffs 1070 1071 def _get_incumbent_best_answer_coordinates(self, x_max: float, y_max: float) -> List[List[Tuple[float, float]]]: 1072 coordinates: List[List[Tuple[float, float]]] = super(BargainingPowerModel, 1073 self)._get_incumbent_best_answer_coordinates(x_max=x_max, 1074 y_max=y_max) 1075 # add additional area 7 1076 if self._copying_fixed_costs["F(YY)s"] != self._copying_fixed_costs["F(YN)c"]: 1077 coordinates.append([(self._assets['A-s'], self._copying_fixed_costs['F(YY)s']), 1078 (self._assets['A-c'], self._copying_fixed_costs['F(YY)s']), 1079 (self._assets['A-c'], max(self._copying_fixed_costs['F(YN)c'], 0)), 1080 (self._assets['A-s'], max(self._copying_fixed_costs['F(YN)c'], 0))]) 1081 return coordinates 1082 1083 def _get_incumbent_best_answer_labels(self) -> List[str]: 1084 labels: List[str] = super(BargainingPowerModel, self)._get_incumbent_best_answer_labels() 1085 # add additional label for area 7 1086 if self._copying_fixed_costs["F(YY)s"] != self._copying_fixed_costs["F(YN)c"]: 1087 if self._copying_fixed_costs["F(YY)s"] > self._copying_fixed_costs["F(YN)c"]: 1088 labels.append( 1089 # Area 7 1090 self._create_choice_answer_label(entrant="substitute", incumbent="copy", 1091 development="success") + " \n" + 1092 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success"), 1093 ) 1094 else: 1095 labels.append( 1096 # Area 7 1097 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", 1098 development="success") + " \n" + 1099 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="failure"), 1100 ) 1101 return labels
Besides the parameters used in the paper (and in the BaseModel), this class will introduce the parameter $\beta$ in the models, called the bargaining power of the incumbent. $\beta$ describes how much of the profits from the complementary product of the entrant will go to the incumbent In the paper the default value $\beta=0.5$ is used to derive the results, which indicate an equal share of the profits.
934 def __init__(self, u: float = 1, B: float = 0.5, small_delta: float = 0.5, delta: float = 0.51, 935 K: float = 0.2, beta: float = 0.5): 936 """ 937 Besides $\\beta$ the parameters in this model do not change compared to Shelegia_Motta_2021.Models.BaseModel. 938 939 Parameters 940 ---------- 941 beta: float 942 Bargaining power of the incumbent relative to the entrant ($0 < \\beta < 1$). 943 """ 944 assert 0 < beta < 1, 'Invalid bargaining power beta (has to be between 0 and 1).' 945 self._beta: float = beta 946 super(BargainingPowerModel, self).__init__(u=u, B=B, small_delta=small_delta, delta=delta, K=K)
Besides $\beta$ the parameters in this model do not change compared to Shelegia_Motta_2021.Models.BaseModel.
Parameters
- beta (float): Bargaining power of the incumbent relative to the entrant ($0 < \beta < 1$).
1006 def get_asset_values(self) -> Dict[str, float]: 1007 """ 1008 Returns the asset thresholds of the entrant. 1009 1010 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 1011 |----------------|:----------|:-----------| 1012 | $\\underline{A}_S$ | A_s | $B + K - \Delta - \delta(2 - \\beta)$ | 1013 | $\\underline{A}_C$ | A_c | $B + K - 3\delta(1 - \\beta)$ | 1014 | $\overline{A}_S$ | A-s | $B + K - \Delta$ | 1015 | $\overline{A}_C$ | A-c | $B + K - \delta(1 - \\beta)$ | 1016 <br> 1017 Returns 1018 ------- 1019 Dict[str, float] 1020 Includes the thresholds for the assets of the entrant. 1021 """ 1022 return self._assets
Returns the asset thresholds of the entrant.
Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ |
---|---|---|
$\underline{A}_S$ | A_s | $B + K - \Delta - \delta(2 - \beta)$ |
$\underline{A}_C$ | A_c | $B + K - 3\delta(1 - \beta)$ |
$\overline{A}_S$ | A-s | $B + K - \Delta$ |
$\overline{A}_C$ | A-c | $B + K - \delta(1 - \beta)$ |
Returns
- Dict[str, float]: Includes the thresholds for the assets of the entrant.
1024 def get_copying_fixed_costs_values(self) -> Dict[str, float]: 1025 """ 1026 Returns the fixed costs for copying thresholds of the incumbent. 1027 1028 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 1029 |----------|:-------|:--------| 1030 | $F^{YY}_S$ | F(YY)s | $\delta(1 - \\beta)$ | 1031 | $F^{YN}_S$ | F(YN)s | $u + \delta(2 - \\beta)$ | 1032 | $F^{YY}_C$ | F(YY)c | $2\delta(1 - \\beta)$ | 1033 | $F^{YN}_C$ | F(YN)c | $\delta(2 - \\beta)$ | 1034 <br> 1035 Returns 1036 ------- 1037 Dict[str, float] 1038 Includes the thresholds for the fixed costs for copying of the incumbent. 1039 """ 1040 return self._copying_fixed_costs
Returns the fixed costs for copying thresholds of the incumbent.
Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ |
---|---|---|
$F^{YY}_S$ | F(YY)s | $\delta(1 - \beta)$ |
$F^{YN}_S$ | F(YN)s | $u + \delta(2 - \beta)$ |
$F^{YY}_C$ | F(YY)c | $2\delta(1 - \beta)$ |
$F^{YN}_C$ | F(YN)c | $\delta(2 - \beta)$ |
Returns
- Dict[str, float]: Includes the thresholds for the fixed costs for copying of the incumbent.
1042 def get_payoffs(self) -> Dict[str, Dict[str, float]]: 1043 """ 1044 Returns the payoffs for different market configurations. 1045 1046 A market configuration can include: 1047 - $I_P$ : Primary product sold by the incumbent. 1048 - $I_C$ : Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$. 1049 - $E_P$ : Perfect substitute to $I_P$ potentially sold by the entrant. 1050 - $E_C$ : Complementary product to $I_P$ currently sold by the entrant 1051 - $\\tilde{E}_C$ : Complementary product to $I_P$ potentially sold by the entrant. 1052 <br> 1053 1054 | Market Config. $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(I) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(E) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | CS $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | W $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 1055 |-----------------------|:--------|:--------|:--|:-| 1056 | $I_P$ ; $E_C$ | $u + \delta\\beta$ | $\delta(1 - \\beta)$ | 0 | $u + \delta$ | 1057 | $I_P + I_C$ ; $E_C$ | $u + \delta$ | 0 | 0 | $u + \delta$ | 1058 | $I_P$ ; $E_P + E_C$ | 0 | $\Delta + \delta$ | $u$ | $u + \Delta + \delta$ | 1059 | $I_P + I_C$ ; $E_P + E_C$ | 0 | $\Delta$ | $u + \delta$ | $u + \Delta + \delta$ | 1060 | $I_P$ ; $E_C + \\tilde{E}_C$ | $u + 2\delta\\beta$ | $2\delta(1 - \\beta)$ | 0 | $u + 2\delta$ | 1061 | $I_P + I_C$ ; $E_C + \\tilde{E}_C$ | $u + \delta(1 + \\beta)$ | $\delta(1 - \\beta)$ | 0 | $u + 2\delta$ | 1062 <br> 1063 1064 Returns 1065 ------- 1066 Dict[str, Dict[str, float]] 1067 Contains the mentioned payoffs for different market configurations. 1068 """ 1069 return self._payoffs
Returns the payoffs for different market configurations.
A market configuration can include:
- $I_P$ : Primary product sold by the incumbent.
- $I_C$ : Complementary product to $I_P$ potentially sold by the incumbent, which is copied from $E_C$.
- $E_P$ : Perfect substitute to $I_P$ potentially sold by the entrant.
- $E_C$ : Complementary product to $I_P$ currently sold by the entrant
- $\tilde{E}_C$ : Complementary product to $I_P$ potentially sold by the entrant.
Market Config. $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(I) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | $\pi(E) \:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | CS $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | W $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ |
---|---|---|---|---|
$I_P$ ; $E_C$ | $u + \delta\beta$ | $\delta(1 - \beta)$ | 0 | $u + \delta$ |
$I_P + I_C$ ; $E_C$ | $u + \delta$ | 0 | 0 | $u + \delta$ |
$I_P$ ; $E_P + E_C$ | 0 | $\Delta + \delta$ | $u$ | $u + \Delta + \delta$ |
$I_P + I_C$ ; $E_P + E_C$ | 0 | $\Delta$ | $u + \delta$ | $u + \Delta + \delta$ |
$I_P$ ; $E_C + \tilde{E}_C$ | $u + 2\delta\beta$ | $2\delta(1 - \beta)$ | 0 | $u + 2\delta$ |
$I_P + I_C$ ; $E_C + \tilde{E}_C$ | $u + \delta(1 + \beta)$ | $\delta(1 - \beta)$ | 0 | $u + 2\delta$ |
Returns
- Dict[str, Dict[str, float]]: Contains the mentioned payoffs for different market configurations.
1104class UnobservableModel(BargainingPowerModel): 1105 """ 1106 This model indicates that if the incumbent were not able to observe the entrant at the moment of choosing, 1107 the “kill zone” effect whereby the entrant stays away from the substitute in order to avoid being copied would not take place. 1108 Intuitively, in the game as we studied it so far, the only reason why the entrant is choosing a trajectory leading to another complement 1109 is that it anticipates that if it chose one leading to a substitute, the incumbent would copy, making it an inefficient strategy 1110 for entering the market. However, if the incumbent cannot observe the entrant’s choice of strategy, the entrant could not hope to strategically affect the decision 1111 of the incumbent. This would lead to the entrant having a host of new opportunities when entering the market makes the entrant competing with a large company much more attractive. 1112 1113 Although there may be situations where the entrant could commit to some actions (product design or marketing choices) 1114 which signals that it will not become a rival, and it would have all the incentive to commit to do so, 1115 then the game would be like the sequential moves game analyzed in the basic model. 1116 Otherwise, the entrant will never choose a complement just to avoid copying, and it will enter the “kill zone”. 1117 """ 1118 1119 def __init__(self, u: float = 1, B: float = 0.5, small_delta: float = 0.5, delta: float = 0.51, 1120 K: float = 0.2, beta: float = 0.5): 1121 """ 1122 The parameters do not change compared to Shelegia_Motta_2021.Models.BargainingPowerModel. 1123 """ 1124 super(UnobservableModel, self).__init__(u=u, B=B, small_delta=small_delta, delta=delta, K=K, beta=beta) 1125 1126 def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 1127 return self.plot_equilibrium(axis=axis, **kwargs) 1128 1129 def _create_choice_answer_label(self, entrant: Literal["complement", "substitute", "indifferent"], 1130 incumbent: Literal["copy", "refrain"], 1131 development: Literal["success", "failure"], 1132 kill_zone: bool = False, 1133 acquisition: str = "") -> str: 1134 return "{" + self.ENTRANT_CHOICES[entrant] + ", " + self.INCUMBENT_CHOICES[incumbent] + "} $\\rightarrow$ " + \ 1135 self.DEVELOPMENT_OUTCOME[development] 1136 1137 def _get_equilibrium_labels(self) -> List[str]: 1138 """ 1139 Returns a list containing the labels for the squares in the plot of the equilibrium path. 1140 1141 For the order of the squares refer to the file resources/dev_notes.md. 1142 1143 Returns 1144 ------- 1145 List containing the labels for the squares in the plot of the best answers of the equilibrium path. 1146 """ 1147 return [ 1148 # Area 1 1149 self._create_choice_answer_label(entrant="indifferent", incumbent="copy", development="failure"), 1150 # Area 2 1151 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success"), 1152 # Area 3 1153 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="failure"), 1154 # Area 4 1155 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success") 1156 ] 1157 1158 def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: 1159 result: Dict = super().get_optimal_choice(A, F) 1160 # adjust the different choices in area three -> since the kill zone does not exist in this model. 1161 if result["entrant"] == self.ENTRANT_CHOICES["complement"]: 1162 result = {"entrant": self.ENTRANT_CHOICES["substitute"], "incumbent": self.INCUMBENT_CHOICES["copy"], 1163 "development": self.DEVELOPMENT_OUTCOME["failure"]} 1164 return result
This model indicates that if the incumbent were not able to observe the entrant at the moment of choosing, the “kill zone” effect whereby the entrant stays away from the substitute in order to avoid being copied would not take place. Intuitively, in the game as we studied it so far, the only reason why the entrant is choosing a trajectory leading to another complement is that it anticipates that if it chose one leading to a substitute, the incumbent would copy, making it an inefficient strategy for entering the market. However, if the incumbent cannot observe the entrant’s choice of strategy, the entrant could not hope to strategically affect the decision of the incumbent. This would lead to the entrant having a host of new opportunities when entering the market makes the entrant competing with a large company much more attractive.
Although there may be situations where the entrant could commit to some actions (product design or marketing choices) which signals that it will not become a rival, and it would have all the incentive to commit to do so, then the game would be like the sequential moves game analyzed in the basic model. Otherwise, the entrant will never choose a complement just to avoid copying, and it will enter the “kill zone”.
1119 def __init__(self, u: float = 1, B: float = 0.5, small_delta: float = 0.5, delta: float = 0.51, 1120 K: float = 0.2, beta: float = 0.5): 1121 """ 1122 The parameters do not change compared to Shelegia_Motta_2021.Models.BargainingPowerModel. 1123 """ 1124 super(UnobservableModel, self).__init__(u=u, B=B, small_delta=small_delta, delta=delta, K=K, beta=beta)
The parameters do not change compared to Shelegia_Motta_2021.Models.BargainingPowerModel.
1126 def plot_incumbent_best_answers(self, axis: matplotlib.axes.Axes = None, **kwargs) -> matplotlib.axes.Axes: 1127 return self.plot_equilibrium(axis=axis, **kwargs)
Plots the best answers of the incumbent to all possible actions of the entrant.
Parameters
- axis (matplotlib.axes.Axes): Axis to draw the plot on. (optional)
- **kwargs: Optional key word arguments for the best answers plot.
- title: title on top of the plot, instead of the default title.
- legend: If false, all legends are turned off.
- options_legend: If true, an additional legend, explaining the options of the entrant and the incumbent, will be added to the plot.
- asset_legend: If true, an additional legend explaining the thresholds of the assets of the entrant will be added to the plot.
- costs_legend: If true, an additional legend explaining the thresholds of the fixed costs of copying for the incumbent will be added to the plot.
- legend_width : Maximum number of characters in one line in the legend (for adjustments to figure width).
- x_max : Maximum number plotted on the x - axis.
- y_max : Maximum number plotted on the y - axis.
- title: title on top of the plot, instead of the default title.
Returns
- matplotlib.axes.Axes: Axis containing the plot.
1158 def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: 1159 result: Dict = super().get_optimal_choice(A, F) 1160 # adjust the different choices in area three -> since the kill zone does not exist in this model. 1161 if result["entrant"] == self.ENTRANT_CHOICES["complement"]: 1162 result = {"entrant": self.ENTRANT_CHOICES["substitute"], "incumbent": self.INCUMBENT_CHOICES["copy"], 1163 "development": self.DEVELOPMENT_OUTCOME["failure"]} 1164 return result
Returns 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)
To understand the details of the logic implemented, consult the chapter in Shelegia and Motta (2021) corresponding to the model.
Parameters
- A (float): Assets of the entrant.
- F (float): Fixed costs for copying of the incumbent.
Returns
- Dict[str, str]: Optimal choice of the entrant, the incumbent and the outcome of the development.
1167class AcquisitionModel(BargainingPowerModel): 1168 """ 1169 In order to explore how acquisitions may modify the entrant’s and the incumbent’s strategic choices, we extend the base model 1170 in order to allow an acquisition to take place after the incumbent commits to copying the entrant’s original complementary product 1171 (between t=1 and t=2, see demo.ipynb "Timing of the game"). We assume that the incumbent and the entrant share the gains (if any) attained from the acquisition equally. 1172 1173 The “kill zone” still appears as a possible equilibrium outcome, however for a more reduced region of the parameter space. 1174 The prospect of getting some acquisition gains does tend to increase the profits gained from developing a substitute to the primary product, 1175 and this explains why part of the “kill zone” region where a complement was chosen without the acquisition, the entrant will now choose a substitute instead. 1176 """ 1177 1178 def __init__(self, u: float = 1, B: float = 0.5, small_delta: float = 0.5, delta: float = 0.51, 1179 K: float = 0.2, beta: float = 0.5) -> None: 1180 """ 1181 An additional constraint is added compared to Shelegia_Motta_2021.Models.BaseModel. Namely, $\Delta$ has to be bigger than $u$, 1182 meaning the innovation of the entrant is not too drastic compared with the primary products of the incumbent. 1183 1184 Meanwhile, the parameters do not change compared to Shelegia_Motta_2021.Models.BargainingPowerModel. 1185 """ 1186 assert delta < u, "Delta has to be smaller than u, meaning the innovation of the entrant is not too drastic." 1187 super(AcquisitionModel, self).__init__(u=u, B=B, small_delta=small_delta, delta=delta, K=K, beta=beta) 1188 self.ACQUISITION_OUTCOME: Final[Dict[str, str]] = {"merged": "M", "apart": "E"} 1189 """ 1190 Contains the options for an acquisition or not. 1191 - merged (M): The incumbent acquired the entrant. 1192 - apart (E): The incumbent did not acquired the entrant. 1193 """ 1194 1195 def _calculate_copying_fixed_costs_values(self) -> Dict[str, float]: 1196 copying_fixed_costs_values: Dict[str, float] = super()._calculate_copying_fixed_costs_values() 1197 copying_fixed_costs_values.update( 1198 {'F(ACQ)s': (self._u + self._delta - self._K) / 2 + self._small_delta * (2 - self._beta), 1199 'F(ACQ)c': self._small_delta * (2.5 - 3 * self._beta) - self._K / 2}) 1200 assert (abs(copying_fixed_costs_values["F(ACQ)c"] - copying_fixed_costs_values["F(YY)c"]) < self.TOLERANCE or 1201 copying_fixed_costs_values["F(ACQ)c"] < copying_fixed_costs_values["F(YY)c"]), "F(ACQ)c has to be smaller or equal than F(YY)c" 1202 return copying_fixed_costs_values 1203 1204 def get_copying_fixed_costs_values(self) -> Dict[str, float]: 1205 """ 1206 Returns the fixed costs for copying thresholds of the incumbent. 1207 1208 Additional thresholds for the fixed cost of copying of the incumbent compared to the Shelegia_Motta_2021.Models.BargainingModel: 1209 1210 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 1211 |----------|:-------|:--------| 1212 | $F^{ACQ}_S$ | F(ACQ)s | $\\frac{(u + \Delta - K)}{2} + \delta(2 - \\beta)$ | 1213 | $F^{ACQ}_C$ | F(ACQ)c | $\\frac{K}{2} + \delta(2.5 - 3\\beta)$ | 1214 <br> 1215 As an additional constraint, $F^{ACQ}_C$ has to be smaller or equal than $F^{YY}_C$, since the logic described in the paper may not apply anymore for the other cases. 1216 1217 Returns 1218 ------- 1219 Dict[str, float] 1220 Includes the thresholds for the fixed costs for copying of the incumbent. 1221 """ 1222 return self._copying_fixed_costs 1223 1224 def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: 1225 """ 1226 Returns 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. 1227 1228 The output dictionary will contain the following details: 1229 1230 - "entrant": choice of the entrant (possible choices listed in Shelegia_Motta_2021.IModel.IModel.ENTRANT_CHOICES)) 1231 - "incumbent": choice of the incumbent (possible choices listed in Shelegia_Motta_2021.IModel.IModel.INCUMBENT_CHOICES) 1232 - "development": outcome of the development (possible outcomes listed in Shelegia_Motta_2021.IModel.IModel.DEVELOPMENT_OUTCOME) 1233 - "acquisition": outcome of the acquisition (possible outcomes listed in Shelegia_Motta_2021.Models.AcquisitionModel.ACQUISITION_OUTCOME) 1234 1235 To understand the details of the logic implemented, consult the chapter in Shelegia and Motta (2021) corresponding to the model. 1236 1237 Parameters 1238 ---------- 1239 A : float 1240 Assets of the entrant. 1241 F : float 1242 Fixed costs for copying of the incumbent. 1243 1244 Returns 1245 ------- 1246 Dict[str, str] 1247 Optimal choice of the entrant, the incumbent and the outcome of the development. 1248 """ 1249 result: Dict[str, str] = {"entrant": "", "incumbent": "", "development": "", "acquisition": ""} 1250 if self._copying_fixed_costs["F(ACQ)c"] <= F <= self._copying_fixed_costs["F(ACQ)s"] and A < self._assets["A-s"]: 1251 result.update({"entrant": self.ENTRANT_CHOICES["complement"]}) 1252 result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) 1253 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 1254 result.update({"acquisition": self.ACQUISITION_OUTCOME["apart"]}) 1255 elif F < self._copying_fixed_costs["F(ACQ)c"] and A < self._assets["A-s"]: 1256 # to develop a substitute is the weakly dominant strategy of the entrant 1257 entrant_choice_area_1: Literal["substitute", "complement"] = "substitute" 1258 # if the payoff for a complement is higher than for a substitute, the entrant will choose the complement. 1259 if self._delta < self._small_delta: 1260 entrant_choice_area_1 = "complement" 1261 result.update({"entrant": self.ENTRANT_CHOICES[entrant_choice_area_1]}) 1262 result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) 1263 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 1264 result.update({"acquisition": self.ACQUISITION_OUTCOME["merged"]}) 1265 else: 1266 result.update({"entrant": self.ENTRANT_CHOICES["substitute"]}) 1267 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 1268 result.update({"acquisition": self.ACQUISITION_OUTCOME["merged"]}) 1269 if F <= self._copying_fixed_costs["F(YY)c"]: 1270 result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) 1271 else: 1272 result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) 1273 return result 1274 1275 def _get_incumbent_best_answer_coordinates(self, x_max: float, y_max: float) -> List[List[Tuple[float, float]]]: 1276 y_max: float = self._get_y_max(y_max) 1277 x_max: float = self._get_x_max(x_max) 1278 return [ 1279 # Area 1 1280 [(0, 0), (self._assets['A-c'], 0), (self._assets['A-c'], max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1281 (0, max(self._copying_fixed_costs['F(ACQ)c'], 0))], 1282 # Area 2 1283 [(self._assets['A-c'], 0), (x_max, 0), (x_max, self._copying_fixed_costs['F(YY)c']), 1284 (self._assets['A-c'], self._copying_fixed_costs['F(YY)c'])], 1285 # Area 3 1286 [(0, max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1287 (self._assets['A-s'], max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1288 (self._assets['A-s'], self._copying_fixed_costs['F(ACQ)s']), (0, self._copying_fixed_costs['F(ACQ)s'])], 1289 # Area 4 1290 [(self._assets['A-s'], max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1291 (self._assets['A-c'], max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1292 (self._assets['A-c'], self._copying_fixed_costs['F(YY)c']), 1293 (self._assets['A-s'], self._copying_fixed_costs['F(YY)c'])], 1294 # Area 5 1295 [(self._assets['A-s'], self._copying_fixed_costs['F(YY)c']), (x_max, self._copying_fixed_costs['F(YY)c']), 1296 (x_max, y_max), 1297 (0, y_max), (0, self._copying_fixed_costs['F(ACQ)s']), 1298 (self._assets['A-s'], self._copying_fixed_costs['F(ACQ)s'])]] 1299 1300 def _get_incumbent_best_answer_labels(self) -> List[str]: 1301 return [ 1302 # Area 1 1303 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success", 1304 acquisition=self.ACQUISITION_OUTCOME["merged"]) + " \n" + 1305 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="success", 1306 acquisition=self.ACQUISITION_OUTCOME["merged"]), 1307 # Area 2 1308 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success", 1309 acquisition=self.ACQUISITION_OUTCOME["merged"]) + " \n" + 1310 self._create_choice_answer_label(entrant="complement", incumbent="copy", development="success", 1311 acquisition=self.ACQUISITION_OUTCOME["apart"]), 1312 # Area 3 1313 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success", 1314 acquisition=self.ACQUISITION_OUTCOME["merged"]) + " \n" + 1315 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success", 1316 acquisition=self.ACQUISITION_OUTCOME["apart"]), 1317 # Area 4 1318 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success", 1319 acquisition=self.ACQUISITION_OUTCOME["merged"]) + " \n" + 1320 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success", 1321 acquisition=self.ACQUISITION_OUTCOME["apart"]), 1322 # Area 5 1323 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success", 1324 acquisition=self.ACQUISITION_OUTCOME["merged"]) + " \n" + 1325 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success", 1326 acquisition=self.ACQUISITION_OUTCOME["apart"]), 1327 ] 1328 1329 def _get_equilibrium_coordinates(self, x_max: float, y_max: float) -> List[List[Tuple[float, float]]]: 1330 y_max: float = self._get_y_max(y_max) 1331 x_max: float = self._get_x_max(x_max) 1332 return [ 1333 # Area 1 1334 [(0, 0), (self._assets['A-s'], 0), (self._assets['A-s'], max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1335 (0, max(self._copying_fixed_costs['F(ACQ)c'], 0))], 1336 # Area 2 1337 [(self._assets['A-s'], 0), (x_max, 0), (x_max, self._copying_fixed_costs['F(YY)c']), 1338 (self._assets['A-s'], self._copying_fixed_costs['F(YY)c'])], 1339 # Area 3 1340 [(0, max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1341 (self._assets['A-s'], max(self._copying_fixed_costs['F(ACQ)c'], 0)), 1342 (self._assets['A-s'], self._copying_fixed_costs['F(ACQ)s']), (0, self._copying_fixed_costs['F(ACQ)s'])], 1343 # Area 4 1344 [(self._assets['A-s'], self._copying_fixed_costs['F(YY)c']), (x_max, self._copying_fixed_costs['F(YY)c']), 1345 (x_max, y_max), (0, y_max), (0, self._copying_fixed_costs['F(ACQ)s']), 1346 (self._assets['A-s'], self._copying_fixed_costs['F(ACQ)s'])]] 1347 1348 def _get_equilibrium_labels(self) -> List[str]: 1349 # to develop a substitute is the weakly dominant strategy of the entrant 1350 entrant_choice_area_1: Literal["substitute", "complement"] = "substitute" 1351 # if the payoff for a complement is higher than for a substitute, the entrant will choose the complement. 1352 if self._delta < self._small_delta: 1353 entrant_choice_area_1 = "complement" 1354 return [ 1355 # Area 1 1356 self._create_choice_answer_label(entrant=entrant_choice_area_1, incumbent="copy", development="success", 1357 acquisition=self.ACQUISITION_OUTCOME["merged"]), 1358 # Area 2 1359 self._create_choice_answer_label(entrant="substitute", incumbent="copy", development="success", 1360 acquisition=self.ACQUISITION_OUTCOME["merged"]), 1361 # Area 3 1362 self._create_choice_answer_label(entrant="complement", incumbent="refrain", development="success", 1363 kill_zone=True, acquisition=self.ACQUISITION_OUTCOME["apart"]), 1364 # Area 4 1365 self._create_choice_answer_label(entrant="substitute", incumbent="refrain", development="success", 1366 acquisition=self.ACQUISITION_OUTCOME["merged"]) 1367 ] 1368 1369 def _draw_thresholds(self, axis: matplotlib.axes.Axes, x_horizontal: float = 0, y_vertical: float = 0) -> None: 1370 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(ACQ)s'], label="$F^{ACQ}_S$", 1371 x=x_horizontal) 1372 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YN)s'], label="$F^{YN}_S$", 1373 x=x_horizontal) 1374 1375 if abs(self._copying_fixed_costs['F(YY)c'] - self._copying_fixed_costs['F(ACQ)c']) < self.TOLERANCE: 1376 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(ACQ)c'], x=x_horizontal, 1377 label="$F^{ACQ}_C=F^{YY}_C$") 1378 else: 1379 if self._copying_fixed_costs['F(ACQ)c'] >= 0: 1380 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(ACQ)c'], x=x_horizontal, 1381 label="$F^{ACQ}_C$") 1382 self._draw_horizontal_line_with_label(axis, y=self._copying_fixed_costs['F(YY)c'], label="$F^{YY}_C$", 1383 x=x_horizontal) 1384 # vertical lines (asset thresholds) 1385 self._draw_vertical_line_with_label(axis, x=self._assets['A-s'], label=r'$\bar{A}_S$', y=y_vertical) 1386 self._draw_vertical_line_with_label(axis, x=self._assets['A-c'], label=r'$\bar{A}_C$', y=y_vertical) 1387 1388 @staticmethod 1389 def _create_cost_thresholds_legend(width: int) -> str: 1390 legend: str = super(AcquisitionModel, AcquisitionModel)._create_cost_thresholds_legend(width=width) 1391 return legend + "\n" + \ 1392 AcquisitionModel._format_legend_line(r'$F^{ACQ}_C$' + ": Maximum level of fixed costs that ensure that the incumbent acquires the entrant if the entrant develops a second complement.", width=width) + "\n" + \ 1393 AcquisitionModel._format_legend_line(r'$F^{ACQ}_S$' + ": Maximum level of fixed costs that ensure that the incumbent acquires the entrant if the entrant develops a perfect substitute.", width=width) 1394 1395 def _create_options_legend(self, width: int) -> str: 1396 legend: str = super(AcquisitionModel, self)._create_options_legend(width=width) 1397 # modify outcomes without acquisition 1398 legend = legend.replace(self.DEVELOPMENT_OUTCOME['success'], "$" + self.DEVELOPMENT_OUTCOME['success'] + "_" + self.ACQUISITION_OUTCOME["apart"] + "$") 1399 legend = legend.replace(self.DEVELOPMENT_OUTCOME['failure'], "$" + self.DEVELOPMENT_OUTCOME['failure'] + "_" + self.ACQUISITION_OUTCOME["apart"] + "$") 1400 1401 # add additional outcomes with acquisition 1402 return legend + "\n" + \ 1403 self._format_legend_line("$" + self.DEVELOPMENT_OUTCOME['success'] + "_" + self.ACQUISITION_OUTCOME["merged"] + "$ : The merged entity has sufficient assets to develop the product.", width=width) + "\n" + \ 1404 self._format_legend_line("$" + self.DEVELOPMENT_OUTCOME['failure'] + "_" + self.ACQUISITION_OUTCOME["merged"] + "$ : The merged entity has not sufficient assets to develop the product.", width=width)
In order to explore how acquisitions may modify the entrant’s and the incumbent’s strategic choices, we extend the base model in order to allow an acquisition to take place after the incumbent commits to copying the entrant’s original complementary product (between t=1 and t=2, see demo.ipynb "Timing of the game"). We assume that the incumbent and the entrant share the gains (if any) attained from the acquisition equally.
The “kill zone” still appears as a possible equilibrium outcome, however for a more reduced region of the parameter space. The prospect of getting some acquisition gains does tend to increase the profits gained from developing a substitute to the primary product, and this explains why part of the “kill zone” region where a complement was chosen without the acquisition, the entrant will now choose a substitute instead.
1178 def __init__(self, u: float = 1, B: float = 0.5, small_delta: float = 0.5, delta: float = 0.51, 1179 K: float = 0.2, beta: float = 0.5) -> None: 1180 """ 1181 An additional constraint is added compared to Shelegia_Motta_2021.Models.BaseModel. Namely, $\Delta$ has to be bigger than $u$, 1182 meaning the innovation of the entrant is not too drastic compared with the primary products of the incumbent. 1183 1184 Meanwhile, the parameters do not change compared to Shelegia_Motta_2021.Models.BargainingPowerModel. 1185 """ 1186 assert delta < u, "Delta has to be smaller than u, meaning the innovation of the entrant is not too drastic." 1187 super(AcquisitionModel, self).__init__(u=u, B=B, small_delta=small_delta, delta=delta, K=K, beta=beta) 1188 self.ACQUISITION_OUTCOME: Final[Dict[str, str]] = {"merged": "M", "apart": "E"} 1189 """ 1190 Contains the options for an acquisition or not. 1191 - merged (M): The incumbent acquired the entrant. 1192 - apart (E): The incumbent did not acquired the entrant. 1193 """
An additional constraint is added compared to Shelegia_Motta_2021.Models.BaseModel. Namely, $\Delta$ has to be bigger than $u$, meaning the innovation of the entrant is not too drastic compared with the primary products of the incumbent.
Meanwhile, the parameters do not change compared to Shelegia_Motta_2021.Models.BargainingPowerModel.
Contains the options for an acquisition or not.
- merged (M): The incumbent acquired the entrant.
- apart (E): The incumbent did not acquired the entrant.
1204 def get_copying_fixed_costs_values(self) -> Dict[str, float]: 1205 """ 1206 Returns the fixed costs for copying thresholds of the incumbent. 1207 1208 Additional thresholds for the fixed cost of copying of the incumbent compared to the Shelegia_Motta_2021.Models.BargainingModel: 1209 1210 | Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ | 1211 |----------|:-------|:--------| 1212 | $F^{ACQ}_S$ | F(ACQ)s | $\\frac{(u + \Delta - K)}{2} + \delta(2 - \\beta)$ | 1213 | $F^{ACQ}_C$ | F(ACQ)c | $\\frac{K}{2} + \delta(2.5 - 3\\beta)$ | 1214 <br> 1215 As an additional constraint, $F^{ACQ}_C$ has to be smaller or equal than $F^{YY}_C$, since the logic described in the paper may not apply anymore for the other cases. 1216 1217 Returns 1218 ------- 1219 Dict[str, float] 1220 Includes the thresholds for the fixed costs for copying of the incumbent. 1221 """ 1222 return self._copying_fixed_costs
Returns the fixed costs for copying thresholds of the incumbent.
Additional thresholds for the fixed cost of copying of the incumbent compared to the Shelegia_Motta_2021.Models.BargainingModel:
Threshold $\:\:\:\:\:$ | Name $\:\:\:\:\:$ | Formula $\:\:\:\:\:\:\:\:\:\:\:\:\:\:\:$ |
---|---|---|
$F^{ACQ}_S$ | F(ACQ)s | $\frac{(u + \Delta - K)}{2} + \delta(2 - \beta)$ |
$F^{ACQ}_C$ | F(ACQ)c | $\frac{K}{2} + \delta(2.5 - 3\beta)$ |
As an additional constraint, $F^{ACQ}_C$ has to be smaller or equal than $F^{YY}_C$, since the logic described in the paper may not apply anymore for the other cases.
Returns
- Dict[str, float]: Includes the thresholds for the fixed costs for copying of the incumbent.
1224 def get_optimal_choice(self, A: float, F: float) -> Dict[str, str]: 1225 """ 1226 Returns 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. 1227 1228 The output dictionary will contain the following details: 1229 1230 - "entrant": choice of the entrant (possible choices listed in Shelegia_Motta_2021.IModel.IModel.ENTRANT_CHOICES)) 1231 - "incumbent": choice of the incumbent (possible choices listed in Shelegia_Motta_2021.IModel.IModel.INCUMBENT_CHOICES) 1232 - "development": outcome of the development (possible outcomes listed in Shelegia_Motta_2021.IModel.IModel.DEVELOPMENT_OUTCOME) 1233 - "acquisition": outcome of the acquisition (possible outcomes listed in Shelegia_Motta_2021.Models.AcquisitionModel.ACQUISITION_OUTCOME) 1234 1235 To understand the details of the logic implemented, consult the chapter in Shelegia and Motta (2021) corresponding to the model. 1236 1237 Parameters 1238 ---------- 1239 A : float 1240 Assets of the entrant. 1241 F : float 1242 Fixed costs for copying of the incumbent. 1243 1244 Returns 1245 ------- 1246 Dict[str, str] 1247 Optimal choice of the entrant, the incumbent and the outcome of the development. 1248 """ 1249 result: Dict[str, str] = {"entrant": "", "incumbent": "", "development": "", "acquisition": ""} 1250 if self._copying_fixed_costs["F(ACQ)c"] <= F <= self._copying_fixed_costs["F(ACQ)s"] and A < self._assets["A-s"]: 1251 result.update({"entrant": self.ENTRANT_CHOICES["complement"]}) 1252 result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) 1253 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 1254 result.update({"acquisition": self.ACQUISITION_OUTCOME["apart"]}) 1255 elif F < self._copying_fixed_costs["F(ACQ)c"] and A < self._assets["A-s"]: 1256 # to develop a substitute is the weakly dominant strategy of the entrant 1257 entrant_choice_area_1: Literal["substitute", "complement"] = "substitute" 1258 # if the payoff for a complement is higher than for a substitute, the entrant will choose the complement. 1259 if self._delta < self._small_delta: 1260 entrant_choice_area_1 = "complement" 1261 result.update({"entrant": self.ENTRANT_CHOICES[entrant_choice_area_1]}) 1262 result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) 1263 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 1264 result.update({"acquisition": self.ACQUISITION_OUTCOME["merged"]}) 1265 else: 1266 result.update({"entrant": self.ENTRANT_CHOICES["substitute"]}) 1267 result.update({"development": self.DEVELOPMENT_OUTCOME["success"]}) 1268 result.update({"acquisition": self.ACQUISITION_OUTCOME["merged"]}) 1269 if F <= self._copying_fixed_costs["F(YY)c"]: 1270 result.update({"incumbent": self.INCUMBENT_CHOICES["copy"]}) 1271 else: 1272 result.update({"incumbent": self.INCUMBENT_CHOICES["refrain"]}) 1273 return result
Returns 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)
- "acquisition": outcome of the acquisition (possible outcomes listed in Shelegia_Motta_2021.Models.AcquisitionModel.ACQUISITION_OUTCOME)
To understand the details of the logic implemented, consult the chapter in Shelegia and Motta (2021) corresponding to the model.
Parameters
- A (float): Assets of the entrant.
- F (float): Fixed costs for copying of the incumbent.
Returns
- Dict[str, str]: Optimal choice of the entrant, the incumbent and the outcome of the development.