From 97c4fce6c816f77f351c2f68c61bafe15f61af46 Mon Sep 17 00:00:00 2001 From: Quarter Date: Mon, 30 Oct 2023 14:59:04 +0300 Subject: [PATCH 1/3] Added ScheduledProject for maintaining general scheduling parameters through the pipeline --- examples/scheduling_project.ipynb | 319 ++++++++++++++++++++++++ examples/simple_generation.ipynb | 400 +++++++----------------------- sampo/schemas/project.py | 11 + sampo/schemas/scheduled_work.py | 3 +- 4 files changed, 423 insertions(+), 310 deletions(-) create mode 100644 examples/scheduling_project.ipynb create mode 100644 sampo/schemas/project.py diff --git a/examples/scheduling_project.ipynb b/examples/scheduling_project.ipynb new file mode 100644 index 00000000..a1be8b18 --- /dev/null +++ b/examples/scheduling_project.ipynb @@ -0,0 +1,319 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2023-10-30T11:58:15.178958500Z", + "start_time": "2023-10-30T11:58:14.729575400Z" + } + }, + "outputs": [], + "source": [ + "from sampo.generator.base import SimpleSynthetic\n", + "from sampo.generator.types import SyntheticGraphType" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 1. Graph generation" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [], + "source": [ + "# SimpleSynthetic object used for the simple work graph structure generation\n", + "\n", + "r_seed = 231\n", + "ss = SimpleSynthetic(r_seed)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:58:15.199062800Z", + "start_time": "2023-10-30T11:58:15.178958500Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 3, + "outputs": [], + "source": [ + "# simple graph\n", + "# should generate general (average) type of graph with 10 clusters from 100 to 200 vertices each\n", + "\n", + "simple_wg = ss.work_graph(mode=SyntheticGraphType.GENERAL,\n", + " cluster_counts=10,\n", + " bottom_border=100,\n", + " top_border=200)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:58:15.239450600Z", + "start_time": "2023-10-30T11:58:15.204103700Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 4, + "outputs": [], + "source": [ + "# complex graph\n", + "# should generate general (average) type of graph with 300 unique works, 100 resources and 2000 vertices\n", + "\n", + "advanced_wg = ss.advanced_work_graph(works_count_top_border=2000,\n", + " uniq_works=300,\n", + " uniq_resources=100)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:58:22.218955300Z", + "start_time": "2023-10-30T11:58:15.239450600Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "# 2. Contractor generation" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [], + "source": [ + "from uuid import uuid4\n", + "from sampo.schemas.resources import Worker\n", + "from sampo.schemas.contractor import Contractor" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:58:22.239236400Z", + "start_time": "2023-10-30T11:58:22.218955300Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Manual generation\n", + "To create contractor, you should provide minimal info: unique id, contractor name, and supplied workers (simple renewable resources)." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [], + "source": [ + "contractors = [\n", + " Contractor(id=str(uuid4()),\n", + " name=\"OOO Berezka\",\n", + " workers={'worker' : Worker(id='0', name='worker', count=100)})\n", + "]" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:58:22.259121400Z", + "start_time": "2023-10-30T11:58:22.239236400Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Generation from graph" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 7, + "outputs": [], + "source": [ + "from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg\n", + "\n", + "contractors = [get_contractor_by_wg(simple_wg)]" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:58:22.279192200Z", + "start_time": "2023-10-30T11:58:22.259121400Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "# 3. Scheduling" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Scheduler construction\n", + "Before scheduling you should specify scheduling algorithm used for transforming input data to the final schedule.\n", + "At this time SAMPO contains heuristic algorithms, such as HEFTAddEnd, HEFTAddBetween and Topological scheduler, and the Genetic algorithm.\n", + "While creation, you can specify the hyperparameters to fit the algorithm." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [], + "source": [ + "from sampo.scheduler.heft.base import HEFTScheduler\n", + "\n", + "# here we can just create simple heuristic scheduler\n", + "scheduler = HEFTScheduler()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:58:22.299194700Z", + "start_time": "2023-10-30T11:58:22.274152200Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Scheduling process\n", + "SAMPO provides a simple interface to all its features.\n", + "It called SchedulingPipeline.\n", + "Using it you only should pass all the scheduling arguments, it remembers them, and you can produce schedules in many ways." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 9, + "outputs": [ + { + "data": { + "text/plain": "1194" + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sampo.pipeline import SchedulingPipeline\n", + "\n", + "project = SchedulingPipeline.create() \\\n", + " .wg(simple_wg) \\\n", + " .contractors(contractors) \\\n", + " .schedule(scheduler) \\\n", + " .finish()\n", + "\n", + "project.schedule.execution_time" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:58:22.599186200Z", + "start_time": "2023-10-30T11:58:22.299194700Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 10, + "outputs": [ + { + "ename": "TypeError", + "evalue": "Object of type Contractor is not JSON serializable", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[1;32mIn[10], line 1\u001B[0m\n\u001B[1;32m----> 1\u001B[0m \u001B[43mproject\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdump\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43m.\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43mproject_test\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m)\u001B[49m\n", + "File \u001B[1;32m~\\PycharmProjects\\sampo\\sampo\\schemas\\serializable.py:222\u001B[0m, in \u001B[0;36mJSONSerializable.dump\u001B[1;34m(self, folder_path, file_name)\u001B[0m\n\u001B[0;32m 220\u001B[0m serialized_dict \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_serialize()\n\u001B[0;32m 221\u001B[0m \u001B[38;5;28;01mwith\u001B[39;00m \u001B[38;5;28mopen\u001B[39m(full_file_name, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mw\u001B[39m\u001B[38;5;124m'\u001B[39m, encoding\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mutf-8\u001B[39m\u001B[38;5;124m'\u001B[39m) \u001B[38;5;28;01mas\u001B[39;00m write_file:\n\u001B[1;32m--> 222\u001B[0m \u001B[43mjson\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdump\u001B[49m\u001B[43m(\u001B[49m\u001B[43mserialized_dict\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mwrite_file\u001B[49m\u001B[43m)\u001B[49m\n", + "File \u001B[1;32m~\\AppData\\Local\\Programs\\Python\\Python310\\lib\\json\\__init__.py:179\u001B[0m, in \u001B[0;36mdump\u001B[1;34m(obj, fp, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)\u001B[0m\n\u001B[0;32m 173\u001B[0m iterable \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mcls\u001B[39m(skipkeys\u001B[38;5;241m=\u001B[39mskipkeys, ensure_ascii\u001B[38;5;241m=\u001B[39mensure_ascii,\n\u001B[0;32m 174\u001B[0m check_circular\u001B[38;5;241m=\u001B[39mcheck_circular, allow_nan\u001B[38;5;241m=\u001B[39mallow_nan, indent\u001B[38;5;241m=\u001B[39mindent,\n\u001B[0;32m 175\u001B[0m separators\u001B[38;5;241m=\u001B[39mseparators,\n\u001B[0;32m 176\u001B[0m default\u001B[38;5;241m=\u001B[39mdefault, sort_keys\u001B[38;5;241m=\u001B[39msort_keys, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkw)\u001B[38;5;241m.\u001B[39miterencode(obj)\n\u001B[0;32m 177\u001B[0m \u001B[38;5;66;03m# could accelerate with writelines in some versions of Python, at\u001B[39;00m\n\u001B[0;32m 178\u001B[0m \u001B[38;5;66;03m# a debuggability cost\u001B[39;00m\n\u001B[1;32m--> 179\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m chunk \u001B[38;5;129;01min\u001B[39;00m iterable:\n\u001B[0;32m 180\u001B[0m fp\u001B[38;5;241m.\u001B[39mwrite(chunk)\n", + "File \u001B[1;32m~\\AppData\\Local\\Programs\\Python\\Python310\\lib\\json\\encoder.py:431\u001B[0m, in \u001B[0;36m_make_iterencode.._iterencode\u001B[1;34m(o, _current_indent_level)\u001B[0m\n\u001B[0;32m 429\u001B[0m \u001B[38;5;28;01myield from\u001B[39;00m _iterencode_list(o, _current_indent_level)\n\u001B[0;32m 430\u001B[0m \u001B[38;5;28;01melif\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(o, \u001B[38;5;28mdict\u001B[39m):\n\u001B[1;32m--> 431\u001B[0m \u001B[38;5;28;01myield from\u001B[39;00m _iterencode_dict(o, _current_indent_level)\n\u001B[0;32m 432\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[0;32m 433\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m markers \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n", + "File \u001B[1;32m~\\AppData\\Local\\Programs\\Python\\Python310\\lib\\json\\encoder.py:405\u001B[0m, in \u001B[0;36m_make_iterencode.._iterencode_dict\u001B[1;34m(dct, _current_indent_level)\u001B[0m\n\u001B[0;32m 403\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[0;32m 404\u001B[0m chunks \u001B[38;5;241m=\u001B[39m _iterencode(value, _current_indent_level)\n\u001B[1;32m--> 405\u001B[0m \u001B[38;5;28;01myield from\u001B[39;00m chunks\n\u001B[0;32m 406\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m newline_indent \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[0;32m 407\u001B[0m _current_indent_level \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[38;5;241m1\u001B[39m\n", + "File \u001B[1;32m~\\AppData\\Local\\Programs\\Python\\Python310\\lib\\json\\encoder.py:325\u001B[0m, in \u001B[0;36m_make_iterencode.._iterencode_list\u001B[1;34m(lst, _current_indent_level)\u001B[0m\n\u001B[0;32m 323\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[0;32m 324\u001B[0m chunks \u001B[38;5;241m=\u001B[39m _iterencode(value, _current_indent_level)\n\u001B[1;32m--> 325\u001B[0m \u001B[38;5;28;01myield from\u001B[39;00m chunks\n\u001B[0;32m 326\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m newline_indent \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[0;32m 327\u001B[0m _current_indent_level \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[38;5;241m1\u001B[39m\n", + "File \u001B[1;32m~\\AppData\\Local\\Programs\\Python\\Python310\\lib\\json\\encoder.py:438\u001B[0m, in \u001B[0;36m_make_iterencode.._iterencode\u001B[1;34m(o, _current_indent_level)\u001B[0m\n\u001B[0;32m 436\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mValueError\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mCircular reference detected\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m 437\u001B[0m markers[markerid] \u001B[38;5;241m=\u001B[39m o\n\u001B[1;32m--> 438\u001B[0m o \u001B[38;5;241m=\u001B[39m \u001B[43m_default\u001B[49m\u001B[43m(\u001B[49m\u001B[43mo\u001B[49m\u001B[43m)\u001B[49m\n\u001B[0;32m 439\u001B[0m \u001B[38;5;28;01myield from\u001B[39;00m _iterencode(o, _current_indent_level)\n\u001B[0;32m 440\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m markers \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n", + "File \u001B[1;32m~\\AppData\\Local\\Programs\\Python\\Python310\\lib\\json\\encoder.py:179\u001B[0m, in \u001B[0;36mJSONEncoder.default\u001B[1;34m(self, o)\u001B[0m\n\u001B[0;32m 160\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mdefault\u001B[39m(\u001B[38;5;28mself\u001B[39m, o):\n\u001B[0;32m 161\u001B[0m \u001B[38;5;250m \u001B[39m\u001B[38;5;124;03m\"\"\"Implement this method in a subclass such that it returns\u001B[39;00m\n\u001B[0;32m 162\u001B[0m \u001B[38;5;124;03m a serializable object for ``o``, or calls the base implementation\u001B[39;00m\n\u001B[0;32m 163\u001B[0m \u001B[38;5;124;03m (to raise a ``TypeError``).\u001B[39;00m\n\u001B[1;32m (...)\u001B[0m\n\u001B[0;32m 177\u001B[0m \n\u001B[0;32m 178\u001B[0m \u001B[38;5;124;03m \"\"\"\u001B[39;00m\n\u001B[1;32m--> 179\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mTypeError\u001B[39;00m(\u001B[38;5;124mf\u001B[39m\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mObject of type \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mo\u001B[38;5;241m.\u001B[39m\u001B[38;5;18m__class__\u001B[39m\u001B[38;5;241m.\u001B[39m\u001B[38;5;18m__name__\u001B[39m\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m \u001B[39m\u001B[38;5;124m'\u001B[39m\n\u001B[0;32m 180\u001B[0m \u001B[38;5;124mf\u001B[39m\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mis not JSON serializable\u001B[39m\u001B[38;5;124m'\u001B[39m)\n", + "\u001B[1;31mTypeError\u001B[0m: Object of type Contractor is not JSON serializable" + ] + } + ], + "source": [ + "project.dump('.', 'project_test')" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:58:23.386360200Z", + "start_time": "2023-10-30T11:58:22.619154Z" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "start_time": "2023-10-30T11:58:23.386360200Z" + } + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/simple_generation.ipynb b/examples/simple_generation.ipynb index 79108bb2..d7dcf607 100644 --- a/examples/simple_generation.ipynb +++ b/examples/simple_generation.ipynb @@ -2,9 +2,13 @@ "cells": [ { "cell_type": "code", - "execution_count": 5, + "execution_count": 1, "metadata": { - "collapsed": true + "collapsed": true, + "ExecuteTime": { + "end_time": "2023-10-30T11:21:31.055771800Z", + "start_time": "2023-10-30T11:21:29.736888300Z" + } }, "outputs": [], "source": [ @@ -23,7 +27,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 2, "outputs": [], "source": [ "# SimpleSynthetic object used for the simple work graph structure generation\n", @@ -32,12 +36,16 @@ "ss = SimpleSynthetic(r_seed)" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:21:31.069733100Z", + "start_time": "2023-10-30T11:21:31.055771800Z" + } } }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 3, "outputs": [], "source": [ "# simple graph\n", @@ -49,12 +57,16 @@ " top_border=200)" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:21:31.107631500Z", + "start_time": "2023-10-30T11:21:31.083698500Z" + } } }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 4, "outputs": [], "source": [ "# complex graph\n", @@ -65,7 +77,11 @@ " uniq_resources=100)" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:21:38.124598200Z", + "start_time": "2023-10-30T11:21:31.102645500Z" + } } }, { @@ -79,7 +95,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 5, "outputs": [], "source": [ "from uuid import uuid4\n", @@ -87,7 +103,11 @@ "from sampo.schemas.contractor import Contractor" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:21:38.153209700Z", + "start_time": "2023-10-30T11:21:38.123625400Z" + } } }, { @@ -102,7 +122,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 6, "outputs": [], "source": [ "contractors = [\n", @@ -112,7 +132,11 @@ "]" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:21:38.162187800Z", + "start_time": "2023-10-30T11:21:38.141241900Z" + } } }, { @@ -126,7 +150,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 7, "outputs": [], "source": [ "from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg\n", @@ -134,7 +158,11 @@ "contractors = [get_contractor_by_wg(simple_wg)]" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:21:38.175487800Z", + "start_time": "2023-10-30T11:21:38.156204500Z" + } } }, { @@ -160,7 +188,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 8, "outputs": [], "source": [ "from sampo.scheduler.heft.base import HEFTScheduler\n", @@ -169,12 +197,16 @@ "scheduler = HEFTScheduler()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:21:38.248502Z", + "start_time": "2023-10-30T11:21:38.171500500Z" + } } }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 9, "outputs": [ { "name": "stdout", @@ -192,7 +224,11 @@ " mutate_resources=0.3)" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:21:38.326329400Z", + "start_time": "2023-10-30T11:21:38.249500400Z" + } } }, { @@ -209,77 +245,23 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 10, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Genetic optimizing took 33.90812873840332 ms\n", - "Toolbox initialization & first population took 308.14647674560547 ms\n", - "First population evaluation took 522.6023197174072 ms\n", - "-- Generation 0, population=20, best time=995.0 --\n", - "-- Generation 1, population=22, best time=995.0 --\n", - "-- Generation 2, population=34, best time=989.0 --\n", - "-- Generation 3, population=37, best time=989.0 --\n", - "-- Generation 4, population=29, best time=989.0 --\n", - "-- Generation 5, population=39, best time=989.0 --\n", - "-- Generation 6, population=63, best time=989.0 --\n", - "-- Generation 7, population=34, best time=989.0 --\n", - "-- Generation 8, population=49, best time=989.0 --\n", - "-- Generation 9, population=45, best time=989.0 --\n", - "-- Generation 10, population=101, best time=983.0 --\n", - "-- Generation 11, population=46, best time=983.0 --\n", - "-- Generation 12, population=34, best time=983.0 --\n", - "-- Generation 13, population=49, best time=983.0 --\n", - "-- Generation 14, population=49, best time=983.0 --\n", - "-- Generation 15, population=95, best time=982.0 --\n", - "-- Generation 16, population=32, best time=982.0 --\n", - "-- Generation 17, population=34, best time=982.0 --\n", - "-- Generation 18, population=37, best time=980.0 --\n", - "-- Generation 19, population=56, best time=980.0 --\n", - "-- Generation 20, population=25, best time=980.0 --\n", - "-- Generation 21, population=26, best time=980.0 --\n", - "-- Generation 22, population=31, best time=980.0 --\n", - "-- Generation 23, population=38, best time=980.0 --\n", - "-- Generation 24, population=41, best time=977.0 --\n", - "-- Generation 25, population=75, best time=977.0 --\n", - "-- Generation 26, population=80, best time=976.0 --\n", - "-- Generation 27, population=52, best time=976.0 --\n", - "-- Generation 28, population=45, best time=976.0 --\n", - "-- Generation 29, population=97, best time=972.0 --\n", - "-- Generation 30, population=100, best time=972.0 --\n", - "-- Generation 31, population=47, best time=972.0 --\n", - "-- Generation 32, population=45, best time=972.0 --\n", - "-- Generation 33, population=72, best time=969.0 --\n", - "-- Generation 34, population=55, best time=969.0 --\n", - "-- Generation 35, population=79, best time=969.0 --\n", - "-- Generation 36, population=81, best time=969.0 --\n", - "-- Generation 37, population=144, best time=969.0 --\n", - "-- Generation 38, population=38, best time=969.0 --\n", - "-- Generation 39, population=118, best time=969.0 --\n", - "-- Generation 40, population=40, best time=969.0 --\n", - "-- Generation 41, population=112, best time=969.0 --\n", - "-- Generation 42, population=48, best time=969.0 --\n", - "-- Generation 43, population=108, best time=969.0 --\n", - "-- Generation 44, population=120, best time=969.0 --\n", - "-- Generation 45, population=97, best time=969.0 --\n", - "-- Generation 46, population=72, best time=969.0 --\n", - "-- Generation 47, population=53, best time=969.0 --\n", - "-- Generation 48, population=112, best time=968.0 --\n", - "-- Generation 49, population=46, best time=968.0 --\n", - "Final time: 968.0\n", - "Generations processing took 57636.97600364685 ms\n", - "Evaluation time: 53731.34756088257\n" + "Genetic optimizing took 32.912254333496094 ms\n" ] }, { - "data": { - "text/plain": "968" - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "KeyboardInterrupt\n", + "\n" + ] } ], "source": [ @@ -294,7 +276,11 @@ "schedule.execution_time" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-30T11:21:43.035314100Z", + "start_time": "2023-10-30T11:21:38.328324600Z" + } } }, { @@ -312,80 +298,8 @@ }, { "cell_type": "code", - "execution_count": 15, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Can keep deadline at minimum resources\n", - "Genetic optimizing took 32.93895721435547 ms\n", - "Toolbox initialization & first population took 159.57283973693848 ms\n", - "First population evaluation took 573.4663009643555 ms\n", - "-- Generation 0, population=20, best time=313.0 --\n", - "-- Generation 1, population=57, best time=298.0 --\n", - "-- Generation 2, population=56, best time=298.0 --\n", - "-- Generation 3, population=82, best time=273.0 --\n", - "-- Generation 4, population=100, best time=264.0 --\n", - "-- Generation 5, population=48, best time=243.0 --\n", - "-- Generation 6, population=54, best time=243.0 --\n", - "-- Generation 7, population=22, best time=243.0 --\n", - "-- Generation 8, population=20, best time=243.0 --\n", - "-- Generation 9, population=20, best time=243.0 --\n", - "-- Generation 10, population=20, best time=243.0 --\n", - "-- Generation 11, population=20, best time=243.0 --\n", - "-- Generation 12, population=20, best time=243.0 --\n", - "-- Generation 13, population=20, best time=243.0 --\n", - "-- Generation 14, population=20, best time=243.0 --\n", - "-- Generation 15, population=20, best time=243.0 --\n", - "-- Generation 16, population=20, best time=243.0 --\n", - "-- Generation 17, population=20, best time=243.0 --\n", - "-- Generation 18, population=20, best time=243.0 --\n", - "-- Generation 19, population=20, best time=243.0 --\n", - "-- Generation 20, population=20, best time=243.0 --\n", - "-- Generation 21, population=20, best time=243.0 --\n", - "-- Generation 22, population=20, best time=243.0 --\n", - "-- Generation 23, population=20, best time=243.0 --\n", - "-- Generation 24, population=20, best time=243.0 --\n", - "-- Generation 25, population=20, best time=243.0 --\n", - "-- Generation 26, population=20, best time=243.0 --\n", - "-- Generation 27, population=20, best time=243.0 --\n", - "-- Generation 28, population=20, best time=243.0 --\n", - "-- Generation 29, population=20, best time=243.0 --\n", - "-- Generation 30, population=20, best time=243.0 --\n", - "-- Generation 31, population=20, best time=243.0 --\n", - "-- Generation 32, population=20, best time=243.0 --\n", - "-- Generation 33, population=20, best time=243.0 --\n", - "-- Generation 34, population=20, best time=243.0 --\n", - "-- Generation 35, population=20, best time=243.0 --\n", - "-- Generation 36, population=20, best time=243.0 --\n", - "-- Generation 37, population=20, best time=243.0 --\n", - "-- Generation 38, population=20, best time=243.0 --\n", - "-- Generation 39, population=20, best time=243.0 --\n", - "-- Generation 40, population=20, best time=243.0 --\n", - "-- Generation 41, population=20, best time=243.0 --\n", - "-- Generation 42, population=20, best time=243.0 --\n", - "-- Generation 43, population=20, best time=243.0 --\n", - "-- Generation 44, population=20, best time=243.0 --\n", - "-- Generation 45, population=20, best time=243.0 --\n", - "-- Generation 46, population=20, best time=243.0 --\n", - "-- Generation 47, population=20, best time=243.0 --\n", - "-- Generation 48, population=20, best time=243.0 --\n", - "-- Generation 49, population=20, best time=243.0 --\n", - "Final time: 243.0\n", - "Generations processing took 13938.697099685669 ms\n", - "Evaluation time: 9931.756019592285\n" - ] - }, - { - "data": { - "text/plain": "1891" - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "outputs": [], "source": [ "from sampo.schemas.time import Time\n", "from sampo.scheduler.genetic.operators import DeadlineResourcesFitness\n", @@ -408,7 +322,10 @@ "schedule.execution_time" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "start_time": "2023-10-30T11:21:43.028359300Z" + } } }, { @@ -422,80 +339,8 @@ }, { "cell_type": "code", - "execution_count": 16, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Can keep deadline at minimum resources\n", - "Genetic optimizing took 37.89687156677246 ms\n", - "Toolbox initialization & first population took 248.33416938781738 ms\n", - "First population evaluation took 672.2033023834229 ms\n", - "-- Generation 0, population=20, best time=1312150.0 --\n", - "-- Generation 1, population=20, best time=1312150.0 --\n", - "-- Generation 2, population=20, best time=1312150.0 --\n", - "-- Generation 3, population=20, best time=1312150.0 --\n", - "-- Generation 4, population=20, best time=1312150.0 --\n", - "-- Generation 5, population=20, best time=1312150.0 --\n", - "-- Generation 6, population=20, best time=1312150.0 --\n", - "-- Generation 7, population=20, best time=1312150.0 --\n", - "-- Generation 8, population=20, best time=1312150.0 --\n", - "-- Generation 9, population=20, best time=1312150.0 --\n", - "-- Generation 10, population=20, best time=1312150.0 --\n", - "-- Generation 11, population=20, best time=1312150.0 --\n", - "-- Generation 12, population=20, best time=1312150.0 --\n", - "-- Generation 13, population=20, best time=1312150.0 --\n", - "-- Generation 14, population=20, best time=1312150.0 --\n", - "-- Generation 15, population=20, best time=1312150.0 --\n", - "-- Generation 16, population=20, best time=1312150.0 --\n", - "-- Generation 17, population=20, best time=1312150.0 --\n", - "-- Generation 18, population=20, best time=1312150.0 --\n", - "-- Generation 19, population=20, best time=1312150.0 --\n", - "-- Generation 20, population=20, best time=1312150.0 --\n", - "-- Generation 21, population=20, best time=1312150.0 --\n", - "-- Generation 22, population=20, best time=1312150.0 --\n", - "-- Generation 23, population=20, best time=1312150.0 --\n", - "-- Generation 24, population=20, best time=1312150.0 --\n", - "-- Generation 25, population=20, best time=1312150.0 --\n", - "-- Generation 26, population=20, best time=1312150.0 --\n", - "-- Generation 27, population=20, best time=1312150.0 --\n", - "-- Generation 28, population=20, best time=1312150.0 --\n", - "-- Generation 29, population=20, best time=1312150.0 --\n", - "-- Generation 30, population=20, best time=1312150.0 --\n", - "-- Generation 31, population=20, best time=1312150.0 --\n", - "-- Generation 32, population=20, best time=1312150.0 --\n", - "-- Generation 33, population=20, best time=1312150.0 --\n", - "-- Generation 34, population=20, best time=1312150.0 --\n", - "-- Generation 35, population=20, best time=1312150.0 --\n", - "-- Generation 36, population=20, best time=1312150.0 --\n", - "-- Generation 37, population=20, best time=1312150.0 --\n", - "-- Generation 38, population=20, best time=1312150.0 --\n", - "-- Generation 39, population=20, best time=1312150.0 --\n", - "-- Generation 40, population=20, best time=1312150.0 --\n", - "-- Generation 41, population=20, best time=1312150.0 --\n", - "-- Generation 42, population=20, best time=1312150.0 --\n", - "-- Generation 43, population=20, best time=1312150.0 --\n", - "-- Generation 44, population=20, best time=1312150.0 --\n", - "-- Generation 45, population=20, best time=1312150.0 --\n", - "-- Generation 46, population=20, best time=1312150.0 --\n", - "-- Generation 47, population=20, best time=1312150.0 --\n", - "-- Generation 48, population=20, best time=1312150.0 --\n", - "-- Generation 49, population=20, best time=1312150.0 --\n", - "Final time: 1312150.0\n", - "Generations processing took 4840.416193008423 ms\n", - "Evaluation time: 673.194408416748\n" - ] - }, - { - "data": { - "text/plain": "1891" - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "outputs": [], "source": [ "from sampo.scheduler.genetic.operators import DeadlineCostFitness\n", "\n", @@ -515,85 +360,16 @@ "schedule.execution_time" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "start_time": "2023-10-30T11:21:43.030352900Z" + } } }, { "cell_type": "code", - "execution_count": 17, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Can keep deadline at minimum resources\n", - "Genetic optimizing took 44.44313049316406 ms\n", - "Toolbox initialization & first population took 316.01643562316895 ms\n", - "First population evaluation took 551.9542694091797 ms\n", - "-- Generation 0, population=20, best time=1337.0 --\n", - "-- Generation 1, population=33, best time=1333.0 --\n", - "-- Generation 2, population=27, best time=1333.0 --\n", - "-- Generation 3, population=20, best time=1333.0 --\n", - "-- Generation 4, population=20, best time=1333.0 --\n", - "-- Generation 5, population=20, best time=1333.0 --\n", - "-- Generation 6, population=20, best time=1333.0 --\n", - "-- Generation 7, population=20, best time=1333.0 --\n", - "-- Generation 8, population=20, best time=1333.0 --\n", - "-- Generation 9, population=20, best time=1333.0 --\n", - "-- Generation 10, population=20, best time=1333.0 --\n", - "-- Generation 11, population=20, best time=1333.0 --\n", - "-- Generation 12, population=20, best time=1333.0 --\n", - "-- Generation 13, population=20, best time=1333.0 --\n", - "-- Generation 14, population=20, best time=1333.0 --\n", - "-- Generation 15, population=20, best time=1333.0 --\n", - "-- Generation 16, population=20, best time=1333.0 --\n", - "-- Generation 17, population=20, best time=1333.0 --\n", - "-- Generation 18, population=20, best time=1333.0 --\n", - "-- Generation 19, population=20, best time=1333.0 --\n", - "-- Generation 20, population=20, best time=1333.0 --\n", - "-- Generation 21, population=20, best time=1333.0 --\n", - "-- Generation 22, population=20, best time=1333.0 --\n", - "-- Generation 23, population=20, best time=1333.0 --\n", - "-- Generation 24, population=20, best time=1333.0 --\n", - "-- Generation 25, population=20, best time=1333.0 --\n", - "-- Generation 26, population=20, best time=1333.0 --\n", - "-- Generation 27, population=20, best time=1333.0 --\n", - "-- Generation 28, population=20, best time=1333.0 --\n", - "-- Generation 29, population=20, best time=1333.0 --\n", - "-- Generation 30, population=20, best time=1333.0 --\n", - "-- Generation 31, population=20, best time=1333.0 --\n", - "-- Generation 32, population=20, best time=1333.0 --\n", - "-- Generation 33, population=20, best time=1333.0 --\n", - "-- Generation 34, population=20, best time=1333.0 --\n", - "-- Generation 35, population=20, best time=1333.0 --\n", - "-- Generation 36, population=20, best time=1333.0 --\n", - "-- Generation 37, population=20, best time=1333.0 --\n", - "-- Generation 38, population=20, best time=1333.0 --\n", - "-- Generation 39, population=20, best time=1333.0 --\n", - "-- Generation 40, population=20, best time=1333.0 --\n", - "-- Generation 41, population=20, best time=1333.0 --\n", - "-- Generation 42, population=20, best time=1333.0 --\n", - "-- Generation 43, population=20, best time=1333.0 --\n", - "-- Generation 44, population=20, best time=1333.0 --\n", - "-- Generation 45, population=20, best time=1333.0 --\n", - "-- Generation 46, population=20, best time=1333.0 --\n", - "-- Generation 47, population=20, best time=1333.0 --\n", - "-- Generation 48, population=20, best time=1333.0 --\n", - "-- Generation 49, population=20, best time=1333.0 --\n", - "Final time: 1333.0\n", - "Generations processing took 5778.987169265747 ms\n", - "Evaluation time: 1231.072187423706\n" - ] - }, - { - "data": { - "text/plain": "989" - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "outputs": [], "source": [ "from sampo.scheduler.genetic.operators import TimeAndResourcesFitness\n", "\n", @@ -613,16 +389,22 @@ "schedule.execution_time" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "start_time": "2023-10-30T11:21:43.032349300Z" + } } }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "outputs": [], "source": [], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "start_time": "2023-10-30T11:21:43.034341900Z" + } } } ], diff --git a/sampo/schemas/project.py b/sampo/schemas/project.py new file mode 100644 index 00000000..6ebba9aa --- /dev/null +++ b/sampo/schemas/project.py @@ -0,0 +1,11 @@ +from sampo.schemas.contractor import Contractor +from sampo.schemas.graph import WorkGraph +from sampo.schemas.schedule import Schedule +from sampo.schemas.serializable import AutoJSONSerializable + + +class ScheduledProject(AutoJSONSerializable['ScheduledProject']): + def __init__(self, wg: WorkGraph, contractors: list[Contractor], schedule: Schedule): + self.schedule = schedule + self.wg = wg + self.contractors = contractors diff --git a/sampo/schemas/scheduled_work.py b/sampo/schemas/scheduled_work.py index 3aa1733e..e3b68f12 100644 --- a/sampo/schemas/scheduled_work.py +++ b/sampo/schemas/scheduled_work.py @@ -27,7 +27,7 @@ class ScheduledWork(AutoJSONSerializable['ScheduledWork']): * object - variable, that is used in landscape """ - ignored_fields = ['equipments', 'materials', 'object'] + ignored_fields = ['equipments', 'materials', 'object', 'work_unit'] def __init__(self, work_unit: WorkUnit, @@ -39,6 +39,7 @@ def __init__(self, zones_post: list[ZoneTransition] | None = None, materials: list[MaterialDelivery] | None = None, object: ConstructionObject | None = None): + self.id = work_unit.id self.work_unit = work_unit self.start_end_time = start_end_time self.workers = workers if workers is not None else [] From 752c4edc07d011596ce70a8b91acaa440ca78f3f Mon Sep 17 00:00:00 2001 From: Quarter Date: Tue, 31 Oct 2023 11:42:15 +0300 Subject: [PATCH 2/3] Fixed ScheduledProject serialization, updated all examples --- examples/generator_scenarios.py | 5 +- examples/local_optimization.ipynb | 141 ++++---- examples/scheduling_project.ipynb | 66 ++-- examples/simple_generation.ipynb | 325 +++++++++++++++--- examples/simple_synthetic_graph_scheduling.py | 9 +- examples/visualization.ipynb | 6 +- sampo/pipeline/default.py | 8 +- sampo/schemas/project.py | 10 + sampo/schemas/resources.py | 2 +- 9 files changed, 395 insertions(+), 177 deletions(-) diff --git a/examples/generator_scenarios.py b/examples/generator_scenarios.py index e72f82d5..f744590b 100644 --- a/examples/generator_scenarios.py +++ b/examples/generator_scenarios.py @@ -1,7 +1,10 @@ import random from itertools import chain -from sampo.generator import SimpleSynthetic, get_contractor_by_wg +from matplotlib import pyplot as plt + +from sampo.generator.base import SimpleSynthetic +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg from sampo.generator.pipeline.extension import extend_names, extend_resources from sampo.scheduler.heft.base import HEFTScheduler from sampo.schemas.graph import WorkGraph diff --git a/examples/local_optimization.ipynb b/examples/local_optimization.ipynb index 38aa69c1..0ffa03e3 100644 --- a/examples/local_optimization.ipynb +++ b/examples/local_optimization.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "start_time": "2023-07-01T21:15:24.697601Z", - "end_time": "2023-07-01T21:15:25.125812Z" + "end_time": "2023-10-31T08:06:06.215669600Z", + "start_time": "2023-10-31T08:06:05.535679400Z" } }, "outputs": [], @@ -53,8 +53,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "start_time": "2023-07-01T21:15:25.122811Z", - "end_time": "2023-07-01T21:15:25.154843Z" + "end_time": "2023-10-31T08:06:06.275585700Z", + "start_time": "2023-10-31T08:06:06.215669600Z" } } }, @@ -86,13 +86,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "Swapped 1 times!\n", - "Swapped 1 times!\n" + "Swapped 0 times!\n" ] }, { "data": { - "text/plain": "8661" + "text/plain": "1194" }, "execution_count": 3, "metadata": {}, @@ -104,20 +103,20 @@ "\n", "local_optimizer = SwapOrderLocalOptimizer()\n", "\n", - "schedule = SchedulingPipeline.create() \\\n", + "project = SchedulingPipeline.create() \\\n", " .wg(simple_wg) \\\n", " .contractors(contractors) \\\n", " .optimize_local(local_optimizer, range(0, 10)) \\\n", " .schedule(scheduler) \\\n", " .finish()\n", "\n", - "schedule.execution_time" + "project.schedule.execution_time" ], "metadata": { "collapsed": false, "ExecuteTime": { - "start_time": "2023-07-01T21:15:27.212547Z", - "end_time": "2023-07-01T21:15:27.796855Z" + "end_time": "2023-10-31T08:06:06.450877800Z", + "start_time": "2023-10-31T08:06:06.365998300Z" } } }, @@ -135,16 +134,9 @@ "cell_type": "code", "execution_count": 4, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found! finish of project finish of project\n" - ] - }, { "data": { - "text/plain": "8661" + "text/plain": "1194" }, "execution_count": 4, "metadata": {}, @@ -157,20 +149,20 @@ "\n", "local_optimizer = ParallelizeScheduleLocalOptimizer(JustInTimeTimeline)\n", "\n", - "schedule = SchedulingPipeline.create() \\\n", + "project = SchedulingPipeline.create() \\\n", " .wg(simple_wg) \\\n", " .contractors(contractors) \\\n", " .schedule(scheduler) \\\n", " .optimize_local(local_optimizer, range(0, 5)) \\\n", " .finish()\n", "\n", - "schedule.execution_time" + "project.schedule.execution_time" ], "metadata": { "collapsed": false, "ExecuteTime": { - "start_time": "2023-07-01T21:15:28.199018Z", - "end_time": "2023-07-01T21:15:28.861640Z" + "end_time": "2023-10-31T08:06:06.635515100Z", + "start_time": "2023-10-31T08:06:06.465560600Z" } } }, @@ -193,18 +185,16 @@ "name": "stdout", "output_type": "stream", "text": [ - "Swapped 27 times!\n", - "Swapped 33 times!\n", - "Swapped 13 times!\n", - "Swapped 14 times!\n", + "Swapped 26 times!\n", + "Swapped 32 times!\n", "Found! temporary road engineering preparation\n", - "Found! finish of project elem of pipe_network\n", - "Found! finish of project elem of pipe_network\n", - "Found! finish of project elem of pipe_network\n", - "Found! finish of project elem of pipe_network\n", + "Found! final road elem of pipe_network\n", + "Found! final road elem of pipe_network\n", + "Found! final road elem of pipe_network\n", + "Found! final road elem of pipe_network\n", "Found! pipe drainage tank\n", "Found! pipe KTP and NEP\n", - "Found! finish of project high power line\n", + "Found! final road high power line\n", "Found! pipe block local automation\n", "Found! pipe metering installation\n", "Found! pipe block dosage inhibitor\n", @@ -214,41 +204,39 @@ "Found! pipe block water distribution\n", "Found! pipe drainage tank\n", "Found! pipe firewall tank\n", - "Found! finish of project elem of pipe_network\n", - "Found! finish of project elem of pipe_network\n", - "Found! finish of project elem of pipe_network\n", - "Found! finish of project mast\n", - "Found! finish of project mast\n", - "Found! finish of project power network\n", - "Found! finish of project pipe\n", - "Found! finish of project pipe\n", - "Found! finish of project power line\n", - "Found! finish of project power line\n", - "Found! finish of project pipe\n", - "Found! finish of project looping\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project borehole\n", - "Found! finish of project node\n", - "Found! finish of project node\n", - "Found! finish of project node\n", - "Found! finish of project final road\n", - "Found! cluster handing finish of project\n", + "Found! final road elem of pipe_network\n", + "Found! final road elem of pipe_network\n", + "Found! final road elem of pipe_network\n", + "Found! final road mast\n", + "Found! final road mast\n", + "Found! final road power network\n", + "Found! final road pipe\n", + "Found! final road pipe\n", + "Found! final road power line\n", + "Found! final road power line\n", + "Found! final road pipe\n", + "Found! final road looping\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road borehole\n", + "Found! final road node\n", + "Found! final road node\n", + "Found! final road node\n", "Found! engineering preparation temporary road\n", "Found! final road engineering preparation\n", "Found! node final road\n", @@ -273,7 +261,6 @@ "Found! node elem of pipe_network\n", "Found! node elem of pipe_network\n", "Found! node elem of pipe_network\n", - "Found! node elem of pipe_network\n", "Found! node mast\n", "Found! node mast\n", "Found! node mast\n", @@ -319,7 +306,7 @@ }, { "data": { - "text/plain": "8661" + "text/plain": "1240" }, "execution_count": 5, "metadata": {}, @@ -332,7 +319,7 @@ "order_optimizer = SwapOrderLocalOptimizer()\n", "schedule_optimizer = ParallelizeScheduleLocalOptimizer(JustInTimeTimeline)\n", "\n", - "schedule = SchedulingPipeline.create() \\\n", + "project = SchedulingPipeline.create() \\\n", " .wg(simple_wg) \\\n", " .contractors(contractors) \\\n", " .optimize_local(order_optimizer, range(0, simple_wg.vertex_count // 2)) \\\n", @@ -342,23 +329,27 @@ " .optimize_local(schedule_optimizer, range(simple_wg.vertex_count // 2, simple_wg.vertex_count)) \\\n", " .finish()\n", "\n", - "schedule.execution_time" + "project.schedule.execution_time" ], "metadata": { "collapsed": false, "ExecuteTime": { - "start_time": "2023-07-01T21:15:30.679374Z", - "end_time": "2023-07-01T21:15:31.396371Z" + "end_time": "2023-10-31T08:06:06.860829700Z", + "start_time": "2023-10-31T08:06:06.690476Z" } } }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "outputs": [], "source": [], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-10-31T08:06:06.870912400Z", + "start_time": "2023-10-31T08:06:06.855820800Z" + } } } ], diff --git a/examples/scheduling_project.ipynb b/examples/scheduling_project.ipynb index a1be8b18..9dd45ae6 100644 --- a/examples/scheduling_project.ipynb +++ b/examples/scheduling_project.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2023-10-30T11:58:15.178958500Z", - "start_time": "2023-10-30T11:58:14.729575400Z" + "end_time": "2023-10-31T07:27:22.658921Z", + "start_time": "2023-10-31T07:27:22.164401500Z" } }, "outputs": [], @@ -38,8 +38,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:58:15.199062800Z", - "start_time": "2023-10-30T11:58:15.178958500Z" + "end_time": "2023-10-31T07:27:22.663942900Z", + "start_time": "2023-10-31T07:27:22.658921Z" } } }, @@ -59,8 +59,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:58:15.239450600Z", - "start_time": "2023-10-30T11:58:15.204103700Z" + "end_time": "2023-10-31T07:27:22.703949100Z", + "start_time": "2023-10-31T07:27:22.663942900Z" } } }, @@ -79,8 +79,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:58:22.218955300Z", - "start_time": "2023-10-30T11:58:15.239450600Z" + "end_time": "2023-10-31T07:27:29.632916Z", + "start_time": "2023-10-31T07:27:22.708956400Z" } } }, @@ -105,8 +105,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:58:22.239236400Z", - "start_time": "2023-10-30T11:58:22.218955300Z" + "end_time": "2023-10-31T07:27:29.651210500Z", + "start_time": "2023-10-31T07:27:29.633954Z" } } }, @@ -134,8 +134,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:58:22.259121400Z", - "start_time": "2023-10-30T11:58:22.239236400Z" + "end_time": "2023-10-31T07:27:29.674082Z", + "start_time": "2023-10-31T07:27:29.653754100Z" } } }, @@ -160,8 +160,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:58:22.279192200Z", - "start_time": "2023-10-30T11:58:22.259121400Z" + "end_time": "2023-10-31T07:27:29.694269700Z", + "start_time": "2023-10-31T07:27:29.674082Z" } } }, @@ -199,8 +199,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:58:22.299194700Z", - "start_time": "2023-10-30T11:58:22.274152200Z" + "end_time": "2023-10-31T07:27:29.733816500Z", + "start_time": "2023-10-31T07:27:29.694269700Z" } } }, @@ -243,54 +243,36 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:58:22.599186200Z", - "start_time": "2023-10-30T11:58:22.299194700Z" + "end_time": "2023-10-31T07:27:30.018935700Z", + "start_time": "2023-10-31T07:27:29.733816500Z" } } }, { "cell_type": "code", "execution_count": 10, - "outputs": [ - { - "ename": "TypeError", - "evalue": "Object of type Contractor is not JSON serializable", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[1;32mIn[10], line 1\u001B[0m\n\u001B[1;32m----> 1\u001B[0m \u001B[43mproject\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdump\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43m.\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43mproject_test\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m)\u001B[49m\n", - "File \u001B[1;32m~\\PycharmProjects\\sampo\\sampo\\schemas\\serializable.py:222\u001B[0m, in \u001B[0;36mJSONSerializable.dump\u001B[1;34m(self, folder_path, file_name)\u001B[0m\n\u001B[0;32m 220\u001B[0m serialized_dict \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_serialize()\n\u001B[0;32m 221\u001B[0m \u001B[38;5;28;01mwith\u001B[39;00m \u001B[38;5;28mopen\u001B[39m(full_file_name, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mw\u001B[39m\u001B[38;5;124m'\u001B[39m, encoding\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mutf-8\u001B[39m\u001B[38;5;124m'\u001B[39m) \u001B[38;5;28;01mas\u001B[39;00m write_file:\n\u001B[1;32m--> 222\u001B[0m \u001B[43mjson\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdump\u001B[49m\u001B[43m(\u001B[49m\u001B[43mserialized_dict\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mwrite_file\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[1;32m~\\AppData\\Local\\Programs\\Python\\Python310\\lib\\json\\__init__.py:179\u001B[0m, in \u001B[0;36mdump\u001B[1;34m(obj, fp, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)\u001B[0m\n\u001B[0;32m 173\u001B[0m iterable \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mcls\u001B[39m(skipkeys\u001B[38;5;241m=\u001B[39mskipkeys, ensure_ascii\u001B[38;5;241m=\u001B[39mensure_ascii,\n\u001B[0;32m 174\u001B[0m check_circular\u001B[38;5;241m=\u001B[39mcheck_circular, allow_nan\u001B[38;5;241m=\u001B[39mallow_nan, indent\u001B[38;5;241m=\u001B[39mindent,\n\u001B[0;32m 175\u001B[0m separators\u001B[38;5;241m=\u001B[39mseparators,\n\u001B[0;32m 176\u001B[0m default\u001B[38;5;241m=\u001B[39mdefault, sort_keys\u001B[38;5;241m=\u001B[39msort_keys, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkw)\u001B[38;5;241m.\u001B[39miterencode(obj)\n\u001B[0;32m 177\u001B[0m \u001B[38;5;66;03m# could accelerate with writelines in some versions of Python, at\u001B[39;00m\n\u001B[0;32m 178\u001B[0m \u001B[38;5;66;03m# a debuggability cost\u001B[39;00m\n\u001B[1;32m--> 179\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m chunk \u001B[38;5;129;01min\u001B[39;00m iterable:\n\u001B[0;32m 180\u001B[0m fp\u001B[38;5;241m.\u001B[39mwrite(chunk)\n", - "File \u001B[1;32m~\\AppData\\Local\\Programs\\Python\\Python310\\lib\\json\\encoder.py:431\u001B[0m, in \u001B[0;36m_make_iterencode.._iterencode\u001B[1;34m(o, _current_indent_level)\u001B[0m\n\u001B[0;32m 429\u001B[0m \u001B[38;5;28;01myield from\u001B[39;00m _iterencode_list(o, _current_indent_level)\n\u001B[0;32m 430\u001B[0m \u001B[38;5;28;01melif\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(o, \u001B[38;5;28mdict\u001B[39m):\n\u001B[1;32m--> 431\u001B[0m \u001B[38;5;28;01myield from\u001B[39;00m _iterencode_dict(o, _current_indent_level)\n\u001B[0;32m 432\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[0;32m 433\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m markers \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n", - "File \u001B[1;32m~\\AppData\\Local\\Programs\\Python\\Python310\\lib\\json\\encoder.py:405\u001B[0m, in \u001B[0;36m_make_iterencode.._iterencode_dict\u001B[1;34m(dct, _current_indent_level)\u001B[0m\n\u001B[0;32m 403\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[0;32m 404\u001B[0m chunks \u001B[38;5;241m=\u001B[39m _iterencode(value, _current_indent_level)\n\u001B[1;32m--> 405\u001B[0m \u001B[38;5;28;01myield from\u001B[39;00m chunks\n\u001B[0;32m 406\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m newline_indent \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[0;32m 407\u001B[0m _current_indent_level \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[38;5;241m1\u001B[39m\n", - "File \u001B[1;32m~\\AppData\\Local\\Programs\\Python\\Python310\\lib\\json\\encoder.py:325\u001B[0m, in \u001B[0;36m_make_iterencode.._iterencode_list\u001B[1;34m(lst, _current_indent_level)\u001B[0m\n\u001B[0;32m 323\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[0;32m 324\u001B[0m chunks \u001B[38;5;241m=\u001B[39m _iterencode(value, _current_indent_level)\n\u001B[1;32m--> 325\u001B[0m \u001B[38;5;28;01myield from\u001B[39;00m chunks\n\u001B[0;32m 326\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m newline_indent \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[0;32m 327\u001B[0m _current_indent_level \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[38;5;241m1\u001B[39m\n", - "File \u001B[1;32m~\\AppData\\Local\\Programs\\Python\\Python310\\lib\\json\\encoder.py:438\u001B[0m, in \u001B[0;36m_make_iterencode.._iterencode\u001B[1;34m(o, _current_indent_level)\u001B[0m\n\u001B[0;32m 436\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mValueError\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mCircular reference detected\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m 437\u001B[0m markers[markerid] \u001B[38;5;241m=\u001B[39m o\n\u001B[1;32m--> 438\u001B[0m o \u001B[38;5;241m=\u001B[39m \u001B[43m_default\u001B[49m\u001B[43m(\u001B[49m\u001B[43mo\u001B[49m\u001B[43m)\u001B[49m\n\u001B[0;32m 439\u001B[0m \u001B[38;5;28;01myield from\u001B[39;00m _iterencode(o, _current_indent_level)\n\u001B[0;32m 440\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m markers \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n", - "File \u001B[1;32m~\\AppData\\Local\\Programs\\Python\\Python310\\lib\\json\\encoder.py:179\u001B[0m, in \u001B[0;36mJSONEncoder.default\u001B[1;34m(self, o)\u001B[0m\n\u001B[0;32m 160\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mdefault\u001B[39m(\u001B[38;5;28mself\u001B[39m, o):\n\u001B[0;32m 161\u001B[0m \u001B[38;5;250m \u001B[39m\u001B[38;5;124;03m\"\"\"Implement this method in a subclass such that it returns\u001B[39;00m\n\u001B[0;32m 162\u001B[0m \u001B[38;5;124;03m a serializable object for ``o``, or calls the base implementation\u001B[39;00m\n\u001B[0;32m 163\u001B[0m \u001B[38;5;124;03m (to raise a ``TypeError``).\u001B[39;00m\n\u001B[1;32m (...)\u001B[0m\n\u001B[0;32m 177\u001B[0m \n\u001B[0;32m 178\u001B[0m \u001B[38;5;124;03m \"\"\"\u001B[39;00m\n\u001B[1;32m--> 179\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mTypeError\u001B[39;00m(\u001B[38;5;124mf\u001B[39m\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mObject of type \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mo\u001B[38;5;241m.\u001B[39m\u001B[38;5;18m__class__\u001B[39m\u001B[38;5;241m.\u001B[39m\u001B[38;5;18m__name__\u001B[39m\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m \u001B[39m\u001B[38;5;124m'\u001B[39m\n\u001B[0;32m 180\u001B[0m \u001B[38;5;124mf\u001B[39m\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mis not JSON serializable\u001B[39m\u001B[38;5;124m'\u001B[39m)\n", - "\u001B[1;31mTypeError\u001B[0m: Object of type Contractor is not JSON serializable" - ] - } - ], + "outputs": [], "source": [ "project.dump('.', 'project_test')" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:58:23.386360200Z", - "start_time": "2023-10-30T11:58:22.619154Z" + "end_time": "2023-10-31T07:27:30.233814800Z", + "start_time": "2023-10-31T07:27:30.113622300Z" } } }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "outputs": [], "source": [], "metadata": { "collapsed": false, "ExecuteTime": { - "start_time": "2023-10-30T11:58:23.386360200Z" + "end_time": "2023-10-31T07:27:30.254027100Z", + "start_time": "2023-10-31T07:27:30.233814800Z" } } } diff --git a/examples/simple_generation.ipynb b/examples/simple_generation.ipynb index d7dcf607..ba977213 100644 --- a/examples/simple_generation.ipynb +++ b/examples/simple_generation.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2023-10-30T11:21:31.055771800Z", - "start_time": "2023-10-30T11:21:29.736888300Z" + "end_time": "2023-10-31T08:17:22.640827Z", + "start_time": "2023-10-31T08:17:22.161108400Z" } }, "outputs": [], @@ -38,8 +38,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:21:31.069733100Z", - "start_time": "2023-10-30T11:21:31.055771800Z" + "end_time": "2023-10-31T08:17:22.663581500Z", + "start_time": "2023-10-31T08:17:22.640827Z" } } }, @@ -59,8 +59,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:21:31.107631500Z", - "start_time": "2023-10-30T11:21:31.083698500Z" + "end_time": "2023-10-31T08:17:22.701082400Z", + "start_time": "2023-10-31T08:17:22.681145200Z" } } }, @@ -79,8 +79,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:21:38.124598200Z", - "start_time": "2023-10-30T11:21:31.102645500Z" + "end_time": "2023-10-31T08:17:29.710850800Z", + "start_time": "2023-10-31T08:17:22.701082400Z" } } }, @@ -105,8 +105,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:21:38.153209700Z", - "start_time": "2023-10-30T11:21:38.123625400Z" + "end_time": "2023-10-31T08:17:29.731148Z", + "start_time": "2023-10-31T08:17:29.710850800Z" } } }, @@ -134,8 +134,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:21:38.162187800Z", - "start_time": "2023-10-30T11:21:38.141241900Z" + "end_time": "2023-10-31T08:17:29.751365800Z", + "start_time": "2023-10-31T08:17:29.731148Z" } } }, @@ -160,8 +160,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:21:38.175487800Z", - "start_time": "2023-10-30T11:21:38.156204500Z" + "end_time": "2023-10-31T08:17:29.770932Z", + "start_time": "2023-10-31T08:17:29.751365800Z" } } }, @@ -199,8 +199,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:21:38.248502Z", - "start_time": "2023-10-30T11:21:38.171500500Z" + "end_time": "2023-10-31T08:17:29.831002900Z", + "start_time": "2023-10-31T08:17:29.770932Z" } } }, @@ -226,8 +226,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:21:38.326329400Z", - "start_time": "2023-10-30T11:21:38.249500400Z" + "end_time": "2023-10-31T08:17:29.851237100Z", + "start_time": "2023-10-31T08:17:29.811172300Z" } } }, @@ -251,35 +251,90 @@ "name": "stdout", "output_type": "stream", "text": [ - "Genetic optimizing took 32.912254333496094 ms\n" + "Genetic optimizing took 30.315876007080078 ms\n", + "Toolbox initialization & first population took 5215.619802474976 ms\n", + "First population evaluation took 4028.813600540161 ms\n", + "-- Generation 1, population=50, best fitness=1120.0 --\n", + "-- Generation 2, population=50, best fitness=1068.0 --\n", + "-- Generation 3, population=50, best fitness=1016.0 --\n", + "-- Generation 4, population=50, best fitness=1016.0 --\n", + "-- Generation 5, population=50, best fitness=996.0 --\n", + "-- Generation 6, population=50, best fitness=996.0 --\n", + "-- Generation 7, population=50, best fitness=996.0 --\n", + "-- Generation 8, population=50, best fitness=996.0 --\n", + "-- Generation 9, population=50, best fitness=996.0 --\n", + "-- Generation 10, population=50, best fitness=996.0 --\n", + "-- Generation 11, population=50, best fitness=996.0 --\n", + "-- Generation 12, population=50, best fitness=996.0 --\n", + "-- Generation 13, population=50, best fitness=996.0 --\n", + "-- Generation 14, population=50, best fitness=979.0 --\n", + "-- Generation 15, population=50, best fitness=979.0 --\n", + "-- Generation 16, population=50, best fitness=979.0 --\n", + "-- Generation 17, population=50, best fitness=972.0 --\n", + "-- Generation 18, population=50, best fitness=972.0 --\n", + "-- Generation 19, population=50, best fitness=972.0 --\n", + "-- Generation 20, population=50, best fitness=972.0 --\n", + "-- Generation 21, population=50, best fitness=972.0 --\n", + "-- Generation 22, population=50, best fitness=972.0 --\n", + "-- Generation 23, population=50, best fitness=972.0 --\n", + "-- Generation 24, population=50, best fitness=972.0 --\n", + "-- Generation 25, population=50, best fitness=972.0 --\n", + "-- Generation 26, population=50, best fitness=970.0 --\n", + "-- Generation 27, population=50, best fitness=970.0 --\n", + "-- Generation 28, population=50, best fitness=970.0 --\n", + "-- Generation 29, population=50, best fitness=970.0 --\n", + "-- Generation 30, population=50, best fitness=970.0 --\n", + "-- Generation 31, population=50, best fitness=970.0 --\n", + "-- Generation 32, population=50, best fitness=970.0 --\n", + "-- Generation 33, population=50, best fitness=964.0 --\n", + "-- Generation 34, population=50, best fitness=964.0 --\n", + "-- Generation 35, population=50, best fitness=964.0 --\n", + "-- Generation 36, population=50, best fitness=964.0 --\n", + "-- Generation 37, population=50, best fitness=964.0 --\n", + "-- Generation 38, population=50, best fitness=964.0 --\n", + "-- Generation 39, population=50, best fitness=964.0 --\n", + "-- Generation 40, population=50, best fitness=964.0 --\n", + "-- Generation 41, population=50, best fitness=964.0 --\n", + "-- Generation 42, population=50, best fitness=964.0 --\n", + "-- Generation 43, population=50, best fitness=964.0 --\n", + "-- Generation 44, population=50, best fitness=964.0 --\n", + "-- Generation 45, population=50, best fitness=964.0 --\n", + "-- Generation 46, population=50, best fitness=964.0 --\n", + "-- Generation 47, population=50, best fitness=964.0 --\n", + "-- Generation 48, population=50, best fitness=964.0 --\n", + "-- Generation 49, population=50, best fitness=964.0 --\n", + "-- Generation 50, population=50, best fitness=964.0 --\n", + "Final time: 951.0\n", + "Generations processing took 213235.8374595642 ms\n", + "Full genetic processing took 222483.2377433777 ms\n", + "Evaluation time: 204837.13102340698\n" ] }, { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "KeyboardInterrupt\n", - "\n" - ] + "data": { + "text/plain": "951" + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ "from sampo.pipeline import SchedulingPipeline\n", "\n", - "schedule = SchedulingPipeline.create() \\\n", + "project = SchedulingPipeline.create() \\\n", " .wg(simple_wg) \\\n", " .contractors(contractors) \\\n", " .schedule(scheduler) \\\n", " .finish()\n", "\n", - "schedule.execution_time" + "project.schedule.execution_time" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-30T11:21:43.035314100Z", - "start_time": "2023-10-30T11:21:38.328324600Z" + "end_time": "2023-10-31T08:21:13.881802200Z", + "start_time": "2023-10-31T08:17:29.851237100Z" } } }, @@ -298,8 +353,68 @@ }, { "cell_type": "code", - "execution_count": null, - "outputs": [], + "execution_count": 11, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Genetic optimizing took 32.92560577392578 ms\n", + "Toolbox initialization & first population took 5090.832233428955 ms\n", + "First population evaluation took 4190.772771835327 ms\n", + "-- Generation 1, population=50, best fitness=334.0 --\n", + "-- Generation 1, population=50, best peak=334.0 --\n", + "-- Generation 2, population=50, best peak=243.0 --\n", + "-- Generation 3, population=50, best peak=243.0 --\n", + "-- Generation 4, population=50, best peak=243.0 --\n", + "-- Generation 5, population=50, best peak=243.0 --\n", + "-- Generation 6, population=50, best peak=243.0 --\n", + "-- Generation 7, population=50, best peak=243.0 --\n", + "-- Generation 8, population=50, best peak=243.0 --\n", + "-- Generation 9, population=50, best peak=243.0 --\n", + "-- Generation 10, population=50, best peak=243.0 --\n", + "-- Generation 11, population=50, best peak=243.0 --\n", + "-- Generation 12, population=50, best peak=243.0 --\n", + "-- Generation 13, population=50, best peak=229.0 --\n", + "-- Generation 14, population=50, best peak=229.0 --\n", + "-- Generation 15, population=50, best peak=229.0 --\n", + "-- Generation 16, population=50, best peak=229.0 --\n", + "-- Generation 17, population=50, best peak=229.0 --\n", + "-- Generation 18, population=50, best peak=229.0 --\n", + "-- Generation 19, population=50, best peak=229.0 --\n", + "-- Generation 20, population=50, best peak=229.0 --\n", + "-- Generation 21, population=50, best peak=229.0 --\n", + "-- Generation 22, population=50, best peak=229.0 --\n", + "-- Generation 23, population=50, best peak=229.0 --\n", + "-- Generation 24, population=50, best peak=229.0 --\n", + "-- Generation 25, population=50, best peak=229.0 --\n", + "-- Generation 26, population=50, best peak=229.0 --\n", + "-- Generation 27, population=50, best peak=229.0 --\n", + "-- Generation 28, population=50, best peak=229.0 --\n", + "-- Generation 29, population=50, best peak=229.0 --\n", + "-- Generation 30, population=50, best peak=229.0 --\n", + "-- Generation 31, population=50, best peak=229.0 --\n", + "-- Generation 32, population=50, best peak=229.0 --\n", + "-- Generation 33, population=50, best peak=229.0 --\n", + "-- Generation 34, population=50, best peak=229.0 --\n", + "-- Generation 35, population=50, best peak=229.0 --\n", + "-- Generation 36, population=50, best peak=229.0 --\n", + "-- Generation 37, population=50, best peak=229.0 --\n", + "Final time: 229.0\n", + "Generations processing took 339735.8045578003 ms\n", + "Full genetic processing took 349020.9403038025 ms\n", + "Evaluation time: 334693.89057159424\n" + ] + }, + { + "data": { + "text/plain": "1756" + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "from sampo.schemas.time import Time\n", "from sampo.scheduler.genetic.operators import DeadlineResourcesFitness\n", @@ -313,18 +428,19 @@ " fitness_constructor=fitness_constructor)\n", "scheduler.set_deadline(deadline)\n", "\n", - "schedule = SchedulingPipeline.create() \\\n", + "project = SchedulingPipeline.create() \\\n", " .wg(simple_wg) \\\n", " .contractors(contractors) \\\n", " .schedule(scheduler) \\\n", " .finish()\n", "\n", - "schedule.execution_time" + "project.schedule.execution_time" ], "metadata": { "collapsed": false, "ExecuteTime": { - "start_time": "2023-10-30T11:21:43.028359300Z" + "end_time": "2023-10-31T08:27:06.272880400Z", + "start_time": "2023-10-31T08:21:13.896735100Z" } } }, @@ -339,8 +455,43 @@ }, { "cell_type": "code", - "execution_count": null, - "outputs": [], + "execution_count": 12, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Genetic optimizing took 32.875776290893555 ms\n", + "Toolbox initialization & first population took 5288.079023361206 ms\n", + "First population evaluation took 4078.0670642852783 ms\n", + "-- Generation 1, population=50, best fitness=1375560.0 --\n", + "-- Generation 2, population=50, best fitness=1375560.0 --\n", + "-- Generation 3, population=50, best fitness=1375560.0 --\n", + "-- Generation 4, population=50, best fitness=1375560.0 --\n", + "-- Generation 5, population=50, best fitness=1375560.0 --\n", + "-- Generation 6, population=50, best fitness=1375560.0 --\n", + "-- Generation 7, population=50, best fitness=1375560.0 --\n", + "-- Generation 8, population=50, best fitness=1375560.0 --\n", + "-- Generation 9, population=50, best fitness=1375560.0 --\n", + "-- Generation 10, population=50, best fitness=1375560.0 --\n", + "-- Generation 11, population=50, best fitness=1375560.0 --\n", + "-- Generation 12, population=50, best fitness=1375560.0 --\n", + "Deadline not reached !!! Deadline 2000 < best time 1375560.0\n", + "Final time: 1375560.0\n", + "Generations processing took 49289.43705558777 ms\n", + "Full genetic processing took 58659.178256988525 ms\n", + "Evaluation time: 50605.92722892761\n" + ] + }, + { + "data": { + "text/plain": "1737" + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "from sampo.scheduler.genetic.operators import DeadlineCostFitness\n", "\n", @@ -351,59 +502,135 @@ " fitness_constructor=fitness_constructor)\n", "scheduler.set_deadline(deadline)\n", "\n", - "schedule = SchedulingPipeline.create() \\\n", + "project = SchedulingPipeline.create() \\\n", " .wg(simple_wg) \\\n", " .contractors(contractors) \\\n", " .schedule(scheduler) \\\n", " .finish()\n", "\n", - "schedule.execution_time" + "project.schedule.execution_time" ], "metadata": { "collapsed": false, "ExecuteTime": { - "start_time": "2023-10-30T11:21:43.030352900Z" + "end_time": "2023-10-31T08:28:08.257361900Z", + "start_time": "2023-10-31T08:27:06.276925900Z" } } }, { "cell_type": "code", - "execution_count": null, - "outputs": [], + "execution_count": 13, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Genetic optimizing took 30.807018280029297 ms\n", + "Toolbox initialization & first population took 4955.848932266235 ms\n", + "First population evaluation took 4028.3138751983643 ms\n", + "-- Generation 1, population=50, best fitness=1523.0 --\n", + "-- Generation 1, population=50, best peak=341.0 --\n", + "-- Generation 2, population=50, best peak=307.0 --\n", + "-- Generation 3, population=50, best peak=279.0 --\n", + "-- Generation 4, population=50, best peak=269.0 --\n", + "-- Generation 5, population=50, best peak=269.0 --\n", + "-- Generation 6, population=50, best peak=269.0 --\n", + "-- Generation 7, population=50, best peak=269.0 --\n", + "-- Generation 8, population=50, best peak=269.0 --\n", + "-- Generation 9, population=50, best peak=269.0 --\n", + "-- Generation 10, population=50, best peak=269.0 --\n", + "-- Generation 11, population=50, best peak=269.0 --\n", + "-- Generation 12, population=50, best peak=269.0 --\n", + "-- Generation 13, population=50, best peak=269.0 --\n", + "-- Generation 14, population=50, best peak=269.0 --\n", + "-- Generation 15, population=50, best peak=269.0 --\n", + "-- Generation 16, population=50, best peak=269.0 --\n", + "-- Generation 17, population=50, best peak=269.0 --\n", + "-- Generation 18, population=50, best peak=263.0 --\n", + "-- Generation 19, population=50, best peak=263.0 --\n", + "-- Generation 20, population=50, best peak=263.0 --\n", + "-- Generation 21, population=50, best peak=263.0 --\n", + "-- Generation 22, population=50, best peak=263.0 --\n", + "-- Generation 23, population=50, best peak=263.0 --\n", + "-- Generation 24, population=50, best peak=263.0 --\n", + "-- Generation 25, population=50, best peak=263.0 --\n", + "-- Generation 26, population=50, best peak=263.0 --\n", + "-- Generation 27, population=50, best peak=263.0 --\n", + "-- Generation 28, population=50, best peak=263.0 --\n", + "-- Generation 29, population=50, best peak=263.0 --\n", + "-- Generation 30, population=50, best peak=263.0 --\n", + "-- Generation 31, population=50, best peak=263.0 --\n", + "-- Generation 32, population=50, best peak=263.0 --\n", + "-- Generation 33, population=50, best peak=263.0 --\n", + "-- Generation 34, population=50, best peak=263.0 --\n", + "-- Generation 35, population=50, best peak=263.0 --\n", + "-- Generation 36, population=50, best peak=263.0 --\n", + "-- Generation 37, population=50, best peak=263.0 --\n", + "-- Generation 38, population=50, best peak=251.0 --\n", + "-- Generation 39, population=50, best peak=251.0 --\n", + "-- Generation 40, population=50, best peak=251.0 --\n", + "-- Generation 41, population=50, best peak=251.0 --\n", + "-- Generation 42, population=50, best peak=251.0 --\n", + "-- Generation 43, population=50, best peak=251.0 --\n", + "-- Generation 44, population=50, best peak=251.0 --\n", + "-- Generation 45, population=50, best peak=251.0 --\n", + "-- Generation 46, population=50, best peak=251.0 --\n", + "-- Generation 47, population=50, best peak=251.0 --\n", + "-- Generation 48, population=50, best peak=251.0 --\n", + "-- Generation 49, population=50, best peak=251.0 --\n", + "-- Generation 50, population=50, best peak=251.0 --\n", + "Final time: 251.0\n", + "Generations processing took 420072.54123687744 ms\n", + "Full genetic processing took 429056.70404434204 ms\n", + "Evaluation time: 411582.2513103485\n" + ] + }, + { + "data": { + "text/plain": "1441" + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "from sampo.scheduler.genetic.operators import TimeAndResourcesFitness\n", + "from sampo.scheduler.genetic.operators import TimeWithResourcesFitness\n", "\n", - "fitness_constructor = TimeAndResourcesFitness\n", + "fitness_constructor = TimeWithResourcesFitness\n", "\n", "scheduler = GeneticScheduler(mutate_order=0.1,\n", " mutate_resources=0.3,\n", " fitness_constructor=fitness_constructor)\n", "scheduler.set_deadline(deadline)\n", "\n", - "schedule = SchedulingPipeline.create() \\\n", + "project = SchedulingPipeline.create() \\\n", " .wg(simple_wg) \\\n", " .contractors(contractors) \\\n", " .schedule(scheduler) \\\n", " .finish()\n", "\n", - "schedule.execution_time" + "project.schedule.execution_time" ], "metadata": { "collapsed": false, "ExecuteTime": { - "start_time": "2023-10-30T11:21:43.032349300Z" + "end_time": "2023-10-31T08:35:20.615297500Z", + "start_time": "2023-10-31T08:28:08.257361900Z" } } }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "outputs": [], "source": [], "metadata": { "collapsed": false, "ExecuteTime": { - "start_time": "2023-10-30T11:21:43.034341900Z" + "end_time": "2023-10-31T08:35:20.674007700Z", + "start_time": "2023-10-31T08:35:20.615367400Z" } } } diff --git a/examples/simple_synthetic_graph_scheduling.py b/examples/simple_synthetic_graph_scheduling.py index 50889fc5..7a52e64d 100644 --- a/examples/simple_synthetic_graph_scheduling.py +++ b/examples/simple_synthetic_graph_scheduling.py @@ -1,12 +1,13 @@ from itertools import chain +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg from sampo.utilities.visualization.base import VisualizationMode from sampo.utilities.visualization.schedule import schedule_gant_chart_fig from sampo.utilities.schedule import remove_service_tasks -from sampo.generator import SimpleSynthetic, get_contractor_by_wg +from sampo.generator.base import SimpleSynthetic from sampo.scheduler.heft.base import HEFTScheduler from sampo.schemas.time import Time @@ -16,7 +17,7 @@ synth_resources = 100 # Set up scheduling algorithm and project's start date -scheduler_type = HEFTScheduler() +scheduler = HEFTScheduler() start_date = "2023-01-01" # Set up visualization mode (ShowFig or SaveFig) and the gant chart file's name (if SaveFig mode is chosen) @@ -45,7 +46,7 @@ contractors = [get_contractor_by_wg(wg)] # Schedule works -schedule = scheduler_type.schedule(wg, contractors) +schedule = scheduler.schedule(wg, contractors) schedule_df = remove_service_tasks(schedule.merged_stages_datetime_df(start_date)) # Schedule's gant chart visualization gant_fig = schedule_gant_chart_fig(schedule_df, @@ -55,5 +56,5 @@ print(schedule.execution_time) # Check the validity of the schedule's time -assert schedule.execution_time != Time.inf(), f'Scheduling failed on {scheduler_type.name}' +assert schedule.execution_time != Time.inf(), f'Scheduling failed on {scheduler.scheduler_type.name}' diff --git a/examples/visualization.ipynb b/examples/visualization.ipynb index 1dda0870..9cd6221b 100644 --- a/examples/visualization.ipynb +++ b/examples/visualization.ipynb @@ -33,11 +33,13 @@ "\n", "scheduler = HEFTScheduler()\n", "\n", - "schedule = SchedulingPipeline.create() \\\n", + "project = SchedulingPipeline.create() \\\n", " .wg(simple_wg) \\\n", " .contractors(contractors) \\\n", " .schedule(scheduler) \\\n", - " .finish()" + " .finish()\n", + "\n", + "schedule = project.schedule" ], "metadata": { "collapsed": false diff --git a/sampo/pipeline/default.py b/sampo/pipeline/default.py index 6c95a95a..b92a5596 100644 --- a/sampo/pipeline/default.py +++ b/sampo/pipeline/default.py @@ -9,6 +9,7 @@ from sampo.schemas.exceptions import NoSufficientContractorError from sampo.schemas.graph import WorkGraph, GraphNode from sampo.schemas.landscape import LandscapeConfiguration +from sampo.schemas.project import ScheduledProject from sampo.schemas.schedule import Schedule from sampo.schemas.schedule_spec import ScheduleSpec from sampo.schemas.time import Time @@ -246,7 +247,7 @@ def __init__(self, s_input: DefaultInputPipeline, wg: WorkGraph, schedule: Sched self._worker_pool = get_worker_contractor_pool(s_input._contractors) self._schedule = schedule self._scheduled_works = {wg[swork.work_unit.id]: - swork for swork in schedule.to_schedule_work_dict.values()} + swork for swork in schedule.to_schedule_work_dict.values()} self._local_optimize_stack = ApplyQueue() def optimize_local(self, optimizer: ScheduleLocalOptimizer, area: range) -> 'SchedulePipeline': @@ -258,6 +259,7 @@ def optimize_local(self, optimizer: ScheduleLocalOptimizer, area: range) -> 'Sch self._input._assigned_parent_time, area)) return self - def finish(self) -> Schedule: + def finish(self) -> ScheduledProject: processed_sworks = self._local_optimize_stack.apply(self._scheduled_works) - return Schedule.from_scheduled_works(processed_sworks.values(), self._wg) + schedule = Schedule.from_scheduled_works(processed_sworks.values(), self._wg) + return ScheduledProject(self._wg, self._input._contractors, schedule) diff --git a/sampo/schemas/project.py b/sampo/schemas/project.py index 6ebba9aa..1da6e671 100644 --- a/sampo/schemas/project.py +++ b/sampo/schemas/project.py @@ -2,6 +2,7 @@ from sampo.schemas.graph import WorkGraph from sampo.schemas.schedule import Schedule from sampo.schemas.serializable import AutoJSONSerializable +from sampo.utilities.serializers import custom_serializer class ScheduledProject(AutoJSONSerializable['ScheduledProject']): @@ -9,3 +10,12 @@ def __init__(self, wg: WorkGraph, contractors: list[Contractor], schedule: Sched self.schedule = schedule self.wg = wg self.contractors = contractors + + @custom_serializer('contractors') + def serialize_contractors(self, value): + return [v._serialize() for v in value] + + @classmethod + @custom_serializer('contractors', deserializer=True) + def deserialize_equipment(cls, value): + return [Contractor._deserialize(v) for v in value] diff --git a/sampo/schemas/resources.py b/sampo/schemas/resources.py index a0a6eff4..23cba3a7 100644 --- a/sampo/schemas/resources.py +++ b/sampo/schemas/resources.py @@ -15,7 +15,7 @@ class WorkerProductivityMode(Enum): @dataclass -class Resource(AutoJSONSerializable['Equipment'], Identifiable): +class Resource(AutoJSONSerializable['Resource'], Identifiable): """ A class summarizing the different resources used in the work: Human resources, equipment, materials, etc. """ From 489756b1076c4744c4b9a99e712adbd9d3e51566 Mon Sep 17 00:00:00 2001 From: Quarter Date: Tue, 31 Oct 2023 12:59:50 +0300 Subject: [PATCH 3/3] Fixed tests and all ScheduledWork#work_unit usages --- sampo/pipeline/base.py | 3 +- sampo/pipeline/default.py | 2 +- sampo/scheduler/generic.py | 8 ++--- sampo/scheduler/genetic/converter.py | 17 ++++------ sampo/scheduler/utils/local_optimization.py | 4 +-- sampo/schemas/schedule.py | 36 +++++++++----------- sampo/schemas/scheduled_work.py | 21 ++++++------ sampo/utilities/validation.py | 37 ++++----------------- tests/pipeline/basic_pipeline_test.py | 15 ++++----- tests/utils/validation_test.py | 8 ++--- 10 files changed, 59 insertions(+), 92 deletions(-) diff --git a/sampo/pipeline/base.py b/sampo/pipeline/base.py index 5e501553..fd7631c1 100644 --- a/sampo/pipeline/base.py +++ b/sampo/pipeline/base.py @@ -8,6 +8,7 @@ from sampo.schemas.contractor import Contractor from sampo.schemas.graph import WorkGraph, GraphNode from sampo.schemas.landscape import LandscapeConfiguration +from sampo.schemas.project import ScheduledProject from sampo.schemas.schedule import Schedule from sampo.schemas.schedule_spec import ScheduleSpec from sampo.schemas.time import Time @@ -88,5 +89,5 @@ def optimize_local(self, optimizer: ScheduleLocalOptimizer, area: range) -> 'Sch ... @abstractmethod - def finish(self) -> Schedule: + def finish(self) -> ScheduledProject: ... diff --git a/sampo/pipeline/default.py b/sampo/pipeline/default.py index b92a5596..e80d5e7b 100644 --- a/sampo/pipeline/default.py +++ b/sampo/pipeline/default.py @@ -246,7 +246,7 @@ def __init__(self, s_input: DefaultInputPipeline, wg: WorkGraph, schedule: Sched self._wg = wg self._worker_pool = get_worker_contractor_pool(s_input._contractors) self._schedule = schedule - self._scheduled_works = {wg[swork.work_unit.id]: + self._scheduled_works = {wg[swork.id]: swork for swork in schedule.to_schedule_work_dict.values()} self._local_optimize_stack = ApplyQueue() diff --git a/sampo/scheduler/generic.py b/sampo/scheduler/generic.py index 21e95ae9..0dbb0a6c 100644 --- a/sampo/scheduler/generic.py +++ b/sampo/scheduler/generic.py @@ -95,7 +95,7 @@ def schedule_with_cache(self, ordered_nodes = self.prioritization(wg, self.work_estimator) schedule, schedule_start_time, timeline = \ - self.build_scheduler(ordered_nodes, contractors, landscape, spec, self.work_estimator, + self.build_scheduler(wg, ordered_nodes, contractors, landscape, spec, self.work_estimator, assigned_parent_time, timeline) schedule = Schedule.from_scheduled_works( schedule, @@ -108,6 +108,7 @@ def schedule_with_cache(self, return schedule, schedule_start_time, timeline, ordered_nodes def build_scheduler(self, + wg: WorkGraph, ordered_nodes: list[GraphNode], contractors: list[Contractor], landscape: LandscapeConfiguration = LandscapeConfiguration(), @@ -157,7 +158,4 @@ def build_scheduler(self, timeline.schedule(node, node2swork, best_worker_team, contractor, work_spec, start_time, work_spec.assigned_time, assigned_parent_time, work_estimator) - schedule_start_time = min((swork.start_time for swork in node2swork.values() if - len(swork.work_unit.worker_reqs) != 0), default=assigned_parent_time) - - return node2swork.values(), schedule_start_time, timeline + return node2swork.values(), assigned_parent_time, timeline diff --git a/sampo/scheduler/genetic/converter.py b/sampo/scheduler/genetic/converter.py index b7772800..d088f2d7 100644 --- a/sampo/scheduler/genetic/converter.py +++ b/sampo/scheduler/genetic/converter.py @@ -42,11 +42,11 @@ def convert_schedule_to_chromosome(work_id2index: dict[str, int], :return: """ - order: list[GraphNode] = order if order is not None else [work for work in schedule.works - if work.work_unit.id in work_id2index] + order: list[ScheduledWork] = order if order is not None else [work for work in schedule.works + if work.id in work_id2index] # order works part of chromosome - order_chromosome: np.ndarray = np.array([work_id2index[work.work_unit.id] for work in order]) + order_chromosome: np.ndarray = np.array([work_id2index[work.id] for work in order]) # convert to convenient form schedule = schedule.to_schedule_work_dict @@ -59,7 +59,7 @@ def convert_schedule_to_chromosome(work_id2index: dict[str, int], zone_changes_chromosome = np.zeros((len(order_chromosome), len(landscape.zone_config.start_statuses)), dtype=int) for node in order: - node_id = node.work_unit.id + node_id = node.id index = work_id2index[node_id] for resource in schedule[node_id].workers: res_count = resource.count @@ -105,7 +105,7 @@ def convert_chromosome_to_schedule(chromosome: ChromosomeType, for worker_index in worker_pool: for contractor_index in worker_pool[worker_index]: worker_pool[worker_index][contractor_index].with_count(border[contractor2index[contractor_index], - worker_name2index[worker_index]]) + worker_name2index[worker_index]]) if not isinstance(timeline, JustInTimeTimeline): timeline = JustInTimeTimeline(worker_pool, landscape) @@ -139,7 +139,7 @@ def decode(work_index): # declare current checkpoint index cpkt_idx = 0 - start_time = Time(-1) + start_time = assigned_parent_time - 1 def work_scheduled(args) -> bool: idx, (work_idx, node, worker_team, contractor, exec_time, work_spec) = args @@ -185,7 +185,4 @@ def work_scheduled(args) -> bool: enumerated_works_remaining.remove_if(work_scheduled) cpkt_idx = min(cpkt_idx + 1, len(work_timeline)) - schedule_start_time = min((swork.start_time for swork in node2swork.values() if - len(swork.work_unit.worker_reqs) != 0), default=assigned_parent_time) - - return node2swork, schedule_start_time, timeline, order_nodes + return node2swork, assigned_parent_time, timeline, order_nodes diff --git a/sampo/scheduler/utils/local_optimization.py b/sampo/scheduler/utils/local_optimization.py index 0b78953f..08939473 100644 --- a/sampo/scheduler/utils/local_optimization.py +++ b/sampo/scheduler/utils/local_optimization.py @@ -230,7 +230,7 @@ def optimize(self, scheduled_works: dict[GraphNode, ScheduledWork], node_order: my_schedule: ScheduledWork = scheduled_works[node] my_workers: dict[str, Worker] = build_index(my_schedule.workers, attrgetter('name')) - my_schedule_reqs: dict[str, WorkerReq] = build_index(my_schedule.work_unit.worker_reqs, attrgetter('kind')) + my_schedule_reqs: dict[str, WorkerReq] = build_index(node.work_unit.worker_reqs, attrgetter('kind')) new_my_workers = {} @@ -238,7 +238,7 @@ def optimize(self, scheduled_works: dict[GraphNode, ScheduledWork], node_order: for candidate in accepted_candidates: candidate_schedule = scheduled_works[candidate] - candidate_schedule_reqs: dict[str, WorkerReq] = build_index(candidate_schedule.work_unit.worker_reqs, + candidate_schedule_reqs: dict[str, WorkerReq] = build_index(candidate.work_unit.worker_reqs, attrgetter('kind')) new_candidate_workers: dict[str, int] = {} diff --git a/sampo/schemas/schedule.py b/sampo/schemas/schedule.py index 870b0143..2c1a1a3c 100644 --- a/sampo/schemas/schedule.py +++ b/sampo/schemas/schedule.py @@ -23,7 +23,7 @@ class Schedule(JSONSerializable['Schedule']): """ _data_columns: list[str] = ['idx', 'task_id', 'task_name', 'task_name_mapped', 'contractor', 'cost', - 'volume', 'measurement', 'successors', 'start', + 'volume', 'measurement', 'start', 'finish', 'duration', 'workers'] _scheduled_work_column: str = 'scheduled_work_object' @@ -46,7 +46,7 @@ def pure_schedule_df(self) -> DataFrame: :return: Pure schedule DataFrame. """ return self._schedule[~self._schedule.apply( - lambda row: row[self._scheduled_work_column].work_unit.is_service_unit, + lambda row: row[self._scheduled_work_column].is_service_unit, axis=1 )][self._data_columns] @@ -80,7 +80,7 @@ def execution_time(self) -> Time: def __init__(self, schedule: DataFrame) -> None: """ Initializes new `Schedule` object as a wrapper around `DataFrame` with specific structure. - Don't use manually. Create Schedule `objects` via `from_scheduled_works` factory method. + Do not use manually. Create Schedule `objects` via `from_scheduled_works` factory method. :param schedule: Prepared schedule `DataFrame`. """ @@ -136,13 +136,6 @@ def from_scheduled_works(works: Iterable[ScheduledWork], """ ordered_task_ids = order_nodes_by_start_time(works, wg) if wg else None - def info(work_unit: WorkUnit) -> tuple[float, str, list[tuple[str, str]]]: - if wg is None: - return 0, "", [] - # noinspection PyTypeChecker - return work_unit.volume, work_unit.volume_type, \ - [(edge.finish.id, edge.type.value) for edge in wg[work_unit.id].edges_from] - def sed(time1, time2) -> tuple: """ Sorts times and calculates difference. @@ -154,16 +147,17 @@ def sed(time1, time2) -> tuple: return start, end, end - start data_frame = [(i, # idx - w.work_unit.id, # task_id - w.work_unit.display_name, # task_name - w.work_unit.name, # task_name_mapped - w.contractor, # contractor info - w.cost, # work cost - *info(w.work_unit), # volume, measurement, successors - *sed(*(t.value for t in w.start_end_time)), # start, end, duration - repr(dict((i.name, i.count) for i in w.workers)), # workers - w # full ScheduledWork info - ) for i, w in enumerate(works)] + w.id, # task_id + w.display_name, # task_name + w.name, # task_name_mapped + w.contractor, # contractor info + w.cost, # work cost + w.volume, # work volume + w.volume_type, # work volume type + *sed(*(t.value for t in w.start_end_time)), # start, end, duration + repr(dict((i.name, i.count) for i in w.workers)), # workers + w # full ScheduledWork info + ) for i, w in enumerate(works)] data_frame = DataFrame.from_records(data_frame, columns=Schedule._columns) data_frame = data_frame.set_index('idx') @@ -191,7 +185,7 @@ def order_nodes_by_start_time(works: Iterable[ScheduledWork], wg: WorkGraph) -> :return: """ res = [] - order_by_start_time = [(item.start_time, item.work_unit.id) for item in + order_by_start_time = [(item.start_time, item.id) for item in sorted(works, key=lambda item: item.start_time)] cur_time = 0 diff --git a/sampo/schemas/scheduled_work.py b/sampo/schemas/scheduled_work.py index e3b68f12..7fd41104 100644 --- a/sampo/schemas/scheduled_work.py +++ b/sampo/schemas/scheduled_work.py @@ -38,16 +38,20 @@ def __init__(self, zones_pre: list[ZoneTransition] | None = None, zones_post: list[ZoneTransition] | None = None, materials: list[MaterialDelivery] | None = None, - object: ConstructionObject | None = None): + c_object: ConstructionObject | None = None): self.id = work_unit.id - self.work_unit = work_unit + self.name = work_unit.name + self.display_name = work_unit.display_name + self.is_service_unit = work_unit.is_service_unit + self.volume = work_unit.volume + self.volume_type = work_unit.volume_type self.start_end_time = start_end_time self.workers = workers if workers is not None else [] self.equipments = equipments if equipments is not None else [] self.zones_pre = zones_pre if zones_pre is not None else [] self.zones_post = zones_post if zones_post is not None else [] self.materials = materials if materials is not None else [] - self.object = object if object is not None else [] + self.object = c_object if c_object is not None else [] if contractor is not None: if isinstance(contractor, str): @@ -60,7 +64,7 @@ def __init__(self, self.cost = sum([worker.get_cost() * self.duration.value for worker in self.workers]) def __str__(self): - return f'ScheduledWork[work_unit={self.work_unit}, start_end_time={self.start_end_time}, ' \ + return f'ScheduledWork[work_unit={self.id}, start_end_time={self.start_end_time}, ' \ f'workers={self.workers}, contractor={self.contractor}]' def __repr__(self): @@ -85,9 +89,6 @@ def deserialize_time(cls, value): def deserialize_workers(cls, value): return [Worker._deserialize(t) for t in value] - def get_actual_duration(self, work_estimator: WorkTimeEstimator) -> Time: - return work_estimator.estimate_time(self.work_unit, self.workers) - @property def start_time(self) -> Time: return self.start_end_time[0] @@ -106,7 +107,7 @@ def finish_time(self, val: Time): @property def min_child_start_time(self) -> Time: - return self.finish_time if self.work_unit.is_service_unit else self.finish_time + 1 + return self.finish_time if self.is_service_unit else self.finish_time + 1 @staticmethod def start_time_getter(): @@ -127,8 +128,8 @@ def is_overlapped(self, time: int) -> bool: def to_dict(self) -> dict[str, Any]: return { - 'task_id': self.work_unit.id, - 'task_name': self.work_unit.name, + 'task_id': self.id, + 'task_name': self.name, 'start': self.start_time.value, 'finish': self.finish_time.value, 'contractor_id': self.contractor, diff --git a/sampo/utilities/validation.py b/sampo/utilities/validation.py index b01da310..47a524fd 100644 --- a/sampo/utilities/validation.py +++ b/sampo/utilities/validation.py @@ -28,13 +28,13 @@ def validate_schedule(schedule: Schedule, wg: WorkGraph, contractors: list[Contr _check_all_tasks_scheduled(schedule, wg) _check_parent_dependencies(schedule, wg) _check_all_tasks_have_valid_duration(schedule) - _check_all_workers_correspond_to_worker_reqs(schedule) + _check_all_workers_correspond_to_worker_reqs(wg, schedule) _check_all_allocated_workers_do_not_exceed_capacity_of_contractors(schedule, contractors) def _check_all_tasks_scheduled(schedule: Schedule, wg: WorkGraph) -> None: # 1. each task of the graph is scheduled - scheduled_works = {work.work_unit.id: work for work in schedule.works} + scheduled_works = {work.id: work for work in schedule.works} absent_works = [node for node in wg.nodes if node.work_unit.id not in scheduled_works] assert len(absent_works) == 0, \ @@ -43,7 +43,7 @@ def _check_all_tasks_scheduled(schedule: Schedule, wg: WorkGraph) -> None: def _check_parent_dependencies(schedule: Schedule, wg: WorkGraph) -> None: - scheduled_works: dict[str, ScheduledWork] = {work.work_unit.id: work for work in schedule.works} + scheduled_works: dict[str, ScheduledWork] = {work.id: work for work in schedule.works} for node in wg.nodes: start, end = scheduled_works[node.work_unit.id].start_end_time @@ -56,12 +56,12 @@ def _check_all_tasks_have_valid_duration(schedule: Schedule) -> None: # 3. check if all tasks have duration appropriate for their working hours service_works_with_incorrect_duration = [ work for work in schedule.works - if work.work_unit.is_service_unit and work.duration != 0 + if work.is_service_unit and work.duration != 0 ] assert len(service_works_with_incorrect_duration) == 0, \ f'Found service works that have non-zero duration:\n' \ - f'\t{[work.work_unit.id for work in service_works_with_incorrect_duration]}' + f'\t{[work.id for work in service_works_with_incorrect_duration]}' # # TODO: make correct duration calculation # works_with_incorrect_duration = [ @@ -153,32 +153,9 @@ def check_all_allocated_workers_do_not_exceed_capacity_of_contractors(schedule: return cur_worker_pool -def _check_all_workers_correspond_to_worker_reqs(schedule: Schedule): +def _check_all_workers_correspond_to_worker_reqs(wg: WorkGraph, schedule: Schedule): for swork in schedule.works: - worker2req = build_index(swork.work_unit.worker_reqs, attrgetter('kind')) + worker2req = build_index(wg[swork.id].work_unit.worker_reqs, attrgetter('kind')) for worker in swork.workers: req = worker2req[worker.name] assert req.min_count <= worker.count <= req.max_count - - -def _check_all_workers_have_same_qualification(wg: WorkGraph, contractors: list[Contractor]): - # 1. all workers of the same category belonging to the same contractor should have the same characteristics - for c in contractors: - assert all(ws.count >= 1 for _, ws in c.workers.items()), \ - 'There should be only one worker for the same worker category' - - # добавляем агентов в словарь - agents = {} - for contractor in contractors: - for name, val in contractor.workers.items(): - if name[0] not in agents: - agents[name[0]] = 0 - agents[name[0]] += val.count - # 2. all tasks should have worker reqs that can be satisfied by at least one contractor - for v in wg.nodes: - assert any( - all(c.worker_types[wreq.kind][0].count - >= (wreq.min_count + min(agents[wreq.kind], wreq.max_count)) // 2 - for wreq in v.work_unit.worker_reqs) - for c in contractors - ), f'The work unit with id {v.work_unit.id} cannot be satisfied by any contractors' diff --git a/tests/pipeline/basic_pipeline_test.py b/tests/pipeline/basic_pipeline_test.py index c098f123..22ff7172 100644 --- a/tests/pipeline/basic_pipeline_test.py +++ b/tests/pipeline/basic_pipeline_test.py @@ -11,20 +11,20 @@ def test_plain_scheduling(setup_scheduler_parameters): setup_wg, setup_contractors, setup_landscape = setup_scheduler_parameters - schedule = SchedulingPipeline.create() \ + project = SchedulingPipeline.create() \ .wg(setup_wg) \ .contractors(setup_contractors) \ .landscape(setup_landscape) \ .schedule(HEFTScheduler()) \ .finish() - print(f'Scheduled {len(schedule.to_schedule_work_dict)} works') + print(f'Scheduled {len(project.schedule.to_schedule_work_dict)} works') def test_local_optimize_scheduling(setup_scheduler_parameters): setup_wg, setup_contractors, setup_landscape = setup_scheduler_parameters - schedule = SchedulingPipeline.create() \ + project = SchedulingPipeline.create() \ .wg(setup_wg) \ .contractors(setup_contractors) \ .landscape(setup_landscape) \ @@ -33,7 +33,7 @@ def test_local_optimize_scheduling(setup_scheduler_parameters): .optimize_local(ParallelizeScheduleLocalOptimizer(JustInTimeTimeline), range(0, setup_wg.vertex_count // 2)) \ .finish() - print(f'Scheduled {len(schedule.to_schedule_work_dict)} works') + print(f'Scheduled {len(project.schedule.to_schedule_work_dict)} works') # this test is needed to check validation of input contractors @@ -42,7 +42,7 @@ def test_plain_scheduling_with_no_sufficient_number_of_contractors(setup_wg, set setup_landscape_many_holders): thrown = False try: - schedule = SchedulingPipeline.create() \ + SchedulingPipeline.create() \ .wg(setup_wg) \ .contractors(setup_empty_contractors) except NoSufficientContractorError: @@ -52,11 +52,10 @@ def test_plain_scheduling_with_no_sufficient_number_of_contractors(setup_wg, set def test_plain_scheduling_with_parse_data(): - - schedule = SchedulingPipeline.create() \ + project = SchedulingPipeline.create() \ .wg(wg=os.path.join(sys.path[0], 'tests/parser/test_wg.csv'), change_base_on_history=True) \ .history(history=os.path.join(sys.path[0], 'tests/parser/test_history_data.csv')) \ .schedule(HEFTScheduler()) \ .finish() - print(f'Scheduled {len(schedule.to_schedule_work_dict)} works') + print(f'Scheduled {len(project.schedule.to_schedule_work_dict)} works') diff --git a/tests/utils/validation_test.py b/tests/utils/validation_test.py index 4b72f442..b9cdb0cd 100644 --- a/tests/utils/validation_test.py +++ b/tests/utils/validation_test.py @@ -58,7 +58,7 @@ def test_check_resources_validity_right(setup_default_schedules): for scheduler, (schedule, _, _, _) in setup_default_schedules.items(): try: - _check_all_workers_correspond_to_worker_reqs(schedule) + _check_all_workers_correspond_to_worker_reqs(setup_wg, schedule) _check_all_allocated_workers_do_not_exceed_capacity_of_contractors(schedule, setup_contractors) except AssertionError as e: raise AssertionError(f'Scheduler {scheduler} failed validation', e) @@ -74,7 +74,7 @@ def test_check_resources_validity_wrong(setup_default_schedules): broken = break_schedule(break_type, schedule, setup_wg, setup_worker_pool) thrown = False try: - _check_all_workers_correspond_to_worker_reqs(broken) + _check_all_workers_correspond_to_worker_reqs(setup_wg, broken) _check_all_allocated_workers_do_not_exceed_capacity_of_contractors(broken, setup_contractors) except AssertionError: thrown = True @@ -88,7 +88,7 @@ def break_schedule(break_type: BreakType, schedule: Schedule, wg: WorkGraph, if break_type == BreakType.OrderBrokenDependencies: for swork in broken.values(): - parents = [broken[parent.work_unit.id] for parent in wg[swork.work_unit.id].parents] + parents = [broken[parent.work_unit.id] for parent in wg[swork.id].parents] if not parents or swork.start_time == 0: continue parent = parents[0] @@ -105,7 +105,7 @@ def break_schedule(break_type: BreakType, schedule: Schedule, wg: WorkGraph, worker.count = -1 elif break_type == BreakType.ResourcesTooBigToWorkReq: for swork in broken.values(): - worker2req = build_index(swork.work_unit.worker_reqs, attrgetter('kind')) + worker2req = build_index(wg[swork.id].work_unit.worker_reqs, attrgetter('kind')) for worker in swork.workers: worker.count = worker2req[worker.name].max_count + 1000000 elif break_type == BreakType.ResourcesTooBigToSupplyByThisContractor: