{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "3cdc1b46", "metadata": {}, "outputs": [], "source": [ "# Cost function modelling the University of the Caribbean Lands scenario\n", "#\n", "# Parameters (beside the debug flag, which is boolean), passed as a list:\n", "# * x[0], fraction of the investment to go to School of Agronomy (A)\n", "# * x[1], fraction of the investment to go to School of Business (B)\n", "# * x[2], fraction of the investment to go to School of Computing (C)\n", "#\n", "# Contract/precondition: All x[i] >= 0, sum over all x[i] <= 1.\n", "# The remainder is understood to go into Dormitories (D).\n", "#\n", "# The function returns a list with the predicted outcome for the three minimization objectives:\n", "# * first element y_0: Decrease in research strength within five years,\n", "# quantified by the annual number of citations to papers from the university\n", "# * second element y_1: Others' share in the real-estate tenancy business (1 - our share),\n", "# quantified in terms of the fraction of the overall the number of tenants in Pt. Reston\n", "# * third element y_2: Number of votes against the investment plan in the Grand Council.\n", "# The Grand Council is assumed to have 60 seats.\n", "#\n", "# Remark: We intentionally return fractional numbers of seats in the Grand Council, i.e.,\n", "# that criterion is treated as a continuous rather than a discrete quantity.\n", "# The same goes for the number of citations to papers.\n", "#\n", "# Justification:\n", "# a) Numerical optimization; it is easier that way for opt.minimize() to walk toward minima.\n", "# b) There is an actual sense in which these values should be floating-point numbers.\n", "# We are here returning the *expected* outcome for these quantities.\n", "# This technically would be a weighted average over probabilities for random variables.\n", "# We are here developing a simple qualitative model, where these random variables are not\n", "# included explicitly, but both research success and votes do indeed depend on chance.\n", "#\n", "def caribbean_cost_function(x, debug_output):\n", " # copy x into a list containing investment fractions\n", " # if the constraints are violated, all are assumed to be zero\n", " #\n", " # note that technically it is not our duty to ensure that the preconditions are met;\n", " # nonetheless, the scalar optimizer from scipy might not adhere to the contract\n", " #\n", " if x[0] >= 0 and x[1] >= 0 and x[2] >= 0 and (x[0] + x[1] + x[2]) <= 1:\n", " investment_fraction = [x[0], x[1], x[2], 1 - x[0] - x[1] - x[2]]\n", " else:\n", " investment_fraction = [0, 0, 0, 0]\n", " \n", " # compute the predicted improved research strength for each school,\n", " # and from these data, compute the prediction for y_0\n", " # model: square root of research strength responds linearly to investment\n", " #\n", " present_sqrt_res_strength = [present_research_strength[i]**0.5 for i in range(3)]\n", " future_sqrt_res_strength = [present_sqrt_res_strength[i] + \\\n", " research_response_coefficient*investment_fraction[i] \\\n", " for i in range(3)]\n", " future_research_strength = [future_sqrt_res_strength[i]*future_sqrt_res_strength[i] \\\n", " for i in range(3)]\n", " research_decrease = 0\n", " for i in range(3):\n", " research_decrease += present_research_strength[i] - future_research_strength[i]\n", " \n", " # compute the prediction for the tenancy metric y_1\n", " # model: it responds linearly to investment\n", " #\n", " others_tenancy_share = others_tenancy_share_now + tenancy_response_coefficient*investment_fraction[3]\n", " \n", " # now predict the vote on the Grand Council:\n", " # we assume that the council is split into interest groups concerning A, B, C, D\n", " #\n", " # each group responds linearly to investment, for a range\n", " # between minimal expectation (all against) and maximal expectation (all in favour);\n", " # outside this range, the respective group is all against or all in favour of the proposal\n", " #\n", " votes_against = 0\n", " for i in range(4):\n", " if min_expectation[i] >= investment_fraction[i]:\n", " votes_against += council_presence[i]\n", " elif max_expectation[i] >= investment_fraction[i]:\n", " fraction_in_favour = (investment_fraction[i] - \\\n", " min_expectation[i]) / (max_expectation[i] - min_expectation[i])\n", " votes_against += (1 - fraction_in_favour) * council_presence[i]\n", " \n", " # return the outcome, including debugging output if requested\n", " #\n", " y = [research_decrease, others_tenancy_share, votes_against]\n", " \n", " if debug_output:\n", " print(\"investment fraction:\")\n", " for i in range(len(investment_fraction)):\n", " print(\"\\tinvestment_fraction[\", i, \"]\\t=\\t\", round(100*investment_fraction[i], 3), \" %\", sep=\"\")\n", " \n", " print(\"\\npredicted research strength:\")\n", " for i in range(len(future_research_strength)):\n", " print(\"\\tfuture_research_strength[\", i, \"]\\t=\\t\", \\\n", " round(future_research_strength[i], 1), sep=\"\", end=\"\")\n", " print(\"\\t(now:\\t\", present_research_strength[i], \")\", sep=\"\")\n", " \n", " council_size = 0\n", " for n in council_presence:\n", " council_size += n\n", " print(\"\\npredicted vote in the Grand Council:\\n\\tFor:\", \\\n", " round(council_size - votes_against, 2), \"\\n\\tAgainst:\", round(votes_against, 2))\n", " \n", " \n", " print(\"\\nobjectives:\")\n", " for i in range(len(y)):\n", " print(\"\\ty[\", i, \"]\\t=\\t\", round(y[i], 5), sep=\"\")\n", " return y\n", "\n", "\n", "# constant coefficients used in the model cost function\n", "#\n", "present_research_strength = [20000, 5000, 2000] # present research strength of the three schools\n", "research_response_coefficient = 30 # linear response of sqrt(research strength) to investment\n", "others_tenancy_share_now = 0.993 # at present, 99.3% of tenants in Pt. Reston do not rent from us\n", "tenancy_response_coefficient = -0.007 # our share might be doubled if we invest 100% in this\n", "council_presence = [12, 9, 15, 24] # seats of each of the four factions on the Grand Council\n", "min_expectation = [0.32, 0.08, 0.02, 0.04] # how much needs to be invested until some vote for the plan\n", "max_expectation = [0.64, 0.12, 0.2, 0.64] # how much needs to be invested so that all vote for the plan" ] }, { "cell_type": "code", "execution_count": null, "id": "e36134e7", "metadata": {}, "outputs": [], "source": [ "import random\n", "\n", "# returns a random point in parameter space\n", "#\n", "def random_parameters():\n", " unnormalized = [random.random() for i in range(4)]\n", " u_sum = 0.0\n", " for u in unnormalized: # rescale to unity, since sum of investment fractions is 100%\n", " u_sum += u\n", " x = [unnormalized[i] / u_sum for i in range(3)] # we only include the first three fractions in the list\n", " return x" ] }, { "cell_type": "code", "execution_count": null, "id": "c34f9ea4", "metadata": {}, "outputs": [], "source": [ "# Validation:\n", "#\n", "# We run the cost function seven times with random input\n", "# to check whether it is viable as a rough qualitative model of the scenario\n", "#\n", "tests = 7\n", "for i in range(tests):\n", " print(\"\\n==\\nTest\", i, end = \"\\n\\n\")\n", " caribbean_cost_function(random_parameters(), True)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7" } }, "nbformat": 4, "nbformat_minor": 5 }