diff --git a/rota.ipynb b/rota.ipynb index 9bfe7be..b0de0b6 100644 --- a/rota.ipynb +++ b/rota.ipynb @@ -34,8 +34,21 @@ "from shifts import SingleShift, ShiftCollection, days, sites\n", "from workers import Worker\n", "\n", + "import math\n", + "\n", + "import scipy.stats as stats\n", + "\n", + "import csv\n", + "\n", + "import statistics\n", + "\n", "import datetime\n", - "import itertools" + "import itertools\n", + "import operator\n", + "\n", + "from functools import reduce\n", + "def prod(iterable):\n", + " return reduce(operator.mul, iterable, 1)" ] }, { @@ -44,10 +57,10 @@ "metadata": {}, "outputs": [], "source": [ - "night_balance_offset = 2\n", - "twilight_balance_offset = 1\n", - "weeks_to_rota = 8\n", - "use_test_workers = False" + "#night_balance_offset = 2\n", + "#twilight_balance_offset = 1\n", + "weeks_to_rota = 12\n", + "use_test_workers = True" ] }, { @@ -73,9 +86,11 @@ " SingleShift((\"truro\",), \"weekend_truro\", 12.5, days[5:]), \n", " SingleShift((\"torquay\",), \"weekend_torquay\", 12.5, days[5:]), \n", " SingleShift((\"plymouth\",), \"weekend_plymouth\", 8, days[5:], workers_required=2), \n", - " SingleShift((sites), \"night\", 12.5, days, balance_by_site=False, workers_required=3, rota_on_nwds=True), \n", + " SingleShift((sites), \"night\", 12.5, days, balance_offset=2, balance_weighting=2, workers_required=3, rota_on_nwds=True), \n", ")\n", "\n", + "\n", + "\n", "# Enter shifts of each day\n", "#shifts = ['morning', 'evening', 'night', 'truro_twilight'] # 3 shifts of 8 hours\n", "#shifts = ['night', 'truro_twilight', 'plymouth_twilight'] # 3 shifts of 8 hours\n", @@ -102,7 +117,7 @@ "#rota_days_length = len(weeks) * 7\n", "#rota_end_date = start_date + datetime.timedelta(rota_days_length)\n", "\n", - "unavailable_to_work = set()\n", + "\n", "\n", "\n", "#Shifts.GetAllDayShiftsAsClass()\n", @@ -115,34 +130,17 @@ "cell_type": "code", "execution_count": 4, "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": "['Wijhdan Abusrewil', 'Exeter', 'ST2', '100', '', '', '', '']\n['Seren Peters', 'Plymouth', 'ST2', '100', '', '', '', '']\n['Hannah Lewis', 'Plymouth', 'ST2', '100', '', '', '', '']\n['Christian Greer', 'Torquay', 'ST2', '100', '', '', '', '']\n['Nicolas Dziad', 'Truro', 'ST2', '100', '', '', '', '']\n['Michal Tomek', 'Exeter', 'ST2', '100', '', '', '', '']\n['Alex Wood', 'Exeter', 'ST2', '100', '', '', '', '']\n['Alexander Sanchez- Cabello', 'Plymouth', 'ST2', '100', '', '', '', '']\n['Max Ireland', 'Torquay', 'ST2', '100', '', '', '', '']\n['Vilim Kalamar', 'Truro', 'ST2', '100', '', '', '', '']\n['Salma Aslam', 'Exeter', 'ST3', '0', '', '', '', 'IDT']\n['Adeola Omotade', 'Plymouth', 'ST3', '0', '', '', '', 'IDT']\n['Amina Odeh', 'Plymouth', 'ST3', '100', '', '', '', '']\n['Charles Finan', 'Torquay', 'ST3', '100', '', '', '', '']\n['Alan Eccles', 'Torquay', 'ST3', '80', '', '', '', '']\n['Sarath Vennam', 'Truro', 'ST3', '100', '', '', '', '']\n['Paul Ward', 'Truro', 'ST3', '100', '', '', '', '']\n['Sayed Alqarooni', 'Exeter', 'ST3', '100', '', '', '', '']\n['Jean Sukumar', 'Plymouth', 'ST3', '60', '', '', '', '']\n['Jenn Fong', 'Plymouth', 'ST3', '100', '', '', '', '']\n['Chong Ng', 'Torquay', 'ST3', '100', '', '', '', '']\n['Matthew Thorley', 'Torquay', 'ST3', '100', '', '', '', '']\n['Jenna Millington', 'Truro', 'ST3', '60', '', '', '', '']\n['Chi Leung', 'Truro', 'ST3', '100', '', '', '', '']\n['Tom Welsh', 'Exeter', 'ST4', '100', '', '', '', '']\n['Rebecca Murphy', 'Exeter', 'ST4', '100', '', '', '', '']\n['Delilah Trimmer', 'Exeter', 'ST4', '50', '', '', '', 'Back Nov 20']\n['Sophie McGlade', 'Plymouth', 'ST4', '100', '', '', '', '']\n['S Ghauri', 'Plymouth', 'ST4', '100', '', '', '', '']\n['Khalil Madbak', 'Plymouth', 'ST4', '100', '', '', '', '']\n['Natasha Hougham', 'Plymouth', 'ST4', '60', '', '', '', '']\n['Richard Chaytor', 'Plymouth', 'ST4', '100', '', '', '', '']\n['Safi Rahman', 'Plymouth', 'ST4', '0', '', '', '', 'IDT']\n['George Edwards', 'Plymouth', 'ST4', '100', '', '', '', '']\n['Neil McIntyre', 'Torquay', 'ST4', '100', '', '', '', '']\n['Mo Babsail', 'Torquay', 'ST4', '100', '', '', '', '']\n['Paul Jenkins', 'Torquay', 'ST4', '100', '', '', '', '']\n['Ross Kruger', 'Truro', 'ST4', '100', '', '', '', '']\n['Amoolya Mannava', 'Truro', 'ST4', '100', '', '', '', '']\n['Tom Davies', 'Exeter', 'ST5', '42', '', '', '2020/09/01-2021/04/12', '']\n" - }, - { - "output_type": "error", - "ename": "NameError", - "evalue": "name 'unavailable_to_work' is not defined", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 30\u001b[0m \u001b[0moop\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0moop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"-\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0moop\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 31\u001b[0m \u001b[0;31m#print(nwds, end_date, oop)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 32\u001b[0;31m \u001b[0mworkers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mWorker\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mShifts\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msite\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrade\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfte\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnwds\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mend_date\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moop\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 33\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 34\u001b[0m \u001b[0mworkers\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msorted\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mworkers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/scripts/rota/workers.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, Shifts, id, name, site, grade, fte, nwd, end_date, oop)\u001b[0m\n\u001b[1;32m 46\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mweeks_days\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mShifts\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mweeks_days_product\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mdays_until_oop\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0mdays_until_oop\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0moop_length\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 47\u001b[0m \u001b[0mweek\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mday\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mweeks_days\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 48\u001b[0;31m \u001b[0munavailable_to_work\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mweek\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mday\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 49\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 50\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mNameError\u001b[0m: name 'unavailable_to_work' is not defined" - ] - } - ], + "outputs": [], "source": [ "workers = []\n", "\n", "if use_test_workers:\n", " # Enter workers ids (name, number, ...)\n", - " workers.extend([Worker(Shifts, i, \"Test Person {}\".format(i), \"truro\", 2, nwd=['Fri', \"Sat\", \"Sun\"]) for i in range(1, 2)])\n", - " workers.extend([Worker(Shifts,i, \"Test Person {}\".format(i), \"truro\", 2, nwd=[\"Sat\", \"Sun\"]) for i in range(2, 10)])\n", - " workers.extend([Worker(Shifts,i, \"Test Person {}\".format(i), \"plymouth\", 4) for i in range(10, 20)])\n", - " workers.extend([Worker(Shifts,i, \"Test Person {}\".format(i), \"exeter\", 4) for i in range(20, 30)])\n", + " #workers.extend([Worker(Shifts, i, \"Test Person {}\".format(i), \"truro\", 3, nwd=['Fri', \"Sat\", \"Sun\"]) for i in range(1, 2)])\n", + " workers.extend([Worker(Shifts,i, \"Test Person {}\".format(i), \"truro\", 4, ) for i in range(1, 15)])\n", + " workers.extend([Worker(Shifts,i, \"Test Person {}\".format(i), \"plymouth\", 4) for i in range(15, 30)])\n", + " workers.extend([Worker(Shifts,i, \"Test Person {}\".format(i), \"exeter\", 4) for i in range(30, 45)])\n", + " workers.extend([Worker(Shifts,i, \"Test Person {}\".format(i), \"torquay\", 4) for i in range(45, 60)])\n", " #workers.extend([workerClass(i, \"Test Person {}\".format(i), 4, 200, \"\") for i in range(48, 49)])\n", "else:\n", " # Import trainee data\n", @@ -155,7 +153,7 @@ " n = 0\n", " for row in reader:\n", " n = n + 1\n", - " print(row)\n", + " #print(row)\n", " name, site, grade, fte, nwd, end_date, oop, extra = row\n", "\n", " # Ignore trainees if fte == 0 (what are they doing here anyway)\n", @@ -177,6 +175,16 @@ "execution_count": 5, "metadata": {}, "outputs": [], + "source": [ + "#[workers.site for workers in workers]\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], "source": [ "full_time_equivalent = sum([w.fte_adj for w in workers])\n", "full_time_equivalent_sites = {}\n", @@ -188,7 +196,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -199,7 +207,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": { "tags": [ "outputPrepend", @@ -221,11 +229,19 @@ "model.nights = Var(((worker.id, week, block) for worker in workers for week in Shifts.weeks for block in night_blocks),\n", " within=Binary, initialize=0)\n", "\n", + "model.shift_count = Var(((worker.id, shift) for worker in workers for shift in Shifts.GetShiftNames()), domain=NonNegativeReals, initialize=0)\n", + "\n", + "#model.shift_count_t1 = Var(((worker.id, shift) for worker in workers for shift in Shifts.GetShiftNames()), domain=NonNegativeReals, initialize=0)\n", + "#model.shift_count_t2 = Var(((worker.id, shift) for worker in workers for shift in Shifts.GetShiftNames()), domain=NonNegativeReals, initialize=0)\n", + "model.shift_count_t1 = Var(((worker.id) for worker in workers), domain=NonNegativeReals, initialize=0)\n", + "model.shift_count_t2 = Var(((worker.id) for worker in workers), domain=NonNegativeReals, initialize=0)\n", + "\n", + "model.shift_count_t3 = Var((worker.id for worker in workers), initialize=0)\n", "#model.unavailable = Var(((worker, week, day) for worker in workers for week in weeks for day in days),\n", "# within=Binary, initialize=0)\n", "\n", "def availability_init(model, wid, week, day):\n", - " if (wid, week, day) in unavailable_to_work:\n", + " if (wid, week, day) in Shifts.unavailable_to_work:\n", " #print((wid, week, day))\n", " return 0\n", " return 1\n", @@ -240,27 +256,56 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": {}, + "execution_count": 9, + "metadata": { + "tags": [ + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend" + ] + }, "outputs": [], "source": [ - "# Define an objective function with model as input, to pass later\n", - "def obj_rule(m):\n", - " c = len(workers)\n", - " return 1\n", - " #return sum(m.no_pref[worker.id] for worker in workers)\n", - " #return sum(m.no_pref[worker] for worker in workers) + sum(c * m.needed[worker] for worker in workers)\n", - "# we multiply the second term by a constant to make sure that it is the primary objective\n", - "# since sum(m.no_prefer) is at most len(workers), len(workers) + 1 is a valid constant.\n", + "#for shift in Shifts.shifts:\n", + "# s = []\n", + "# for worker in workers:\n", + "# if worker.site in shift.site:\n", + "# s.append(abs(sum(model.works[worker.id, week, day, shift.name].value for week, day in Shifts.GetAllWeeksDays()) - worker.shift_target_number[shift.name]))\n", + " #f n > 0:\n", + " # s.append(abs(n - worker.shift_target_number[shift.name]))\n", "\n", - "\n", - "# add objective function to the model. rule (pass function) or expr (pass expression directly)\n", - "model.obj = Objective(rule=obj_rule, sense=minimize)" + "# print(shift.name,sum(s))" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "#sum(abs(worker.shift_target_number[shift.name] - sum(model.works[worker.id, week, day, shift.name] for week, day in Shifts.GetAllWeeksDays())) for worker in workers for shift in Shifts.shifts)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -274,8 +319,12 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": {}, + "execution_count": 12, + "metadata": { + "tags": [ + "outputPrepend" + ] + }, "outputs": [], "source": [ "model.constraints = ConstraintList() # Create a set of constraints\n", @@ -289,12 +338,13 @@ "for week, day, shift in Shifts.GetNotRequiredShifts():\n", " model.constraints.add(\n", " 0 == sum(model.works[worker.id, week, day, shift.name] for worker in workers)\n", - " )" + " )\n", + "" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": { "tags": [ "outputPrepend", @@ -399,28 +449,8 @@ "\n", "#model.nwd_constraint = Constraint([(worker) for worker in workers if worker.nwd], rule=nwdRule)\n", "\n", - "for worker in workers:\n", - " if worker.nwd:\n", - " for week, day, shift in Shifts.GetAllShiftsAsClass():\n", - " if not shift.rota_on_nwds and day in worker.nwd:\n", - " model.constraints.add(\n", - " 0 == model.works[worker.id, week, day, shift.name]\n", - " )\n", - "\n", - "# set a rule to even night shifts (could also be done in blocks, which may be quicker?)\n", - "# takes into accound both LTFT, OOP and leaving the training scheme\n", - "total_night_shifts = Shifts.rota_days_length * Shifts.GetShiftByName(\"night\").workers_required\n", - "\n", - "def nightShiftNumberRule(model, wid, fte_adj):\n", - " \n", - " max_shifts = total_night_shifts / full_time_equivalent * fte_adj + night_balance_offset\n", - " min_shifts = total_night_shifts / full_time_equivalent * fte_adj - night_balance_offset\n", - "\n", - " return inequality(min_shifts, sum(model.works[wid, week, day, \"night\"] for week, day in Shifts.GetAllWeeksDays()), max_shifts)\n", - "model.night_shifts_constraint = Constraint([(worker.id, worker.fte_adj) for worker in workers], rule=nightShiftNumberRule)\n", "\n", "\n", - "twilight_constraints = {}\n", "\n", "# Set a rule to even twilight shifts:\n", "#total_twilight_shifts = len(weeks) * 5 * 1 # One person per site\n", @@ -480,15 +510,9 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 14, "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": "4.076923076923077 2.076923076923077\n2.230769230769231 0.23076923076923084\n2.7857142857142856 0.7857142857142856\n2.428571428571429 0.4285714285714286\n2.7857142857142856 0.7857142857142856\n2.428571428571429 0.4285714285714286\n3.272727272727273 1.272727272727273\n1.9090909090909092 -0.09090909090909094\n3.631578947368421 1.6315789473684208\n2.052631578947368 0.05263157894736836\n4.076923076923077 2.076923076923077\n2.230769230769231 0.23076923076923084\n4.076923076923077 2.076923076923077\n2.230769230769231 0.23076923076923084\n2.7857142857142856 0.7857142857142856\n2.428571428571429 0.4285714285714286\n3.272727272727273 1.272727272727273\n1.9090909090909092 -0.09090909090909094\n3.631578947368421 1.6315789473684208\n2.052631578947368 0.05263157894736836\n1.0 -1.0\n1.0 -1.0\n1.0 -1.0\n1.0 -1.0\n2.7857142857142856 0.7857142857142856\n2.428571428571429 0.4285714285714286\n3.272727272727273 1.272727272727273\n1.9090909090909092 -0.09090909090909094\n2.8181818181818183 0.8181818181818183\n1.7272727272727273 -0.2727272727272727\n3.631578947368421 1.6315789473684208\n2.052631578947368 0.05263157894736836\n3.631578947368421 1.6315789473684208\n2.052631578947368 0.05263157894736836\n4.076923076923077 2.076923076923077\n2.230769230769231 0.23076923076923084\n2.071428571428571 0.0714285714285714\n1.8571428571428572 -0.1428571428571429\n2.7857142857142856 0.7857142857142856\n2.428571428571429 0.4285714285714286\n3.272727272727273 1.272727272727273\n1.9090909090909092 -0.09090909090909094\n3.272727272727273 1.272727272727273\n1.9090909090909092 -0.09090909090909094\n2.5789473684210527 0.5789473684210527\n1.631578947368421 -0.368421052631579\n3.631578947368421 1.6315789473684208\n2.052631578947368 0.05263157894736836\n4.076923076923077 2.076923076923077\n2.230769230769231 0.23076923076923084\n4.076923076923077 2.076923076923077\n2.230769230769231 0.23076923076923084\n2.5384615384615383 0.5384615384615385\n1.6153846153846154 -0.3846153846153846\n2.7857142857142856 0.7857142857142856\n2.428571428571429 0.4285714285714286\n2.7857142857142856 0.7857142857142856\n2.428571428571429 0.4285714285714286\n2.7857142857142856 0.7857142857142856\n2.428571428571429 0.4285714285714286\n2.071428571428571 0.0714285714285714\n1.8571428571428572 -0.1428571428571429\n2.7857142857142856 0.7857142857142856\n2.428571428571429 0.4285714285714286\n1.0 -1.0\n1.0 -1.0\n2.7857142857142856 0.7857142857142856\n2.428571428571429 0.4285714285714286\n3.272727272727273 1.272727272727273\n1.9090909090909092 -0.09090909090909094\n3.272727272727273 1.272727272727273\n1.9090909090909092 -0.09090909090909094\n3.272727272727273 1.272727272727273\n1.9090909090909092 -0.09090909090909094\n3.631578947368421 1.6315789473684208\n2.052631578947368 0.05263157894736836\n3.631578947368421 1.6315789473684208\n2.052631578947368 0.05263157894736836\n" - } - ], + "outputs": [], "source": [ "def balanceSiteShiftNumberRule(model, wid, fte_adj, shiftClass):\n", " # TODO: make helper to retrieve\n", @@ -499,72 +523,90 @@ "\n", " max_shifts = total_shifts / full_time_equivalent_joined * fte_adj + twilight_balance_offset\n", " min_shifts = total_shifts / full_time_equivalent_joined * fte_adj - twilight_balance_offset\n", - " print(max_shifts, min_shifts)\n", "\n", " return inequality(min_shifts, sum(model.works[wid, week, day, shiftClass.name] for week, day in Shifts.GetAllWeeksDays()), max_shifts)\n", "\n", "#model.balance_site_shift_constraint = Constraint([(worker.id, worker.fte_adj, shift) for shift in Shifts.GetShiftOptions() for worker in workers if worker.site in shift.site and shift.balance_by_site], rule=balanceSiteShiftNumberRule)\n", "\n", + "# set a rule to even night shifts (could also be done in blocks, which may be quicker?)\n", + "# takes into accound both LTFT, OOP and leaving the training scheme\n", + "#total_night_shifts = Shifts.rota_days_length * Shifts.GetShiftByName(\"night\").workers_required\n", + "\n", + "def nightShiftNumberRule(model, wid, fte_adj): # NOTE no longer used - see generic function below\n", + " target_shifts = total_night_shifts / full_time_equivalent * fte_adj\n", + " max_shifts = target_shifts + night_balance_offset\n", + " min_shifts = target_shifts - night_balance_offset\n", + "\n", + " return inequality(min_shifts, sum(model.works[wid, week, day, \"night\"] for week, day in Shifts.GetAllWeeksDays()), max_shifts)\n", + "#model.night_shifts_constraint = Constraint([(worker.id, worker.fte_adj) for worker in workers], rule=nightShiftNumberRule)\n", + "\n", + "\n", + "#twilight_constraints = {}\n", + "\n", + "# Balance shifts\n", "for worker in workers:\n", " for shift in Shifts.GetShiftOptions():\n", - " if worker.site in shift.site and shift.balance_by_site:\n", - " total_shifts = len(Shifts.weeks) * len(shift.shift_days) * shift.workers_required # One person per site\n", + " if worker.site in shift.site: # Each site specfies which sites workers can fullfill it\n", "\n", + " #total_shifts = Shifts.rota_days_length * shift.workers_required # One person per site\n", + " total_shifts = len(Shifts.weeks) * len(shift.shift_days) * shift.workers_required\n", + "\n", + " #if shift.balance_by_site:\n", " full_time_equivalent_joined = sum([full_time_equivalent_sites[i] for i in shift.site])\n", "\n", + " target_shifts = total_shifts / full_time_equivalent_joined * worker.fte_adj\n", "\n", - " max_shifts = total_shifts / full_time_equivalent_joined * worker.fte_adj + twilight_balance_offset\n", - " min_shifts = total_shifts / full_time_equivalent_joined * worker.fte_adj - twilight_balance_offset\n", - " print(max_shifts, min_shifts)\n", + " worker.shift_target_number[shift.name] = target_shifts\n", + "\n", + " max_shifts = target_shifts + shift.balance_offset\n", + " min_shifts = target_shifts - shift.balance_offset\n", + " #print(max_shifts, min_shifts)\n", " \n", " model.constraints.add(\n", - " #min_shifts <= sum(model.works[worker.id, week, day, shift.name] for week, day in Shifts.GetAllWeeksDays()) <= max_shifts\n", " inequality(min_shifts, sum(model.works[worker.id, week, day, shift.name] for week, day in Shifts.GetAllWeeksDays()), max_shifts)\n", " )\n", " #return inequality(min_shifts, sum(model.works[wid, week, day, shiftClass.name] for week, day in Shifts.GetAllWeeksDays()), max_shifts)" ] }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": "['exeter_twilight',\n 'truro_twilight',\n 'torquay_twilight',\n 'plymouth_twilight',\n 'weekend_exeter',\n 'weekend_truro',\n 'weekend_torquay',\n 'weekend_plymouth',\n 'night']" - }, - "metadata": {}, - "execution_count": 13 - } - ], - "source": [ - "[s.name for s in Shifts.GetShiftOptions() ]" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": "[1, 6, 7, 11, 18, 25, 26, 27]" - }, - "metadata": {}, - "execution_count": 14 - } - ], - "source": [ - "#[[week, day, shift.name] for week, day, shift in Shifts.GetAllShiftsAsClass() if (not shift.rota_on_nwds and day in [\"Fri\", \"Wed\"])]\n", - "[worker.id for worker in workers if worker.site in (\"exeter\")]\n", - "" - ] - }, { "cell_type": "code", "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "for worker in workers:\n", + " # Define shift_count_t1 and shift_count_t2 constraints for the object\n", + " # This bypassing the need for a quadratic solver\n", + " # t1-t2 is the target\n", + " # As the objective is to minimise t1+t2 and t1 and t2 are positive reals \n", + " # t1+t2 approximates the absolute target (which otherwise requires a quadratic solver)\n", + " model.constraints.add(\n", + " #model.shift_count_t3[worker.id] == abs(sum(model.shift_count[worker.id, shift.name] - worker.shift_target_number[shift.name] for shift in Shifts.GetShiftOptions()))\n", + " model.shift_count_t1[worker.id]-model.shift_count_t2[worker.id] == sum((model.shift_count[worker.id, shift.name] - worker.shift_target_number[shift.name]) * shift.balance_weighting for shift in Shifts.GetShiftOptions())\n", + " )\n", + " for shift in Shifts.GetShiftOptions():\n", + " model.constraints.add(\n", + " model.shift_count[worker.id, shift.name] == sum(model.works[worker.id, week, day, shift.name] for week, day in Shifts.GetAllWeeksDays())\n", + " )\n", + " #model.constraints.add( \n", + " # #model.shift_count_t1[worker.id, shift.name]-model.shift_count_t2[worker.id, shift.name] == model.shift_count[worker.id, shift.name]\n", + " #model.shift_count_t1[worker.id, shift.name]-model.shift_count_t2[worker.id, shift.name] == model.shift_count[worker.id, shift.name] - worker.shift_target_number[shift.name]\n", + " #model.shift_count_t3[worker.id] == sum(model.shift_count[worker.id, shift.name] - worker.shift_target_number[shift.name] for shift in Shifts.GetShiftNames())\n", + " #)\n", + "\n", + "\n", + "\n", + " if worker.nwd:\n", + " for week, day, shift in Shifts.GetAllShiftsAsClass():\n", + " if not shift.rota_on_nwds and day in worker.nwd:\n", + " model.constraints.add(\n", + " 0 == model.works[worker.id, week, day, shift.name]\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 16, "metadata": { "tags": [ "outputPrepend", @@ -700,36 +742,2231 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "#print(model.works[18,6, \"Fri\", \"night\"].value)\n", "#print(model.works[18,7, \"Mon\", \"truro_twilight\"].value * 0)\n", - "#Shifts.GetShiftNamesByDay(\"Mon\")" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": "WARNING: Constant objective detected, replacing with a placeholder to prevent\n solver failure.\nWARNING: Loading a SolverResults object with a warning status into\n model=unknown;\n message from solver=\n" - } - ], - "source": [ - "opt = SolverFactory('cbc') # choose a solver\n", - "results = opt.solve(model) # solve the model with the selected solver" + "#Shifts.GetShiftNamesByDay(\"Mon\")\n", + "#[[worker.id, shift, worker.shift_target_number[shift] - model.shift_count[(worker.id, shift)].value] for shift in worker.shift_target_number for worker in workers]" ] }, { "cell_type": "code", "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "#[[worker.shift_target_number[1] - model.shift_count[(worker.id, 1)].value for worker in workers]" + ] + }, + { + "cell_type": "code", + "execution_count": 19, "metadata": { "tags": [ + "outputPrepend" + ] + }, + "outputs": [], + "source": [ + "# Define an objective function with model as input, to pass later\n", + "def obj_rule(m):\n", + " c = len(workers)\n", + "\n", + "\n", + " #v = []\n", + " #for shift in Shifts.shifts:\n", + " # s = []\n", + " # for worker in workers:\n", + " # if worker.site in shift.site:\n", + " # s.append(abs(sum(model.works[worker.id, week, day, shift.name] for week, day in Shifts.GetAllWeeksDays()) - worker.shift_target_number[shift.name]))\n", + " # v.append(sum(s))\n", + "\n", + "\n", + " # To balance shifts as much as possible we work with the Coefficient of variation of \n", + " # differences between the target number of shifts and the given number of shifts\n", + " # stats.variation()\n", + " #return 1\n", + " #return sum(prod((model.shift_count_t1[(worker.id, shift)] + model.shift_count_t2[(worker.id, shift)]) for shift in Shifts.GetShiftNames()) for worker in workers)\n", + " return sum(model.shift_count_t1[(worker.id)] + model.shift_count_t2[(worker.id)] for worker in workers)\n", + " #return sum(model.shift_count_t3[worker.id] for worker in workers)\n", + " \n", + " #sum(abs(worker.shift_target_number - sum(model.works[worker.id, week, day, shift.name] for week, day in Shifts.GetAllWeeksDays())) for worker in workess for shift in Shifts.shifts)\n", + "\n", + "\n", + " #return sum(abs(worker.shift_target_number[shift.name] - sum(model.works[worker.id, week, day, shift.name] for week, day in Shifts.GetAllWeeksDays())) for worker in workers for shift in Shifts.shifts)\n", + " #return sum(m.no_pref[worker.id] for worker in workers)\n", + " #return sum(m.no_pref[worker] for worker in workers) + sum(c * m.needed[worker] for worker in workers)\n", + "# we multiply the second term by a constant to make sure that it is the primary objective\n", + "# since sum(m.no_prefer) is at most len(workers), len(workers) + 1 is a valid constant.\n", + "\n", + "\n", + "# add objective function to the model. rule (pass function) or expr (pass expression directly)\n", + "model.obj = Objective(rule=obj_rule, sense=minimize)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "tags": [ + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", + "outputPrepend", "outputPrepend", "outputPrepend" ] @@ -738,9 +2975,44 @@ { "output_type": "stream", "name": "stdout", - "text": "1 exeter_twilight\n1 truro_twilight\n1 torquay_twilight\n1 plymouth_twilight\n1 weekend_exeter\n1 weekend_truro\n1 weekend_torquay\n1 weekend_plymouth\n1 night\n2 exeter_twilight\n2 truro_twilight\n2 torquay_twilight\n2 plymouth_twilight\n2 weekend_exeter\n2 weekend_truro\n2 weekend_torquay\n2 weekend_plymouth\n2 night\n3 exeter_twilight\n3 truro_twilight\n3 torquay_twilight\n3 plymouth_twilight\n3 weekend_exeter\n3 weekend_truro\n3 weekend_torquay\n3 weekend_plymouth\n3 night\n4 exeter_twilight\n4 truro_twilight\n4 torquay_twilight\n4 plymouth_twilight\n4 weekend_exeter\n4 weekend_truro\n4 weekend_torquay\n4 weekend_plymouth\n4 night\n5 exeter_twilight\n5 truro_twilight\n5 torquay_twilight\n5 plymouth_twilight\n5 weekend_exeter\n5 weekend_truro\n5 weekend_torquay\n5 weekend_plymouth\n5 night\n6 exeter_twilight\n6 truro_twilight\n6 torquay_twilight\n6 plymouth_twilight\n6 weekend_exeter\n6 weekend_truro\n6 weekend_torquay\n6 weekend_plymouth\n6 night\n7 exeter_twilight\n7 truro_twilight\n7 torquay_twilight\n7 plymouth_twilight\n7 weekend_exeter\n7 weekend_truro\n7 weekend_torquay\n7 weekend_plymouth\n7 night\n8 exeter_twilight\n8 truro_twilight\n8 torquay_twilight\n8 plymouth_twilight\n8 weekend_exeter\n8 weekend_truro\n8 weekend_torquay\n8 weekend_plymouth\n8 night\n9 exeter_twilight\n9 truro_twilight\n9 torquay_twilight\n9 plymouth_twilight\n9 weekend_exeter\n9 weekend_truro\n9 weekend_torquay\n9 weekend_plymouth\n9 night\n10 exeter_twilight\n10 truro_twilight\n10 torquay_twilight\n10 plymouth_twilight\n10 weekend_exeter\n10 weekend_truro\n10weekend_torquay\n10 weekend_plymouth\n10 night\n11 exeter_twilight\n11 truro_twilight\n11 torquay_twilight\n11 plymouth_twilight\n11 weekend_exeter\n11 weekend_truro\n11 weekend_torquay\n11 weekend_plymouth\n11 night\n12 exeter_twilight\n12 truro_twilight\n12 torquay_twilight\n12 plymouth_twilight\n12 weekend_exeter\n12 weekend_truro\n12 weekend_torquay\n12 weekend_plymouth\n12 night\n13 exeter_twilight\n13 truro_twilight\n13 torquay_twilight\n13 plymouth_twilight\n13 weekend_exeter\n13 weekend_truro\n13 weekend_torquay\n13 weekend_plymouth\n13 night\n14 exeter_twilight\n14 truro_twilight\n14 torquay_twilight\n14 plymouth_twilight\n14 weekend_exeter\n14 weekend_truro\n14 weekend_torquay\n14 weekend_plymouth\n14 night\n15 exeter_twilight\n15 truro_twilight\n15 torquay_twilight\n15 plymouth_twilight\n15 weekend_exeter\n15 weekend_truro\n15 weekend_torquay\n15 weekend_plymouth\n15 night\n16 exeter_twilight\n16 truro_twilight\n16 torquay_twilight\n16 plymouth_twilight\n16 weekend_exeter\n16 weekend_truro\n16 weekend_torquay\n16 weekend_plymouth\n16 night\n17 exeter_twilight\n17 truro_twilight\n17 torquay_twilight\n17 plymouth_twilight\n17 weekend_exeter\n17 weekend_truro\n17 weekend_torquay\n17 weekend_plymouth\n17 night\n18 exeter_twilight\n18 truro_twilight\n18 torquay_twilight\n18 plymouth_twilight\n18 weekend_exeter\n18 weekend_truro\n18 weekend_torquay\n18 weekend_plymouth\n18 night\n19 exeter_twilight\n19 truro_twilight\n19 torquay_twilight\n19 plymouth_twilight\n19 weekend_exeter\n19 weekend_truro\n19 weekend_torquay\n19 weekend_plymouth\n19 night\n20 exeter_twilight\n20 truro_twilight\n20 torquay_twilight\n20 plymouth_twilight\n20 weekend_exeter\n20 weekend_truro\n20 weekend_torquay\n20 weekend_plymouth\n20 night\n21 exeter_twilight\n21 truro_twilight\n21 torquay_twilight\n21 plymouth_twilight\n21 weekend_exeter\n21 weekend_truro\n21 weekend_torquay\n21 weekend_plymouth\n21 night\n22 exeter_twilight\n22 truro_twilight\n22 torquay_twilight\n22 plymouth_twilight\n22 weekend_exeter\n22 weekend_truro\n22 weekend_torquay\n22 weekend_plymouth\n22 night\n23 exeter_twilight\n23 truro_twilight\n23 torquay_twilight\n23 plymouth_twilight\n23 weekend_exeter\n23 weekend_truro\n23 weekend_torquay\n23 weekend_plymouth\n23 night\n24 exeter_twilight\n24 truro_twilight\n24 torquay_twilight\n24 plymouth_twilight\n24 weekend_exeter\n24 weekend_truro\n24 weekend_torquay\n24 weekend_plymouth\n24 night\n25 exeter_twilight\n25 truro_twilight\n25 torquay_twilight\n25 plymouth_twilight\n25 weekend_exeter\n25 weekend_truro\n25 weekend_torquay\n25 weekend_plymouth\n25 night\n26 exeter_twilight\n26 truro_twilight\n26 torquay_twilight\n26 plymouth_twilight\n26 weekend_exeter\n26 weekend_truro\n26 weekend_torquay\n26 weekend_plymouth\n26 night\n27 exeter_twilight\n27 truro_twilight\n27 torquay_twilight\n27 plymouth_twilight\n27 weekend_exeter\n27 weekend_truro\n27 weekend_torquay\n27 weekend_plymouth\n27 night\n28 exeter_twilight\n28 truro_twilight\n28 torquay_twilight\n28 plymouth_twilight\n28 weekend_exeter\n28 weekend_truro\n28 weekend_torquay\n28 weekend_plymouth\n28 night\n29 exeter_twilight\n29 truro_twilight\n29 torquay_twilight\n29 plymouth_twilight\n29 weekend_exeter\n29 weekend_truro\n29 weekend_torquay\n29 weekend_plymouth\n29 night\n30 exeter_twilight\n30 truro_twilight\n30 torquay_twilight\n30 plymouth_twilight\n30 weekend_exeter\n30 weekend_truro\n30 weekend_torquay\n30 weekend_plymouth\n30 night\n31 exeter_twilight\n31 truro_twilight\n31 torquay_twilight\n31 plymouth_twilight\n31 weekend_exeter\n31 weekend_truro\n31 weekend_torquay\n31 weekend_plymouth\n31 night\n32 exeter_twilight\n32 truro_twilight\n32 torquay_twilight\n32 plymouth_twilight\n32 weekend_exeter\n32 weekend_truro\n32 weekend_torquay\n32 weekend_plymouth\n32 night\n33 exeter_twilight\n33 truro_twilight\n33 torquay_twilight\n33 plymouth_twilight\n33 weekend_exeter\n33 weekend_truro\n33 weekend_torquay\n33 weekend_plymouth\n33 night\n34 exeter_twilight\n34 truro_twilight\n34 torquay_twilight\n34 plymouth_twilight\n34 weekend_exeter\n34 weekend_truro\n34 weekend_torquay\n34 weekend_plymouth\n34 night\n35 exeter_twilight\n35 truro_twilight\n35 torquay_twilight\n35 plymouth_twilight\n35 weekend_exeter\n35 weekend_truro\n35 weekend_torquay\n35 weekend_plymouth\n35 night\n36 exeter_twilight\n36 truro_twilight\n36 torquay_twilight\n36 plymouth_twilight\n36 weekend_exeter\n36 weekend_truro\n36 weekend_torquay\n36 weekend_plymouth\n36 night\n37 exeter_twilight\n37 truro_twilight\n37 torquay_twilight\n37 plymouth_twilight\n37 weekend_exeter\n37 weekend_truro\n37 weekend_torquay\n37 weekend_plymouth\n37 night\n38 exeter_twilight\n38 truro_twilight\n38 torquay_twilight\n38 plymouth_twilight\n38 weekend_exeter\n38 weekend_truro\n38 weekend_torquay\n38 weekend_plymouth\n38 night\n39 exeter_twilight\n39 truro_twilight\n39 torquay_twilight\n39 plymouth_twilight\n39 weekend_exeter\n39 weekend_truro\n39 weekend_torquay\n39 weekend_plymouth\n39 night\n" + "text": "Welcome to the CBC MILP Solver \nVersion: 2.9.9 \nBuild Date: Aug 7 2019 \n\ncommand line - /usr/bin/cbc -seconds 120 -printingOptions all -import /tmp/tmpyr1b9jcq.pyomo.lp -stat=1 -solve -solu /tmp/tmpyr1b9jcq.pyomo.soln (default strategy 1)\nseconds was changed from 1e+100 to 120\nOption for printingOptions changed from normal to all\nPresolve 14257 (-10891) rows, 26974 (-20404) columns and 180160 (-105283) elements\nStatistics for presolved model\nOriginal problem has 46728 integers (46728 of which binary)\nPresolved problem has 26856 integers (26856 of which binary)\n==== 26856 zero objective 2 different\n26856 variables have objective of 0\n118 variables have objective of 1\n==== absolute objective values 2 different\n26856 variables have objective of 0\n118 variables have objective of 1\n==== for integers 26856 zero objective 1 different\n26856 variables have objective of 0\n==== for integers absolute objective values 1 different\n26856 variables have objective of 0\n===== end objective counts\n\n\nProblem has 14257 rows, 26974 columns (118 with objective) and 180160 elements\nThere are 118 singletons with objective 660 singletons with no objective \nColumn breakdown:\n118 of type 0.0->inf, 0 of type 0.0->up, 0 of type lo->inf, \n0 of type lo->up, 0 of type free, 0 of type fixed, \n0 of type -inf->0.0, 0 of type -inf->up, 26856 of type 0.0->1.0 \nRow breakdown:\n1368 of type E 0.0, 972 of type E 1.0, 0 of type E -1.0, \n215 of type E other, 0 of type G 0.0, 12 of type G 1.0, \n189 of type G other, 0 of type L 0.0, 10572 of type L 1.0, \n929 of type L other, 0 of type Range 0.0->1.0, 0 of type Range other, \n0 of type Free \nContinuous objective value is 0 - 1.28 seconds\nCgl0002I 19824 variables fixed\nCgl0003I 0 fixed, 0 tightened bounds, 10100 strengthened rows, 26196 substitutions\nCgl0003I 0 fixed, 0 tightened bounds, 10502 strengthened rows, 0 substitutions\nCgl0003I 0 fixed, 0 tightened bounds, 6750 strengthened rows, 0 substitutions\nCgl0003I 0 fixed, 0 tightened bounds, 5776 strengthened rows, 0 substitutions\nCgl0003I 0 fixed, 0 tightened bounds, 5336 strengthened rows, 0 substitutions\nCgl0003I 0 fixed, 0 tightened bounds, 4286 strengthened rows, 0 substitutions\nCgl0003I 0 fixed, 0 tightened bounds, 3699 strengthened rows, 0 substitutions\nCgl0003I 0 fixed, 0 tightened bounds, 1069 strengthened rows, 0 substitutions\nCgl0003I 0 fixed, 0 tightened bounds, 650 strengthened rows, 0 substitutions\nCgl0004I processed model has 7928 rows, 13210 columns (13092 integer (13092 of which binary)) and 98818 elements\nCbc0038I Initial state - 635 integers unsatisfied sum - 124.129\nCbc0038I Pass 1: (9.41 seconds) suminf. 13.54383 (62) obj. 93.1564 iterations 2715\nCbc0038I Pass 2: (9.44 seconds) suminf. 11.68652 (52) obj. 89.0441 iterations 250\nCbc0038I Pass 3: (9.46 seconds) suminf. 11.11509 (49) obj. 89.9012 iterations 61\nCbc0038I Pass 4: (9.49 seconds) suminf. 7.97224 (37) obj. 90.2114 iterations 235\nCbc0038I Pass 5: (9.51 seconds) suminf. 7.97224 (37) obj. 90.2114 iterations 9\nCbc0038I Pass 6: (9.53 seconds) suminf. 5.82938 (26) obj. 89.5051 iterations 136\nCbc0038I Pass 7: (9.55 seconds) suminf. 5.82938 (26) obj. 89.5051 iterations 63\nCbc0038I Pass 8: (9.60 seconds) suminf. 3.65763 (16) obj. 93.2746 iterations 200\nCbc0038I Pass 9: (9.61 seconds) suminf. 3.65763 (16) obj. 93.2746 iterations 31\nCbc0038I Pass 10: (9.64 seconds) suminf. 3.65763 (16) obj. 91.6203 iterations 134\nCbc0038I Pass 11: (9.66 seconds) suminf. 3.65763 (16) obj. 91.6203 iterations 21\nCbc0038I Pass 12: (9.68 seconds) suminf. 3.65763 (16) obj. 93.2746 iterations 111\nCbc0038I Pass 13: (9.74 seconds) suminf. 10.34334 (39) obj. 109.802 iterations 651\nCbc0038I Pass 14: (9.80 seconds) suminf. 5.20048 (21) obj. 109.861 iterations 450\nCbc0038I Pass 15: (9.83 seconds) suminf. 4.40048 (18) obj. 106.088 iterations 228\nCbc0038I Pass 16: (9.85 seconds) suminf. 4.40048 (19) obj. 106.088 iterations 27\nCbc0038I Pass 17: (9.88 seconds) suminf. 3.60048 (16) obj. 104.434 iterations 212\nCbc0038I Pass 18: (9.90 seconds) suminf. 3.60048 (17) obj. 104.434 iterations 46\nCbc0038I Pass 19: (9.93 seconds) suminf. 3.60048 (16) obj. 104.947 iterations 177\nCbc0038I Pass 20: (9.96 seconds) suminf. 3.60048 (17) obj. 104.947 iterations 67\nCbc0038I Pass 21: (9.98 seconds) suminf. 3.60048 (16) obj. 107.173 iterations 168\nCbc0038I Pass 22: (10.00 seconds) suminf. 3.60048 (17) obj. 107.173 iterations 28\nCbc0038I Pass 23: (10.03 seconds) suminf. 3.60048 (16) obj. 107.686 iterations 146\nCbc0038I Pass 24: (10.04 seconds) suminf. 3.60048 (17) obj. 107.686 iterations 44\nCbc0038I Pass 25: (10.08 seconds) suminf. 3.60048 (16) obj. 104.434 iterations 176\nCbc0038I Pass 26: (10.10 seconds) suminf. 3.60048 (17) obj. 104.434 iterations 37\nCbc0038I Pass 27: (10.13 seconds) suminf. 3.60048 (16) obj. 104.947 iterations 170\nCbc0038I Pass 28: (10.15 seconds) suminf. 3.60048 (17) obj. 104.947 iterations 53\nCbc0038I Pass 29: (10.18 seconds) suminf. 3.60048 (16) obj. 105.803 iterations 180\nCbc0038I Pass 30: (10.20 seconds) suminf. 3.60048 (17) obj. 105.803 iterations 75\nCbc0038I No solution found this major pass\nCbc0038I Before mini branch and bound, 12254 integers at bound fixed and 38 continuous\nCbc0038I Full problem 7928 rows 13210 columns, reduced to 621 rows 573 columns\nCbc0038I Mini branch and bound did not improve solution (10.94 seconds)\nCbc0038I Full problem 7929 rows 13210 columns, reduced to 7929 rows 13210 columns - too large\nCbc0038I After 12.39 seconds - Feasibility pump exiting - took 3.49 seconds\nCbc0031I 165 added rows had average density of 129.56364\nCbc0013I At root node, 165 cuts changed objective from 0 to 14.542373 in 10 passes\nCbc0014I Cut generator 0 (Probing) - 296 row cuts average 17.7 elements, 0 column cuts (29 active) in 3.877 seconds - new frequency is 1\nCbc0014I Cut generator 1 (Gomory) - 292 row cuts average 302.7 elements, 0 column cuts (0 active) in 1.389 seconds - new frequency is 1\nCbc0014I Cut generator 2 (Knapsack) - 36 row cuts average 15.9 elements, 0 column cuts (0 active) in 0.085 seconds - new frequency is 1\nCbc0014I Cut generator 3 (Clique) - 6 row cuts average 3.5 elements, 0 column cuts (0 active) in 0.057 seconds - new frequency is -100\nCbc0014I Cut generator 4 (MixedIntegerRounding2) - 636 row cuts average 66.2 elements, 0 column cuts (0 active) in 0.203 seconds - new frequency is 1\nCbc0014I Cut generator 5 (FlowCover) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.038 seconds - new frequency is -100\nCbc0014I Cut generator 6 (TwoMirCuts) - 585 row cuts average 161.8 elements, 0 column cuts (0 active) in 1.274 seconds - new frequency is 1\nCbc0010I After 0 nodes, 1 on tree, 1e+50 best solution, best possible 14.542373 (27.71 seconds)\nCbc0012I Integer solution of 67.681356 found by DiveCoefficient after 13832 iterations and 2 nodes (31.89 seconds)\nCbc0038I Full problem 7928 rows 13210 columns, reduced to 500 rows 445 columns\nCbc0012I Integer solution of 65.420339 found by RINS after 13832 iterations and 2 nodes (37.68 seconds)\nCbc0012I Integer solution of 63.020339 found by DiveCoefficient after 16683 iterations and 7 nodes (42.56 seconds)\nCbc0012I Integer solution of 56.705085 found by DiveCoefficient after 17021 iterations and 8 nodes (43.21 seconds)\nCbc0038I Full problem 7928 rows 13210 columns, reduced to 475 rows 467 columns\nCbc0038I Full problem 7928 rows 13210 columns, reduced to 540 rows 502 columns\nCbc0012I Integer solution of 55.450847 found by RINS after 41604 iterations and 91 nodes (81.02 seconds)\nCbc0010I After 100 nodes, 56 on tree, 55.450847 best solution, best possible 14.542373 (83.41 seconds)\nCbc0038I Full problem 7928 rows 13210 columns, reduced to 489 rows 478 columns\nCbc0020I Exiting on maximum time\nCbc0005I Partial search - best objective 55.450847 (best possible 14.542373), took 66179 iterations and 200 nodes (112.52 seconds)\nCbc0032I Strong branching done 1622 times (15892 iterations), fathomed 0 nodes and fixed 0 variables\nCbc0035I Maximum depth 56, 0 variables fixed on reduced cost\nCuts at root node changed objective from 0 to 14.5424\nProbing was tried 219 times and created 1847 cuts of which 29 were active after adding rounds of cuts (5.587 seconds)\nGomory was tried 219 times and created 386 cuts of which 0 were active after adding rounds of cuts (6.989 seconds)\nKnapsack was tried 219 times and created 55 cuts of which 0 were active after adding rounds of cuts (1.355 seconds)\nClique was tried 10 times and created 6 cuts of which 0 were active after adding rounds of cuts (0.057 seconds)\nMixedIntegerRounding2 was tried 219 times and created 8454 cuts of which 0 were active after adding rounds of cuts (3.958 seconds)\nFlowCover was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.038 seconds)\nTwoMirCuts was tried 219 times and created 1370 cuts of which 0 were active after adding rounds of cuts (2.921 seconds)\nImplicationCuts was tried 16 times and created 37 cuts of which 0 were active after adding rounds of cuts (0.042 seconds)\n\nResult - Stopped on time limit\n\nObjective value: 55.45084746\nLower bound: 14.542\nGap: 2.81\nEnumerated nodes: 200\nTotal iterations: 66179\nTime (CPU seconds): 112.89\nTime (Wallclock seconds): 113.89\n\nTotal time (CPU seconds): 113.61 (Wallclock seconds): 114.65\n\nWARNING: Loading a SolverResults object with an 'aborted' status, but\n containing a solution\n" } ], + "source": [ + "opt = SolverFactory('cbc') # choose a solver\n", + "#opt = SolverFactory('ipopt') # choose a solver\n", + "\n", + "results = opt.solve(model, tee=True, options={\"seconds\":600}) # solve the model" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": "EnumValue(, 3, 'aborted')" + }, + "metadata": {}, + "execution_count": 21 + } + ], + "source": [ + "results.solver.status" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "tags": [ + "outputPrepend", + "outputPrepend" + ] + }, + "outputs": [], "source": [ "def get_workers_needed(needed):\n", " \"\"\"Extract to a list the needed workers for the optimal solution.\"\"\"\n", @@ -797,7 +3069,7 @@ " c = [works[worker.id, week, day, shift] for week in Shifts.weeks for day in days].count(1)\n", " if c > 0:\n", " l.append(\"{} ({})\".format(shift,c))\n", - " print(worker.id, shift)\n", + " #print(worker.id, shift)\n", " timetable[worker.get_details()][shift[0]] = c\n", " t.append(\",\".join(l))\n", " return \"\\n\".join(t) \n", @@ -828,7 +3100,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -869,74 +3141,19 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 24, "metadata": {}, "outputs": [ { "output_type": "stream", "name": "stdout", - "text": "Worker 1111111222222233333334444444\nWijhdan Abusrewil ----------------------------\nSeren Peters ----------------------------\nHannah Lewis ----------------------------\nChristian Greer ----------------------------\nNicolas Dziad ----------------------------\nMichal Tomek ----------------------------\nAlex Wood ----------------------------\nAlexander Sanchez- Cabello----------------------------\nMax Ireland ----------------------------\nVilim Kalamar ----------------------------\nSalma Aslam ----------------------------\nAdeola Omotade ----------------------------\nAmina Odeh ----------------------------\nCharles Finan ----------------------------\nAlan Eccles ----------------------------\nSarath Vennam ----------------------------\nPaul Ward ----------------------------\nSayed Alqarooni ----------------------------\nJean Sukumar ----------------------------\nJenn Fong ----------------------------\nChong Ng ----------------------------\nMatthew Thorley ----------------------------\nJenna Millington ----------------------------\nChi Leung ----------------------------\nTom Welsh ----------------------------\nRebecca Murphy ----------------------------\nDelilah Trimmer ----------------------------\nSophie McGlade ----------------------------\nS Ghauri ----------------------------\nKhalil Madbak ----------------------------\nNatasha Hougham ----------------------------\nRichard Chaytor ----------------------------\nSafi Rahman ----------------------------\nGeorge Edwards ----------------------------\nNeil McIntyre ----------------------------\nMo Babsail ----------------------------\nPaul Jenkins ----------------------------\nRoss Kruger ----------------------------\nAmoolya Mannava ----------------------------\n1 e 2: \n2 p 2: \n3 p 2: \n4 t 2: \n5 t 2: \n6 e 2: \n7 e 2: \n8 p 2: \n9 t 2: \n10 t 2: \n11 e 3: \n12 p 3: \n13 p 3: \n14 t 3: \n15 t 3: \n16 t 3: \n17 t 3: \n18 e 3: \n19 p 3: \n20 p 3: \n21 t 3: \n22 t 3: \n23 t 3: \n24 t 3: \n25 e 4: \n26 e 4: \n27 e 4: \n28 p 4: \n29 p 4: \n30 p 4: \n31 p 4: \n32 p 4: \n33 p 4: \n34 p 4: \n35 t 4: \n36 t 4: \n37 t 4: \n38 t 4: \n39 t 4: \n" + "text": "Worker 111111122222223333333444444455555556666666777777788888889999999000000011111112222222\nTest Person 30 ---e--------------------------------------e----------e---------e----w-nnnn----------\nTest Person 31 -------------w----------e-----------e---------nnn---------------------e---nnn-------\nTest Person 32 e----------e--------------w------------e---------------w---e---nnnn-----------------\nTest Person 33 --e-------e---------w--------e-----nnnn-----------e----------------------------e----\nTest Person 34 ----e--nnnn-------e------e--------------w----------e----------w---------------------\nTest Person 35 --------e---------nnn--e---w---e---------------w----------e-------------------------\nTest Person 36 -e----------w--------nnnn------------e-------e--------w-------------------------e---\nTest Person 37 -----w--------e------nnnn-------------------e----------------w---e-----e------------\nTest Person 38 ----nnn----------e----------e------------w----------e--------------nnn--------------\nTest Person 39 -------------------w--------------w----nnn----e---------e----------e-----e----------\nTest Person 40 ----nnn-----------nnn-----------e-----e-------------------------e----------w--------\nTest Person 41 ---------e------e----------------w--------nnnn--------------------------e---w----e--\nTest Person 42 ------w-------nnnn---e---------------------------e----------e-------------e---e-----\nTest Person 43 -------e----------------------e------------e--nnn--------e-----------w-------e-----w\nTest Person 44 ---------------e------e------------e------------w-------nnnn------e---------------w-\nTest Person 15 pp----------w----------------------nnnn---p-----w-------------w---------------p---w-\nTest Person 16 ---p----p-----------w-----w------------p--------------------nnn-------p------p----w-\nTest Person 17 ------w--------p------------nnnn-----------------------w------w---------p---w----p--\nTest Person 18 ----p-----p--------------------p--------w-nnnn----p---w----------p----------w-------\nTest Person 19 --p------p---------w----p----------p---------------p---w------------w-nnnn----------\nTest Person 20 -----w-nnnn----------------w--------p-------p--------p------p--------w---p----------\nTest Person 21 --t--------p----------p---w-----nnn-------------w--------p-------------p-----------w\nTest Person 22 -----------------p-----p---------w----p----------p-------------nnnn--w-----w-------w\nTest Person 23 --------------p------------w-------nnnn----p---w-----------p--------w---------------\nTest Person 24 --------------------w--------p----w-----------------p---nnnn-------p-------w----p---\nTest Person 25 -------------w----p-------------p----p---w-----w-nnnn-------------p-----------------\nTest Person 26 nnnn---p----w------w-----p---------------w----p---------p-------p-------------------\nTest Person 27 -----w----------p-----------p----w-----nnn-----------nnn-----w------------p---------\nTest Person 28 --------------t---------------p---w-----w----p---------------w-nnnn------------p----\nTest Person 29 ------w------w-------p----------nnn-------------------w---p----p---nnn--------------\nTest Person 45 tt---w-----nnn----------t-------------------------------t---t--------w-------------w\nTest Person 46 --t---w---------------------nnnn-----------t-----------w---t--------------------t---\nTest Person 47 -------------w------w----------------t----t---t-----t-----------------nnnn----------\nTest Person 48 ------------w-------------------------t-------nnn--t-------------t--------nnn-------\nTest Person 49 ----------t----t----------w------------t---------nnnn----------t-----------w--------\nTest Person 50 -----------------t---nnnn----t--------------------t---w-------w---------------t-----\nTest Person 51 --------------t-------t-------t---w-------nnnn--------------------t----t------------\nTest Person 52 --------t------------t------nnnn---t----w-----------------t------------------------w\nTest Person 53 -------t-----------w--------t---t--------------w-nnnn-----------t-------------------\nTest Person 54 --------------nnnn---------------w----------t--------t---t---------t----t-----------\nTest Person 55 -----------t---------------w--------t--nnn-------t-----------------nnn--------------\nTest Person 56 ---------t--------t----t--------nnn-------------w-------------------w----t-----t---w\nTest Person 57 ----t--------------------nnn-------------w-----------nnn---------------------t---t--\nTest Person 58 -----------nnn--t--------nnn---t-------------t------------------------------w-------\nTest Person 59 ---t----------nnnn-------t-----------------------------------w--------t---t-------w-\nTest Person 1 -t-----nnnn----------t--------------t-------------t---w---------------------------w-\nTest Person 10 ---------t---------w---------------------------w-----t---t--------------t----nnnn--w\nTest Person 11 ----------t---------------w---t----t-------------------w---t-----------------nnnn---\nTest Person 12 ---t-------nnn-------------------------------t--------------t-------w-----nnn---t---\nTest Person 13 nnnn---------------w--------------------------t-----t-----------t----w---t----------\nTest Person 14 --------------t---------t------------------t----w-------t----w----------------t--nnn\nTest Person 2 --t-nnn-----------t---------t------------w-----------------------t---------------nnn\nTest Person 3 nnnn----------------w------w----t-----------t----t----------------t-------t---------\nTest Person 4 --t-----t----w---t---------------------t----------------nnnn----------t-----w-------\nTest Person 5 ----t------t----------t--nnn-----w--------------------------nnn--------------t------\nTest Person 6 -----w-----------------t-------t-----t---------------nnn-------t-----------------nnn\nTest Person 7 ------w---------------------------w---t---t---------------t-nnn----t-------------t--\nTest Person 8 t--------------t--nnn--------t----------w----------------------------------w---t----\nTest Person 9 -------t----w---t--------t-------------------------t----------w--------t-----nnnn---\n" } ], "source": [ "\n", "print(worker_timetable_brief)\n", - "print(worker_shift_summary)" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": "[('Mon', 'exeter_twilight'),\n ('Tue', 'exeter_twilight'),\n ('Wed', 'exeter_twilight'),\n ('Thu', 'exeter_twilight'),\n ('Fri', 'exeter_twilight'),\n ('Mon', 'truro_twilight'),\n ('Tue', 'truro_twilight'),\n ('Wed', 'truro_twilight'),\n ('Thu', 'truro_twilight'),\n ('Fri', 'truro_twilight'),\n ('Mon', 'torquay_twilight'),\n ('Tue', 'torquay_twilight'),\n ('Wed', 'torquay_twilight'),\n ('Thu', 'torquay_twilight'),\n ('Fri', 'torquay_twilight'),\n ('Mon', 'plymouth_twilight'),\n ('Tue', 'plymouth_twilight'),\n ('Wed', 'plymouth_twilight'),\n ('Thu', 'plymouth_twilight'),\n ('Fri', 'plymouth_twilight'),\n ('Sat', 'weekend_exeter'),\n ('Sun', 'weekend_exeter'),\n ('Sat', 'weekend_truro'),\n ('Sun', 'weekend_truro'),\n ('Sat', 'weekend_torquay'),\n ('Sun', 'weekend_torquay'),\n ('Sat', 'weekend_plymouth'),\n ('Sun', 'weekend_plymouth'),\n ('Mon', 'night'),\n ('Tue', 'night'),\n ('Wed', 'night'),\n ('Thu', 'night'),\n ('Fri', 'night'),\n ('Sat', 'night'),\n ('Sun', 'night')]" - }, - "metadata": {}, - "execution_count": 21 - } - ], - "source": [ - "#workers_needed\n", - "Shifts.GetAllDayShiftsAsNames()" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": "{1: {'Mon': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Tue': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Wed': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Thu': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Fri': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []}},\n 2: {'Mon': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Tue': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Wed': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Thu': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Fri': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []}},\n 3: {'Mon': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Tue': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Wed': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Thu': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Fri': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []}},\n 4: {'Mon': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Tue': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Wed': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Thu': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Fri': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': []}}}" - }, - "metadata": {}, - "execution_count": 22 - } - ], - "source": [ - "week_table" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "#worker_night_block" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "#[[worker, week, day, shift] for day in days for shift in [\"night\"]]" + "" ] }, { @@ -945,16 +3162,15 @@ "metadata": {}, "outputs": [ { - "output_type": "execute_result", - "data": { - "text/plain": "1" - }, - "metadata": {}, - "execution_count": 25 + "output_type": "stream", + "name": "stdout", + "text": "30 e 4: ,exeter_twilight (4),weekend_exeter (1),night (4)\n31 e 4: ,exeter_twilight (3),weekend_exeter (1),night (6)\n32 e 4: ,exeter_twilight (4),weekend_exeter (2),night (4)\n33 e 4: ,exeter_twilight (5),weekend_exeter (1),night (4)\n34 e 4: ,exeter_twilight (4),weekend_exeter (2),night (4)\n35 e 4: ,exeter_twilight (4),weekend_exeter (2),night (3)\n36 e 4: ,exeter_twilight (4),weekend_exeter (2),night (4)\n37 e 4: ,exeter_twilight (4),weekend_exeter (2),night (4)\n38 e 4: ,exeter_twilight (3),weekend_exeter (1),night (6)\n39 e 4: ,exeter_twilight (4),weekend_exeter (2),night (3)\n40 e 4: ,exeter_twilight (3),weekend_exeter (1),night (6)\n41 e 4: ,exeter_twilight (4),weekend_exeter (2),night (4)\n42 e 4: ,exeter_twilight (5),weekend_exeter (1),night (4)\n43 e 4: ,exeter_twilight (5),weekend_exeter (2),night (3)\n44 e 4: ,exeter_twilight (4),weekend_exeter (2),night (4)\n15 p 4: ,plymouth_twilight (4),weekend_plymouth (4),night (4)\n16 p 4: ,plymouth_twilight (5),weekend_plymouth (3),night (3)\n17 p 4: ,plymouth_twilight (3),weekend_plymouth (4),night (4)\n18 p 4: ,plymouth_twilight (5),weekend_plymouth (3),night (4)\n19 p 4: ,plymouth_twilight (5),weekend_plymouth (3),night (4)\n20 p 4: ,plymouth_twilight (5),weekend_plymouth (3),night (4)\n21 p 4: ,truro_twilight (1),plymouth_twilight (4),weekend_plymouth (3),night (3)\n22 p 4: ,plymouth_twilight (4),weekend_plymouth (4),night (4)\n23 p 4: ,plymouth_twilight (3),weekend_plymouth (3),night (4)\n24 p 4: ,plymouth_twilight (4),weekend_plymouth (3),night (4)\n25 p 4: ,plymouth_twilight (4),weekend_plymouth (3),night (4)\n26 p 4: ,plymouth_twilight (5),weekend_plymouth (3),night (4)\n27 p 4: ,plymouth_twilight (3),weekend_plymouth (3),night (6)\n28 p 4: ,truro_twilight (1),plymouth_twilight (3),weekend_plymouth (3),night (4)\n29 p 4: ,plymouth_twilight (3),weekend_plymouth (3),night (6)\n45 t 4: ,torquay_twilight (5),weekend_torquay (2),weekend_plymouth (1),night (3)\n46 t 4: ,torquay_twilight (4),weekend_torquay (2),night (4)\n47 t 4: ,torquay_twilight (4),weekend_torquay (2),night (4)\n48 t 4: ,torquay_twilight (3),weekend_torquay (1),night (6)\n49 t 4: ,torquay_twilight (4),weekend_torquay (2),night (4)\n50 t 4: ,torquay_twilight (4),weekend_torquay (2),night (4)\n51 t 4: ,torquay_twilight (5),weekend_torquay (1),night (4)\n52 t 4: ,torquay_twilight (4),weekend_torquay (2),night (4)\n53 t 4: ,torquay_twilight (4),weekend_torquay (2),night (4)\n54 t 4: ,torquay_twilight (5),weekend_torquay (1),night (4)\n55 t 4: ,torquay_twilight (3),weekend_torquay (1),night (6)\n56 t 4: ,torquay_twilight (5),weekend_truro (1),weekend_torquay (2),night (3)\n57 t 4: ,torquay_twilight (3),weekend_torquay (1),night (6)\n58 t 4: ,torquay_twilight (3),weekend_torquay (1),night (6)\n59 t 4: ,torquay_twilight (4),weekend_torquay (2),night (4)\n1 t 4: ,truro_twilight (4),weekend_truro (2),night (4)\n10 t 4: ,truro_twilight (4),weekend_exeter (1),weekend_truro (2),night (4)\n11 t 4: ,truro_twilight (4),weekend_truro (2),night (4)\n12 t 4: ,truro_twilight (4),weekend_truro (1),night (6)\n13 t 4: ,truro_twilight (4),weekend_truro (2),night (4)\n14 t 4: ,truro_twilight (5),weekend_truro (2),night (3)\n2 t 4: ,truro_twilight (4),weekend_truro (1),night (6)\n3 t 4: ,truro_twilight (5),weekend_truro (2),night (4)\n4 t 4: ,truro_twilight (4),torquay_twilight (1),weekend_truro (2),night (4)\n5 t 4: ,truro_twilight (4),weekend_truro (1),night (6)\n6 t 4: ,truro_twilight (4),weekend_truro (1),night (6)\n7 t 4: ,truro_twilight (5),weekend_truro (2),night (3)\n8 t 4: ,truro_twilight (4),weekend_truro (2),night (3)\n9 t 4: ,truro_twilight (5),weekend_truro (2),night (4)\n" } ], "source": [ - "1 if 4 in [4, 1, 0] else 0" + "#workers_needed\n", + "print(worker_shift_summary)\n", + "" ] }, { @@ -965,14 +3181,14 @@ { "output_type": "execute_result", "data": { - "text/plain": "0" + "text/plain": "light': ['9 t 4'],\n 'torquay_twilight': ['58 t 4'],\n 'plymouth_twilight': ['27 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['42 e 4', '54 t 4', '59 t 4']},\n 'Thu': {'exeter_twilight': ['38 e 4'],\n 'truro_twilight': ['4 t 4'],\n 'torquay_twilight': ['50 t 4'],\n 'plymouth_twilight': ['22 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['42 e 4', '54 t 4', '59 t 4']},\n 'Fri': {'exeter_twilight': ['34 e 4'],\n 'truro_twilight': ['2 t 4'],\n 'torquay_twilight': ['56 t 4'],\n 'plymouth_twilight': ['25 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['35 e 4', '40 e 4', '8 t 4']},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['39 e 4', '10 t 4'],\n 'weekend_truro': ['13 t 4'],\n 'weekend_torquay': ['53 t 4'],\n 'weekend_plymouth': ['19 p 4', '26 p 4'],\n 'night': ['35 e 4', '40 e 4', '8 t 4']},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['33 e 4'],\n 'weekend_truro': ['3 t 4'],\n 'weekend_torquay': ['47 t 4'],\n 'weekend_plymouth': ['16 p 4', '24 p 4'],\n 'night': ['35 e 4', '40 e 4', '8 t 4']}},\n 4: {'Mon': {'exeter_twilight': ['42 e 4'],\n 'truro_twilight': ['1 t 4'],\n 'torquay_twilight': ['52 t 4'],\n 'plymouth_twilight': ['29 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['36 e 4', '37 e 4', '50 t 4']},\n 'Tue': {'exeter_twilight': ['44 e 4'],\n 'truro_twilight': ['5 t 4'],\n 'torquay_twilight': ['51 t 4'],\n 'plymouth_twilight': ['21 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['36 e 4', '37 e 4', '50 t 4']},\n 'Wed': {'exeter_twilight': ['35 e 4'],\n 'truro_twilight': ['6 t 4'],\n 'torquay_twilight': ['56 t 4'],\n 'plymouth_twilight': ['22 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['36 e 4', '37 e 4', '50 t 4']},\n 'Thu': {'exeter_twilight': ['31 e 4'],\n 'truro_twilight': ['14 t 4'],\n 'torquay_twilight': ['45 t 4'],\n 'plymouth_twilight': ['19 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['36 e 4', '37 e 4', '50 t 4']},\n 'Fri': {'exeter_twilight': ['34 e 4'],\n 'truro_twilight': ['9 t 4'],\n 'torquay_twilight': ['59 t 4'],\n 'plymouth_twilight': ['26 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['57 t 4', '58 t 4', '5 t 4']},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['32 e 4'],\n 'weekend_truro': ['11 t 4'],\n 'weekend_torquay': ['49 t 4'],\n 'weekend_plymouth': ['16 p 4', '21 p 4'],\n 'night': ['57 t 4', '58 t 4', '5 t 4']},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['35 e 4'],\n 'weekend_truro': ['3 t 4'],\n 'weekend_torquay': ['55 t 4'],\n 'weekend_plymouth': ['20 p 4', '23 p 4'],\n 'night': ['57 t 4', '58 t 4', '5 t 4']}},\n 5: {'Mon': {'exeter_twilight': ['38 e 4'],\n 'truro_twilight': ['2 t 4'],\n 'torquay_twilight': ['53 t 4'],\n 'plymouth_twilight': ['27 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['17 p 4', '46 t 4', '52 t 4']},\n 'Tue': {'exeter_twilight': ['33 e 4'],\n 'truro_twilight': ['8 t 4'],\n 'torquay_twilight': ['50 t 4'],\n 'plymouth_twilight': ['24 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['17 p 4', '46 t 4', '52 t 4']},\n 'Wed': {'exeter_twilight': ['43 e 4'],\n 'truro_twilight': ['11 t 4'],\n 'torquay_twilight': ['51 t 4'],\n 'plymouth_twilight': ['28 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['17 p 4', '46 t 4', '52 t 4']},\n 'Thu': {'exeter_twilight': ['35 e 4'],\n 'truro_twilight': ['6 t 4'],\n 'torquay_twilight': ['58 t 4'],\n 'plymouth_twilight': ['18 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['17 p 4', '46 t 4', '52 t 4']},\n 'Fri': {'exeter_twilight': ['40 e 4'],\n 'truro_twilight': ['3 t 4'],\n 'torquay_twilight': ['53 t 4'],\n 'plymouth_twilight': ['25 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['21 p 4', '29 p 4', '56 t 4']},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['41 e 4'],\n 'weekend_truro': ['5 t 4'],\n 'weekend_torquay': ['54 t 4'],\n 'weekend_plymouth': ['22 p 4', '27 p 4'],\n 'night': ['21 p 4', '29 p 4', '56 t 4']},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['39 e 4'],\n 'weekend_truro': ['7 t 4'],\n 'weekend_torquay': ['51 t 4'],\n 'weekend_plymouth': ['24 p 4', '28 p 4'],\n 'night': ['21 p 4', '29 p 4', '56 t 4']}},\n 6: {'Mon': {'exeter_twilight': ['44 e 4'],\n 'truro_twilight': ['11 t 4'],\n 'torquay_twilight': ['52 t 4'],\n 'plymouth_twilight': ['19 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['33 e 4', '15 p 4', '23 p 4']},\n 'Tue': {'exeter_twilight': ['31 e 4'],\n 'truro_twilight': ['1 t 4'],\n 'torquay_twilight': ['55 t 4'],\n 'plymouth_twilight': ['20 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['33 e 4', '15 p 4', '23 p 4']},\n 'Wed': {'exeter_twilight': ['36 e 4'],\n 'truro_twilight': ['6 t 4'],\n 'torquay_twilight': ['47 t 4'],\n 'plymouth_twilight': ['25 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['33 e 4', '15 p 4', '23 p 4']},\n 'Thu': {'exeter_twilight': ['40 e 4'],\n 'truro_twilight': ['7 t 4'],\n 'torquay_twilight': ['48 t 4'],\n 'plymouth_twilight': ['22 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['33 e 4', '15 p 4', '23 p 4']},\n 'Fri': {'exeter_twilight': ['32 e 4'],\n 'truro_twilight': ['4 t 4'],\n 'torquay_twilight': ['49 t 4'],\n 'plymouth_twilight': ['16 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['39 e 4', '27 p 4', '55 t 4']},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['34 e 4'],\n 'weekend_truro': ['8 t 4'],\n 'weekend_torquay': ['52 t 4'],\n 'weekend_plymouth': ['18 p 4', '28 p 4'],\n 'night': ['39 e 4', '27 p 4', '55 t 4']},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['38 e 4'],\n 'weekend_truro': ['2 t 4'],\n 'weekend_torquay': ['57 t 4'],\n 'weekend_plymouth': ['25 p 4', '26 p 4'],\n 'night': ['39 e 4', '27 p 4', '55 t 4']}},\n 7: {'Mon': {'exeter_twilight': ['30 e 4'],\n 'truro_twilight': ['7 t 4'],\n 'torquay_twilight': ['47 t 4'],\n 'plymouth_twilight': ['15 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['41 e 4', '18 p 4', '51 t 4']},\n 'Tue': {'exeter_twilight': ['43 e 4'],\n 'truro_twilight': ['14 t 4'],\n 'torquay_twilight': ['46 t 4'],\n 'plymouth_twilight': ['23 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['41 e 4', '18 p 4', '51 t 4']},\n 'Wed': {'exeter_twilight': ['37 e 4'],\n 'truro_twilight': ['3 t 4'],\n 'torquay_twilight': ['54 t 4'],\n 'plymouth_twilight': ['20 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['41 e 4', '18 p 4', '51 t 4']},\n 'Thu': {'exeter_twilight': ['36 e 4'],\n 'truro_twilight': ['12 t 4'],\n 'torquay_twilight': ['58 t 4'],\n 'plymouth_twilight': ['28 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['41 e 4', '18 p 4', '51 t 4']},\n 'Fri': {'exeter_twilight': ['39 e 4'],\n 'truro_twilight': ['13 t 4'],\n 'torquay_twilight': ['47 t 4'],\n 'plymouth_twilight': ['26 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['31 e 4', '43 e 4', '48 t 4']},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['35 e 4'],\n 'weekend_truro': ['10 t 4'],\n 'weekend_torquay': ['53 t 4'],\n 'weekend_plymouth': ['23 p 4', '25 p 4'],\n 'night': ['31 e 4', '43 e 4', '48 t 4']},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['44 e 4'],\n 'weekend_truro': ['14 t 4'],\n 'weekend_torquay': ['56 t 4'],\n 'weekend_plymouth': ['15 p 4', '21 p 4'],\n 'night': ['31 e 4', '43 e 4', '48 t 4']}},\n 8: {'Mon': {'exeter_twilight': ['42 e 4'],\n 'truro_twilight': ['3 t 4'],\n 'torquay_twilight': ['55 t 4'],\n 'plymouth_twilight': ['22 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['25 p 4', '49 t 4', '53 t 4']},\n 'Tue': {'exeter_twilight': ['33 e 4'],\n 'truro_twilight': ['1 t 4'],\n 'torquay_twilight': ['50 t 4'],\n 'plymouth_twilight': ['18 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['25 p 4', '49 t 4', '53 t 4']},\n 'Wed': {'exeter_twilight': ['34 e 4'],\n 'truro_twilight': ['9 t 4'],\n 'torquay_twilight': ['48 t 4'],\n 'plymouth_twilight': ['19 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['25 p 4', '49 t 4', '53 t 4']},\n 'Thu': {'exeter_twilight': ['38 e 4'],\n 'truro_twilight': ['13 t 4'],\n 'torquay_twilight': ['47 t 4'],\n 'plymouth_twilight': ['24 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['25 p 4', '49 t 4', '53 t 4']},\n 'Fri': {'exeter_twilight': ['30 e 4'],\n 'truro_twilight': ['10 t 4'],\n 'torquay_twilight': ['54 t 4'],\n 'plymouth_twilight': ['20 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['27 p 4', '57 t 4', '6 t 4']},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['36 e 4'],\n 'weekend_truro': ['1 t 4'],\n 'weekend_torquay': ['50 t 4'],\n 'weekend_plymouth': ['18 p 4', '29 p 4'],\n 'night': ['27 p 4', '57 t 4', '6 t 4']},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['32 e 4'],\n 'weekend_truro': ['11 t 4'],\n 'weekend_torquay': ['46 t 4'],\n 'weekend_plymouth': ['17 p 4', '19 p 4'],\n 'night': ['27 p 4', '57 t 4', '6 t 4']}},\n 9: {'Mon': {'exeter_twilight': ['39 e 4'],\n 'truro_twilight': ['14 t 4'],\n 'torquay_twilight': ['45 t 4'],\n 'plymouth_twilight': ['26 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['44 e 4', '24 p 4', '4 t 4']},\n 'Tue': {'exeter_twilight': ['43 e 4'],\n 'truro_twilight': ['10 t 4'],\n 'torquay_twilight': ['54 t 4'],\n 'plymouth_twilight': ['21 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['44 e 4', '24 p 4', '4 t 4']},\n 'Wed': {'exeter_twilight': ['35 e 4'],\n 'truro_twilight': ['7 t 4'],\n 'torquay_twilight': ['52 t 4'],\n 'plymouth_twilight': ['29 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['44 e 4', '24 p 4', '4 t 4']},\n 'Thu': {'exeter_twilight': ['32 e 4'],\n 'truro_twilight': ['11 t 4'],\n 'torquay_twilight': ['46 t 4'],\n 'plymouth_twilight': ['23 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['44 e 4', '24 p 4', '4 t 4']},\n 'Fri': {'exeter_twilight': ['42 e 4'],\n 'truro_twilight': ['12 t 4'],\n 'torquay_twilight': ['45 t 4'],\n 'plymouth_twilight': ['20 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['16 p 4', '5 t 4', '7 t 4']},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['37 e 4'],\n 'weekend_truro': ['14 t 4'],\n 'weekend_torquay': ['59 t 4'],\n 'weekend_plymouth': ['27 p 4', '28 p 4'],\n 'night': ['16 p 4', '5 t 4', '7 t 4']},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['34 e 4'],\n 'weekend_truro': ['9 t 4'],\n 'weekend_torquay': ['50 t 4'],\n 'weekend_plymouth': ['15 p 4', '17 p 4'],\n 'night': ['16 p 4', '5 t 4', '7 t 4']}},\n 10: {'Mon': {'exeter_twilight': ['30 e 4'],\n 'truro_twilight': ['6 t 4'],\n 'torquay_twilight': ['49 t 4'],\n 'plymouth_twilight': ['29 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['32 e 4', '22 p 4', '28 p 4']},\n 'Tue': {'exeter_twilight': ['40 e 4'],\n 'truro_twilight': ['13 t 4'],\n 'torquay_twilight': ['53 t 4'],\n 'plymouth_twilight': ['26 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['32 e 4', '22 p 4', '28 p 4']},\n 'Wed': {'exeter_twilight': ['37 e 4'],\n 'truro_twilight': ['2 t 4'],\n 'torquay_twilight': ['48 t 4'],\n 'plymouth_twilight': ['18 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['32 e 4', '22 p 4', '28 p 4']},\n 'Thu': {'exeter_twilight': ['44 e 4'],\n 'truro_twilight': ['3 t 4'],\n 'torquay_twilight': ['51 t 4'],\n 'plymouth_twilight': ['25 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['32 e 4', '22 p 4', '28 p 4']},\n 'Fri': {'exeter_twilight': ['39 e 4'],\n 'truro_twilight': ['7 t 4'],\n 'torquay_twilight': ['54 t 4'],\n 'plymouth_twilight': ['24 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['38 e 4', '29 p 4', '55 t 4']},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['30 e 4'],\n 'weekend_truro': ['12 t 4'],\n 'weekend_torquay': ['56 t 4'],\n 'weekend_plymouth': ['19 p 4', '23 p 4'],\n 'night': ['38 e 4', '29 p 4', '55 t 4']},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['43 e 4'],\n 'weekend_truro': ['13 t 4'],\n 'weekend_torquay': ['45 t 4'],\n 'weekend_plymouth': ['20 p 4', '22 p 4'],\n 'night': ['38 e 4', '29 p 4', '55 t 4']}},\n 11: {'Mon': {'exeter_twilight': ['31 e 4'],\n 'truro_twilight': ['4 t 4'],\n 'torquay_twilight': ['59 t 4'],\n 'plymouth_twilight': ['16 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['30 e 4', '19 p 4', '47 t 4']},\n 'Tue': {'exeter_twilight': ['37 e 4'],\n 'truro_twilight': ['9 t 4'],\n 'torquay_twilight': ['51 t 4'],\n 'plymouth_twilight': ['21 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['30 e 4', '19 p 4', '47 t 4']},\n 'Wed': {'exeter_twilight': ['41 e 4'],\n 'truro_twilight': ['10 t 4'],\n 'torquay_twilight': ['54 t 4'],\n 'plymouth_twilight': ['17 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['30 e 4', '19 p 4', '47 t 4']},\n 'Thu': {'exeter_twilight': ['39 e 4'],\n 'truro_twilight': ['13 t 4'],\n 'torquay_twilight': ['56 t 4'],\n 'plymouth_twilight': ['20 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['30 e 4', '19 p 4', '47 t 4']},\n 'Fri': {'exeter_twilight': ['42 e 4'],\n 'truro_twilight': ['3 t 4'],\n 'torquay_twilight': ['59 t 4'],\n 'plymouth_twilight': ['27 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['31 e 4', '48 t 4', '12 t 4']},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['40 e 4'],\n 'weekend_truro': ['8 t 4'],\n 'weekend_torquay': ['49 t 4'],\n 'weekend_plymouth': ['22 p 4', '24 p 4'],\n 'night': ['31 e 4', '48 t 4', '12 t 4']},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['41 e 4'],\n 'weekend_truro': ['4 t 4'],\n 'weekend_torquay': ['58 t 4'],\n 'weekend_plymouth': ['17 p 4', '18 p 4'],\n 'night': ['31 e 4', '48 t 4', '12 t 4']}},\n 12: {'Mon': {'exeter_twilight': ['43 e 4'],\n 'truro_twilight': ['5 t 4'],\n 'torquay_twilight': ['57 t 4'],\n 'plymouth_twilight': ['16 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['10 t 4', '11 t 4', '9 t 4']},\n 'Tue': {'exeter_twilight': ['42 e 4'],\n 'truro_twilight': ['14 t 4'],\n 'torquay_twilight': ['50 t 4'],\n 'plymouth_twilight': ['15 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['10 t 4', '11 t 4', '9 t 4']},\n 'Wed': {'exeter_twilight': ['33 e 4'],\n 'truro_twilight': ['8 t 4'],\n 'torquay_twilight': ['56 t 4'],\n 'plymouth_twilight': ['28 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['10 t 4', '11 t 4', '9 t 4']},\n 'Thu': {'exeter_twilight': ['36 e 4'],\n 'truro_twilight': ['12 t 4'],\n 'torquay_twilight': ['46 t 4'],\n 'plymouth_twilight': ['24 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['10 t 4', '11 t 4', '9 t 4']},\n 'Fri': {'exeter_twilight': ['41 e 4'],\n 'truro_twilight': ['7 t 4'],\n 'torquay_twilight': ['57 t 4'],\n 'plymouth_twilight': ['17 p 4'],\n 'weekend_exeter': [],\n 'weekend_truro': [],\n 'weekend_torquay': [],\n 'weekend_plymouth': [],\n 'night': ['14 t 4', '2 t 4', '6 t 4']},\n 'Sat': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['44 e 4'],\n 'weekend_truro': ['1 t 4'],\n 'weekend_torquay': ['59 t 4'],\n 'weekend_plymouth': ['15 p 4', '16 p 4'],\n 'night': ['14 t 4', '2 t 4', '6 t 4']},\n 'Sun': {'exeter_twilight': [],\n 'truro_twilight': [],\n 'torquay_twilight': [],\n 'plymouth_twilight': [],\n 'weekend_exeter': ['43 e 4'],\n 'weekend_truro': ['56 t 4', '10 t 4'],\n 'weekend_torquay': ['52 t 4'],\n 'weekend_plymouth': ['21 p 4', '22 p 4', '45 t 4'],\n 'night': ['14 t 4', '2 t 4', '6 t 4']}}}" }, "metadata": {}, "execution_count": 26 } ], "source": [ - "(4 + 1) // 7" + "week_table" ] }, { @@ -981,30 +3197,16 @@ "metadata": {}, "outputs": [], "source": [ - "#for worker in workers:\n", - "# for shift in shifts:\n", - "# a = [model.works[worker.id, week, day, shift].value for week in weeks for day in days]" + "#worker_night_block" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, - "outputs": [ - { - "output_type": "error", - "ename": "NameError", - "evalue": "name 'a' is not defined", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mNameError\u001b[0m: name 'a' is not defined" - ] - } - ], + "outputs": [], "source": [ - "a" + "#[[worker, week, day, shift] for day in days for shift in [\"night\"]]" ] }, { @@ -1015,14 +3217,14 @@ { "output_type": "execute_result", "data": { - "text/plain": "0" + "text/plain": "1" }, "metadata": {}, "execution_count": 29 } ], "source": [ - "0 % 7" + "1 if 4 in [4, 1, 0] else 0" ] }, { @@ -1033,16 +3235,120 @@ { "output_type": "execute_result", "data": { - "text/plain": "6" + "text/plain": "0" }, "metadata": {}, "execution_count": 30 } ], + "source": [ + "(4 + 1) // 7" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "#for worker in workers:\n", + "# for shift in shifts:\n", + "# a = [model.works[worker.id, week, day, shift].value for week in weeks for day in days]" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "output_type": "error", + "ename": "NameError", + "evalue": "name 'a' is not defined", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'a' is not defined" + ] + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": "0" + }, + "metadata": {}, + "execution_count": 33 + } + ], + "source": [ + "0 % 7" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": "6" + }, + "metadata": {}, + "execution_count": 34 + } + ], "source": [ "-1 % 7" ] }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": "6.0" + }, + "metadata": {}, + "execution_count": 35 + } + ], + "source": [ + "model.shift_count[(5, \"night\")].value" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": "[30,\n 31,\n 32,\n 33,\n 34,\n 35,\n 36,\n 37,\n 38,\n 39,\n 40,\n 41,\n 42,\n 43,\n 44,\n 15,\n 16,\n 17,\n 18,\n 19,\n 20,\n 21,\n 22,\n 23,\n 24,\n 25,\n 26,\n 27,\n 28,\n 29,\n 45,\n 46,\n 47,\n 48,\n 49,\n 50,\n 51,\n 52,\n 53,\n 54,\n 55,\n 56,\n 57,\n 58,\n 59,\n 1,\n 10,\n 11,\n 12,\n 13,\n 14,\n 2,\n 3,\n 4,\n 5,\n 6,\n 7,\n 8,\n 9]" + }, + "metadata": {}, + "execution_count": 36 + } + ], + "source": [ + "[w.id for w in workers]" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/shifts.py b/shifts.py index 0fee533..1ffd75b 100644 --- a/shifts.py +++ b/shifts.py @@ -24,7 +24,9 @@ class SingleShift(object): name, length, shift_days, - balance_by_site=True, + # balance_by_site=True, + balance_offset=1, # this could be generated dynamically + balance_weighting=1, # this could be generated dynamically workers_required=1, rota_on_nwds=False, ): @@ -32,10 +34,19 @@ class SingleShift(object): self.name = name self.length = length self.shift_days = shift_days - self.balance_by_site = balance_by_site + # self.balance_by_site = balance_by_site + # balance_offset defines the max difference in allocated shifts + # versus target shifts (hard constraint) + # if 0 exactly equal shifts must be assigend (unlikely to be possible) + self.balance_offset = balance_offset + + # weight the shift for balancing, default is 1 + self.balance_weighting = balance_weighting + self.workers_required = workers_required self.rota_on_nwds = rota_on_nwds + self.total_shifts = length * len(days) * len(shift_days) class ShiftCollection(object): """Class to hold and manipulate shifts""" @@ -51,6 +62,8 @@ class ShiftCollection(object): self.rota_days_length = len(self.weeks) * 7 self.rota_end_date = self.start_date + datetime.timedelta(self.rota_days_length) + self.unavailable_to_work = set() + def add_shift(self, shift): """Add a shift to the collection @@ -151,8 +164,12 @@ class ShiftCollection(object): """ return self.week_day_shift_product - def GetAllShiftsAsClass(self) -> List[SingleShift]: - """ """ + def GetAllShiftsAsClass(self) -> List[Tuple[WeekInt, DayStr, SingleShift]]: + """Returns a list of all possible week / day / shift combinations + + Returns: + list: contains tuple of all possible week / day / shift combinations + """ return self.week_day_shiftclass_product def GetAllWeeksDays(self) -> list: diff --git a/workers.py b/workers.py index d423065..1020331 100644 --- a/workers.py +++ b/workers.py @@ -1,10 +1,11 @@ import datetime +from collections import defaultdict from shifts import SingleShift, ShiftCollection, days, sites class Worker: - def __init__(self, Shifts, id, name, site, grade, fte=100, nwd=[], end_date=None, oop=None): + def __init__(self, Shifts, id, name, site, grade, fte=100, nwd=None, end_date=None, oop=None): self.id = id self.name = name self.site = site @@ -13,6 +14,8 @@ class Worker: self.nwd = nwd self.proportion_rota_to_work = 1 + self.shift_target_number = defaultdict(int) + days_to_work = Shifts.rota_days_length if end_date is None: @@ -28,7 +31,7 @@ class Worker: # add unavalabilities for weeks_days in Shifts.weeks_days_product[days_to_work:]: week, day = weeks_days - unavailable_to_work.add((self.id, week, day)) + Shifts.unavailable_to_work.add((self.id, week, day)) if oop is not None: start_oop, end_oop = oop @@ -45,7 +48,7 @@ class Worker: for weeks_days in Shifts.weeks_days_product[days_until_oop:days_until_oop+oop_length]: week, day = weeks_days - unavailable_to_work.add((self.id, week, day)) + Shifts.unavailable_to_work.add((self.id, week, day)) self.proportion_rota_to_work = days_to_work / Shifts.rota_days_length